summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Cozic <laurent@cozic.net>2017-11-26 16:59:29 +0000
committerLaurent Cozic <laurent@cozic.net>2017-11-26 16:59:29 +0000
commit2d0cb29bcb88104e44d04aeb2860cbaf25cd32cd (patch)
treee13739b6ffca0f6d5b12551f1b67c7811beb2e3b
parentc608793b60ced689caacded09b6da21ffa643c16 (diff)
Allow specifying the expiration strategy tokens in any order (less error prone)
-rwxr-xr-xrsync_tmbackup.sh21
-rw-r--r--test.txt6
-rw-r--r--tests/BaseTestCase.php5
-rw-r--r--tests/BasicTest.php40
-rw-r--r--tests/package-lock.json3
-rw-r--r--tests/phpunit-5.7.20.phar73219
6 files changed, 73283 insertions, 11 deletions
diff --git a/rsync_tmbackup.sh b/rsync_tmbackup.sh
index a22ece0..740cc1a 100755
--- a/rsync_tmbackup.sh
+++ b/rsync_tmbackup.sh
@@ -75,6 +75,15 @@ fn_parse_date() {
fi
}
+fn_run_cmd() {
+ if [ -n "$SSH_DEST_FOLDER_PREFIX" ]
+ then
+ eval "$SSH_CMD '$1'"
+ else
+ eval $1
+ fi
+}
+
fn_find_backups() {
fn_run_cmd "find "$DEST_FOLDER" -maxdepth 1 -type d -name \"????-??-??-??????\" -prune | sort -r"
}
@@ -108,8 +117,7 @@ fn_expire_backups() {
fi
# Find which strategy token applies to this particular backup
- IFS=' '
- for strategy_token in $EXPIRATION_STRATEGY; do
+ for strategy_token in $(echo $EXPIRATION_STRATEGY | tr " " "\n" | sort -r -n); do
IFS=':' read -r -a t <<< "$strategy_token"
# After which date (relative to today) this token applies (X)
@@ -161,15 +169,6 @@ fn_parse_ssh() {
fi
}
-fn_run_cmd() {
- if [ -n "$SSH_DEST_FOLDER_PREFIX" ]
- then
- eval "$SSH_CMD '$1'"
- else
- eval $1
- fi
-}
-
fn_find() {
fn_run_cmd "find '$1'" 2>/dev/null
}
diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..b842f04
--- /dev/null
+++ b/test.txt
@@ -0,0 +1,6 @@
+1:123
+2:123
+3:123
+10:123
+4:123
+20:123 \ No newline at end of file
diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php
new file mode 100644
index 0000000..a309be7
--- /dev/null
+++ b/tests/BaseTestCase.php
@@ -0,0 +1,5 @@
+<?php
+
+use PHPUnit\Framework\TestCase;
+
+class BaseTestCase extends TestCase {} \ No newline at end of file
diff --git a/tests/BasicTest.php b/tests/BasicTest.php
new file mode 100644
index 0000000..fcff13d
--- /dev/null
+++ b/tests/BasicTest.php
@@ -0,0 +1,40 @@
+<?php
+
+require_once dirname(__FILE__) . '/BaseTestCase.php';
+
+class BasicTest extends BaseTestCase {
+
+ private function makeDir($dir) {
+ if (!file_exists($dir)) {
+ $ok = @mkdir($dir, 0777, true);
+ if (!$ok) throw new Exception('Could not create source directory: ' . $dir);
+ }
+ return $dir;
+ }
+
+ private function sourceDir() {
+ return $this->makeDir(dirname(__FILE__) . '/data/source');
+ }
+
+ private function destDir() {
+ return $this->makeDir(dirname(__FILE__) . '/data/dest');
+ }
+
+ private function scriptPath() {
+ return dirname(dirname(__FILE__)) . '/rsync_tmbackup.sh';
+ }
+
+ private function execScript($args) {
+ $cmd = $this->scriptPath() . ' ' . implode(' ', $args);
+ exec($cmd, $output, $errorCode);
+ return array(
+ 'output' => $output,
+ 'errorCode' => $errorCode,
+ );
+ }
+
+ public function testFilesAreCopied() {
+ //$this->execScript(
+ }
+
+} \ No newline at end of file
diff --git a/tests/package-lock.json b/tests/package-lock.json
new file mode 100644
index 0000000..48e341a
--- /dev/null
+++ b/tests/package-lock.json
@@ -0,0 +1,3 @@
+{
+ "lockfileVersion": 1
+}
diff --git a/tests/phpunit-5.7.20.phar b/tests/phpunit-5.7.20.phar
new file mode 100644
index 0000000..e818814
--- /dev/null
+++ b/tests/phpunit-5.7.20.phar
@@ -0,0 +1,73219 @@
+#!/usr/bin/env php
+<?php
+if (version_compare('5.6.0', PHP_VERSION, '>')) {
+ fwrite(
+ STDERR,
+ sprintf(
+ 'PHPUnit 5.7.20 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL .
+ 'This version of PHPUnit is supported on PHP 5.6, PHP 7.0, and PHP 7.1.' . PHP_EOL .
+ 'You are using PHP %s (%s).' . PHP_EOL,
+ PHP_VERSION,
+ PHP_BINARY
+ )
+ );
+
+ die(1);
+}
+
+if (__FILE__ == realpath($GLOBALS['_SERVER']['SCRIPT_NAME'])) {
+ $execute = true;
+} else {
+ $execute = false;
+}
+
+define('__PHPUNIT_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', __FILE__));
+define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-5.7.20.phar');
+
+Phar::mapPhar('phpunit-5.7.20.phar');
+
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/DeepCopy.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Filter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php';
+require 'phar://phpunit-5.7.20.phar' . '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php';
+require 'phar://phpunit-5.7.20.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php';
+require 'phar://phpunit-5.7.20.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php';
+require 'phar://phpunit-5.7.20.phar' . '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-file-iterator/Iterator.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-file-iterator/Facade.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-file-iterator/Factory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Assert.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/ForwardCompatibility/Assert.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/TestListener.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/BaseTestListener.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/ForwardCompatibility/BaseTestListener.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Test.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/SelfDescribing.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/TestCase.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/ForwardCompatibility/TestCase.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/ForwardCompatibility/TestListener.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/ITester.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/AbstractTester.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Constraint/DataSetIsEqual.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Constraint/TableIsEqual.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Constraint/TableRowCount.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/IDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/AbstractDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/DataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/IDatabaseConnection.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/DefaultDatabaseConnection.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/FilteredDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/IMetaData.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/Dblib.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/Firebird.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/InformationSchema.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/MySQL.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/Oci.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/PgSQL.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/SqlSrv.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/MetaData/Sqlite.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ITable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/AbstractTable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/ResultSetTable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/Table.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ITableIterator.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/TableIterator.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ITableMetaData.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/AbstractTableMetaData.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/DefaultTableMetaData.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DB/TableMetaData.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/AbstractXmlDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ArrayDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/CompositeDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/CsvDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/DataSetFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/DefaultDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/DefaultTable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/DefaultTableIterator.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/FlatXmlDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/IPersistable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ISpec.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/IYamlParser.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/MysqlXmlDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Persistors/Abstract.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Persistors/Factory.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Persistors/FlatXml.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Persistors/MysqlXml.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Persistors/Xml.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Persistors/Yaml.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/QueryDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/QueryTable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ReplacementDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ReplacementTable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/ReplacementTableIterator.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/Csv.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/IDatabaseListConsumer.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/DbQuery.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/DbTable.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/IFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/Factory.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/FlatXml.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/Xml.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/Specs/Yaml.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/SymfonyYamlParser.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/TableFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/TableMetaDataFilter.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/XmlDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DataSet/YamlDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/DefaultTester.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/IDatabaseOperation.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Composite.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/RowBased.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Delete.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/DeleteAll.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Factory.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Insert.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Null.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Replace.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Truncate.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/Operation/Update.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/TestCaseTrait.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/TestCase.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/Command.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/Context.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/IMediumPrinter.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/IMedium.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/IMode.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/IModeFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/InvalidModeException.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/Mediums/Text.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/ModeFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/Modes/ExportDataSet.php';
+require 'phar://phpunit-5.7.20.phar' . '/dbunit/Extensions/Database/UI/Modes/ExportDataSet/Arguments.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/TestSuite.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Extensions/GroupTestSuite.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Extensions/PhptTestCase.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Extensions/PhptTestSuite.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Extensions/TestDecorator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Extensions/RepeatedTest.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Extensions/TicketListener.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/AssertionFailedError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/CodeCoverageException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/And.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ArrayHasKey.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ArraySubset.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Composite.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Attribute.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Callback.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ClassHasAttribute.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ClassHasStaticAttribute.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Count.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/DirectoryExists.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ExceptionCode.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ExceptionMessage.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ExceptionMessageRegExp.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/FileExists.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/GreaterThan.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsAnything.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsEmpty.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsEqual.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsFalse.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsFinite.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsIdentical.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsInfinite.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsInstanceOf.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsJson.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsNan.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsNull.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsReadable.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsTrue.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsType.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/IsWritable.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/JsonMatches.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/LessThan.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Not.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/ObjectHasAttribute.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Or.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/PCREMatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/SameSize.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/StringContains.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/StringEndsWith.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/StringMatches.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/StringStartsWith.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/TraversableContains.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/TraversableContainsOnly.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Constraint/Xor.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/RiskyTest.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/RiskyTestError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/CoveredCodeNotExecutedException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Error.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Error/Deprecated.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Error/Notice.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Error/Warning.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/ExceptionWrapper.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/ExpectationFailedException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/IncompleteTest.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/IncompleteTestCase.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/IncompleteTestError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/InvalidCoversTargetException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/MissingCoversAnnotationException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Exception/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Exception/BadMethodCallException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Builder/Identity.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Builder/Stub.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Builder/Match.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Builder/ParametersMatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Builder/MethodNameMatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Builder/InvocationMocker.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Builder/Namespace.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Generator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Invocation.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/MatcherCollection.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Verifiable.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Invokable.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/InvocationMocker.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Invocation/Static.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Invocation/Object.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/Invocation.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedRecorder.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/AnyInvokedCount.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/StatelessInvocation.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/AnyParameters.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/ConsecutiveParameters.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtIndex.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtLeastCount.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtLeastOnce.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtMostCount.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedCount.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/MethodName.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Matcher/Parameters.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/MockBuilder.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/MockObject.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Exception/RuntimeException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/ConsecutiveCalls.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/Return.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/ReturnArgument.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/ReturnCallback.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/ReturnReference.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/ReturnSelf.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit-mock-objects/Framework/MockObject/Stub/ReturnValueMap.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/OutputError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/SkippedTest.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/SkippedTestCase.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/SkippedTestError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/SkippedTestSuiteError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/SyntheticError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/TestFailure.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/TestResult.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/TestSuite/DataProvider.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/UnintentionallyCoveredCodeError.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/Warning.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Framework/WarningTestCase.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/BaseTestRunner.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/Filter/Factory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/Filter/Group.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/Filter/Group/Exclude.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/Filter/Group/Include.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/Filter/Test.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/TestSuiteLoader.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/StandardTestSuiteLoader.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Runner/Version.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/TextUI/Command.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Printer.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/TextUI/ResultPrinter.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/TextUI/TestRunner.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Blacklist.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Configuration.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/ConfigurationGenerator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/ErrorHandler.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Fileloader.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Filesystem.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Filter.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Getopt.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/GlobalState.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/InvalidArgumentHelper.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Log/JSON.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Log/JUnit.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Log/TAP.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Log/TeamCity.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/PHP.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/PHP/Default.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/PHP/Windows.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Regex.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/String.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Test.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/TestDox/NamePrettifier.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/TestDox/ResultPrinter.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/TestDox/ResultPrinter/HTML.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/TestDox/ResultPrinter/Text.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/TestDox/ResultPrinter/XML.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/TestSuiteIterator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/Type.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpunit/Util/XML.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-invoker/Invoker.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-invoker/TimeoutException.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-timer/Timer.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-token-stream/Token.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-token-stream/Token/Stream.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-token-stream/Token/Stream/CachingFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Call/Call.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Call/CallCenter.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/Comparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/Factory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Comparator/Factory.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/ArrayComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/ObjectComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Doubler.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Prophet.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Util/ExportUtil.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpspec-prophecy/Prophecy/Util/StringUtil.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/CodeCoverage.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Exception/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Exception/RuntimeException.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Exception/CoveredCodeNotExecutedException.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Driver/Driver.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Driver/Xdebug.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Driver/HHVM.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Driver/PHPDBG.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Filter.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Exception/InvalidArgumentException.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Exception/MissingCoversAnnotationException.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Node/AbstractNode.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Node/Builder.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Node/Directory.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Node/File.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Node/Iterator.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Clover.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Crap4j.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Html/Renderer.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Html/Renderer/Dashboard.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Html/Renderer/Directory.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Html/Facade.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Html/Renderer/File.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/PHP.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Text.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Coverage.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Node.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Directory.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Facade.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/File.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Method.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Project.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Report.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Tests.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Totals.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Report/Xml/Unit.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-code-coverage/Util.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-code-unit-reverse-lookup/Wizard.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/ComparisonFailure.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/DOMNodeComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/DateTimeComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/ScalarComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/NumericComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/DoubleComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/ExceptionComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/MockObjectComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/ResourceComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/SplObjectStorageComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-comparator/TypeComparator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/Chunk.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/Diff.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/Differ.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/LCS/LongestCommonSubsequence.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/Line.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-diff/Parser.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-environment/Console.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-environment/Runtime.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-exporter/Exporter.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-global-state/Blacklist.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-global-state/CodeExporter.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-global-state/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-global-state/Restorer.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-global-state/RuntimeException.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-global-state/Snapshot.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-object-enumerator/Enumerator.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-object-enumerator/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-object-enumerator/InvalidArgumentException.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-recursion-context/Context.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-recursion-context/Exception.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-recursion-context/InvalidArgumentException.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-resource-operations/ResourceOperations.php';
+require 'phar://phpunit-5.7.20.phar' . '/sebastian-version/Version.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Dumper.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Escaper.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Exception/ExceptionInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Exception/RuntimeException.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Exception/DumpException.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Exception/ParseException.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Inline.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Parser.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Unescaper.php';
+require 'phar://phpunit-5.7.20.phar' . '/symfony/yaml/Yaml.php';
+require 'phar://phpunit-5.7.20.phar' . '/php-text-template/Template.php';
+require 'phar://phpunit-5.7.20.phar' . '/webmozart-assert/Assert.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Description.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tag.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/Strategy.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-common/Element.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-common/File.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-common/Fqsen.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/FqsenResolver.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-common/Location.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-common/Project.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-reflection-common/ProjectFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Type.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/TypeResolver.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Array_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Boolean.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Callable_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Compound.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Context.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/ContextFactory.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Float_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Integer.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Mixed.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Null_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Object_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Resource.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Scalar.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Self_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Static_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/String_.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/This.php';
+require 'phar://phpunit-5.7.20.phar' . '/phpdocumentor-type-resolver/Types/Void_.php';
+
+if ($execute) {
+ if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') {
+ print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt');
+ exit;
+ }
+
+ PHPUnit_TextUI_Command::main();
+}
+
+__HALT_COMPILER(); ?>
+
+
+
+
+
+
+
+
+
+
+~
+
+
+
+
+͂
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+c
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+doctrine/instantiator: 1.0.5
+myclabs/deep-copy: 1.6.1
+phpdocumentor/reflection-common: 1.0
+phpdocumentor/reflection-docblock: 3.1.1
+phpdocumentor/type-resolver: 0.2.1
+phpspec/prophecy: v1.7.0
+phpunit/dbunit: 2.0.3
+phpunit/php-code-coverage: 4.0.8
+phpunit/php-file-iterator: 1.4.2
+phpunit/php-invoker: 1.1.4
+phpunit/php-text-template: 1.2.1
+phpunit/php-timer: 1.0.9
+phpunit/php-token-stream: 1.4.11
+phpunit/phpunit-mock-objects: 3.4.3
+sebastian/code-unit-reverse-lookup: 1.0.1
+sebastian/comparator: 1.2.4
+sebastian/diff: 1.4.3
+sebastian/environment: 2.0.0
+sebastian/exporter: 2.0.0
+sebastian/global-state: 1.1.1
+sebastian/object-enumerator: 2.0.1
+sebastian/recursion-context: 2.0.0
+sebastian/resource-operations: 1.0.0
+sebastian/version: 2.0.1
+symfony/yaml: v3.2.8
+webmozart/assert: 1.2.0
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
+IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
+MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
+FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
+bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
+H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
+uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
+mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
+a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
+E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
+WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
+VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
+Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
+IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
+AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
+YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
+Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
+c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
+mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----
+PHP_CodeCoverage
+
+Copyright (c) 2009-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+use SebastianBergmann\CodeCoverage\Driver\Driver;
+use SebastianBergmann\CodeCoverage\Driver\Xdebug;
+use SebastianBergmann\CodeCoverage\Driver\HHVM;
+use SebastianBergmann\CodeCoverage\Driver\PHPDBG;
+use SebastianBergmann\CodeCoverage\Node\Builder;
+use SebastianBergmann\CodeCoverage\Node\Directory;
+use SebastianBergmann\CodeUnitReverseLookup\Wizard;
+use SebastianBergmann\Environment\Runtime;
+
+/**
+ * Provides collection functionality for PHP code coverage information.
+ */
+class CodeCoverage
+{
+ /**
+ * @var Driver
+ */
+ private $driver;
+
+ /**
+ * @var Filter
+ */
+ private $filter;
+
+ /**
+ * @var Wizard
+ */
+ private $wizard;
+
+ /**
+ * @var bool
+ */
+ private $cacheTokens = false;
+
+ /**
+ * @var bool
+ */
+ private $checkForUnintentionallyCoveredCode = false;
+
+ /**
+ * @var bool
+ */
+ private $forceCoversAnnotation = false;
+
+ /**
+ * @var bool
+ */
+ private $checkForUnexecutedCoveredCode = false;
+
+ /**
+ * @var bool
+ */
+ private $checkForMissingCoversAnnotation = false;
+
+ /**
+ * @var bool
+ */
+ private $addUncoveredFilesFromWhitelist = true;
+
+ /**
+ * @var bool
+ */
+ private $processUncoveredFilesFromWhitelist = false;
+
+ /**
+ * @var bool
+ */
+ private $ignoreDeprecatedCode = false;
+
+ /**
+ * @var mixed
+ */
+ private $currentId;
+
+ /**
+ * Code coverage data.
+ *
+ * @var array
+ */
+ private $data = [];
+
+ /**
+ * @var array
+ */
+ private $ignoredLines = [];
+
+ /**
+ * @var bool
+ */
+ private $disableIgnoredLines = false;
+
+ /**
+ * Test data.
+ *
+ * @var array
+ */
+ private $tests = [];
+
+ /**
+ * @var string[]
+ */
+ private $unintentionallyCoveredSubclassesWhitelist = [];
+
+ /**
+ * Determine if the data has been initialized or not
+ *
+ * @var bool
+ */
+ private $isInitialized = false;
+
+ /**
+ * Determine whether we need to check for dead and unused code on each test
+ *
+ * @var bool
+ */
+ private $shouldCheckForDeadAndUnused = true;
+
+ /**
+ * Constructor.
+ *
+ * @param Driver $driver
+ * @param Filter $filter
+ *
+ * @throws RuntimeException
+ */
+ public function __construct(Driver $driver = null, Filter $filter = null)
+ {
+ if ($driver === null) {
+ $driver = $this->selectDriver();
+ }
+
+ if ($filter === null) {
+ $filter = new Filter;
+ }
+
+ $this->driver = $driver;
+ $this->filter = $filter;
+
+ $this->wizard = new Wizard;
+ }
+
+ /**
+ * Returns the code coverage information as a graph of node objects.
+ *
+ * @return Directory
+ */
+ public function getReport()
+ {
+ $builder = new Builder;
+
+ return $builder->build($this);
+ }
+
+ /**
+ * Clears collected code coverage data.
+ */
+ public function clear()
+ {
+ $this->isInitialized = false;
+ $this->currentId = null;
+ $this->data = [];
+ $this->tests = [];
+ }
+
+ /**
+ * Returns the filter object used.
+ *
+ * @return Filter
+ */
+ public function filter()
+ {
+ return $this->filter;
+ }
+
+ /**
+ * Returns the collected code coverage data.
+ * Set $raw = true to bypass all filters.
+ *
+ * @param bool $raw
+ *
+ * @return array
+ */
+ public function getData($raw = false)
+ {
+ if (!$raw && $this->addUncoveredFilesFromWhitelist) {
+ $this->addUncoveredFilesFromWhitelist();
+ }
+
+ return $this->data;
+ }
+
+ /**
+ * Sets the coverage data.
+ *
+ * @param array $data
+ */
+ public function setData(array $data)
+ {
+ $this->data = $data;
+ }
+
+ /**
+ * Returns the test data.
+ *
+ * @return array
+ */
+ public function getTests()
+ {
+ return $this->tests;
+ }
+
+ /**
+ * Sets the test data.
+ *
+ * @param array $tests
+ */
+ public function setTests(array $tests)
+ {
+ $this->tests = $tests;
+ }
+
+ /**
+ * Start collection of code coverage information.
+ *
+ * @param mixed $id
+ * @param bool $clear
+ *
+ * @throws InvalidArgumentException
+ */
+ public function start($id, $clear = false)
+ {
+ if (!is_bool($clear)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ if ($clear) {
+ $this->clear();
+ }
+
+ if ($this->isInitialized === false) {
+ $this->initializeData();
+ }
+
+ $this->currentId = $id;
+
+ $this->driver->start($this->shouldCheckForDeadAndUnused);
+ }
+
+ /**
+ * Stop collection of code coverage information.
+ *
+ * @param bool $append
+ * @param mixed $linesToBeCovered
+ * @param array $linesToBeUsed
+ *
+ * @return array
+ *
+ * @throws InvalidArgumentException
+ */
+ public function stop($append = true, $linesToBeCovered = [], array $linesToBeUsed = [])
+ {
+ if (!is_bool($append)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ if (!is_array($linesToBeCovered) && $linesToBeCovered !== false) {
+ throw InvalidArgumentException::create(
+ 2,
+ 'array or false'
+ );
+ }
+
+ $data = $this->driver->stop();
+ $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed);
+
+ $this->currentId = null;
+
+ return $data;
+ }
+
+ /**
+ * Appends code coverage data.
+ *
+ * @param array $data
+ * @param mixed $id
+ * @param bool $append
+ * @param mixed $linesToBeCovered
+ * @param array $linesToBeUsed
+ *
+ * @throws RuntimeException
+ */
+ public function append(array $data, $id = null, $append = true, $linesToBeCovered = [], array $linesToBeUsed = [])
+ {
+ if ($id === null) {
+ $id = $this->currentId;
+ }
+
+ if ($id === null) {
+ throw new RuntimeException;
+ }
+
+ $this->applyListsFilter($data);
+ $this->applyIgnoredLinesFilter($data);
+ $this->initializeFilesThatAreSeenTheFirstTime($data);
+
+ if (!$append) {
+ return;
+ }
+
+ if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') {
+ $this->applyCoversAnnotationFilter(
+ $data,
+ $linesToBeCovered,
+ $linesToBeUsed
+ );
+ }
+
+ if (empty($data)) {
+ return;
+ }
+
+ $size = 'unknown';
+ $status = null;
+
+ if ($id instanceof \PHPUnit_Framework_TestCase) {
+ $_size = $id->getSize();
+
+ if ($_size == \PHPUnit_Util_Test::SMALL) {
+ $size = 'small';
+ } elseif ($_size == \PHPUnit_Util_Test::MEDIUM) {
+ $size = 'medium';
+ } elseif ($_size == \PHPUnit_Util_Test::LARGE) {
+ $size = 'large';
+ }
+
+ $status = $id->getStatus();
+ $id = get_class($id) . '::' . $id->getName();
+ } elseif ($id instanceof \PHPUnit_Extensions_PhptTestCase) {
+ $size = 'large';
+ $id = $id->getName();
+ }
+
+ $this->tests[$id] = ['size' => $size, 'status' => $status];
+
+ foreach ($data as $file => $lines) {
+ if (!$this->filter->isFile($file)) {
+ continue;
+ }
+
+ foreach ($lines as $k => $v) {
+ if ($v == Driver::LINE_EXECUTED) {
+ if (empty($this->data[$file][$k]) || !in_array($id, $this->data[$file][$k])) {
+ $this->data[$file][$k][] = $id;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Merges the data from another instance.
+ *
+ * @param CodeCoverage $that
+ */
+ public function merge(CodeCoverage $that)
+ {
+ $this->filter->setWhitelistedFiles(
+ array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles())
+ );
+
+ foreach ($that->data as $file => $lines) {
+ if (!isset($this->data[$file])) {
+ if (!$this->filter->isFiltered($file)) {
+ $this->data[$file] = $lines;
+ }
+
+ continue;
+ }
+
+ foreach ($lines as $line => $data) {
+ if ($data !== null) {
+ if (!isset($this->data[$file][$line])) {
+ $this->data[$file][$line] = $data;
+ } else {
+ $this->data[$file][$line] = array_unique(
+ array_merge($this->data[$file][$line], $data)
+ );
+ }
+ }
+ }
+ }
+
+ $this->tests = array_merge($this->tests, $that->getTests());
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setCacheTokens($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->cacheTokens = $flag;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getCacheTokens()
+ {
+ return $this->cacheTokens;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setCheckForUnintentionallyCoveredCode($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->checkForUnintentionallyCoveredCode = $flag;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setForceCoversAnnotation($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->forceCoversAnnotation = $flag;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setCheckForMissingCoversAnnotation($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->checkForMissingCoversAnnotation = $flag;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setCheckForUnexecutedCoveredCode($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->checkForUnexecutedCoveredCode = $flag;
+ }
+
+ /**
+ * @deprecated
+ *
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setMapTestClassNameToCoveredClassName($flag)
+ {
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setAddUncoveredFilesFromWhitelist($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->addUncoveredFilesFromWhitelist = $flag;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setProcessUncoveredFilesFromWhitelist($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->processUncoveredFilesFromWhitelist = $flag;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setDisableIgnoredLines($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->disableIgnoredLines = $flag;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws InvalidArgumentException
+ */
+ public function setIgnoreDeprecatedCode($flag)
+ {
+ if (!is_bool($flag)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ $this->ignoreDeprecatedCode = $flag;
+ }
+
+ /**
+ * @param array $whitelist
+ */
+ public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist)
+ {
+ $this->unintentionallyCoveredSubclassesWhitelist = $whitelist;
+ }
+
+ /**
+ * Applies the @covers annotation filtering.
+ *
+ * @param array $data
+ * @param mixed $linesToBeCovered
+ * @param array $linesToBeUsed
+ *
+ * @throws MissingCoversAnnotationException
+ * @throws UnintentionallyCoveredCodeException
+ */
+ private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed)
+ {
+ if ($linesToBeCovered === false ||
+ ($this->forceCoversAnnotation && empty($linesToBeCovered))) {
+ if ($this->checkForMissingCoversAnnotation) {
+ throw new MissingCoversAnnotationException;
+ }
+
+ $data = [];
+
+ return;
+ }
+
+ if (empty($linesToBeCovered)) {
+ return;
+ }
+
+ if ($this->checkForUnintentionallyCoveredCode &&
+ (!$this->currentId instanceof \PHPUnit_Framework_TestCase ||
+ (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) {
+ $this->performUnintentionallyCoveredCodeCheck(
+ $data,
+ $linesToBeCovered,
+ $linesToBeUsed
+ );
+ }
+
+ if ($this->checkForUnexecutedCoveredCode) {
+ $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed);
+ }
+
+ $data = array_intersect_key($data, $linesToBeCovered);
+
+ foreach (array_keys($data) as $filename) {
+ $_linesToBeCovered = array_flip($linesToBeCovered[$filename]);
+
+ $data[$filename] = array_intersect_key(
+ $data[$filename],
+ $_linesToBeCovered
+ );
+ }
+ }
+
+ /**
+ * Applies the whitelist filtering.
+ *
+ * @param array $data
+ */
+ private function applyListsFilter(array &$data)
+ {
+ foreach (array_keys($data) as $filename) {
+ if ($this->filter->isFiltered($filename)) {
+ unset($data[$filename]);
+ }
+ }
+ }
+
+ /**
+ * Applies the "ignored lines" filtering.
+ *
+ * @param array $data
+ */
+ private function applyIgnoredLinesFilter(array &$data)
+ {
+ foreach (array_keys($data) as $filename) {
+ if (!$this->filter->isFile($filename)) {
+ continue;
+ }
+
+ foreach ($this->getLinesToBeIgnored($filename) as $line) {
+ unset($data[$filename][$line]);
+ }
+ }
+ }
+
+ /**
+ * @param array $data
+ */
+ private function initializeFilesThatAreSeenTheFirstTime(array $data)
+ {
+ foreach ($data as $file => $lines) {
+ if ($this->filter->isFile($file) && !isset($this->data[$file])) {
+ $this->data[$file] = [];
+
+ foreach ($lines as $k => $v) {
+ $this->data[$file][$k] = $v == -2 ? null : [];
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes whitelisted files that are not covered.
+ */
+ private function addUncoveredFilesFromWhitelist()
+ {
+ $data = [];
+ $uncoveredFiles = array_diff(
+ $this->filter->getWhitelist(),
+ array_keys($this->data)
+ );
+
+ foreach ($uncoveredFiles as $uncoveredFile) {
+ if (!file_exists($uncoveredFile)) {
+ continue;
+ }
+
+ if (!$this->processUncoveredFilesFromWhitelist) {
+ $data[$uncoveredFile] = [];
+
+ $lines = count(file($uncoveredFile));
+
+ for ($i = 1; $i <= $lines; $i++) {
+ $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED;
+ }
+ }
+ }
+
+ $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
+ }
+
+ /**
+ * Returns the lines of a source file that should be ignored.
+ *
+ * @param string $filename
+ *
+ * @return array
+ *
+ * @throws InvalidArgumentException
+ */
+ private function getLinesToBeIgnored($filename)
+ {
+ if (!is_string($filename)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'string'
+ );
+ }
+
+ if (!isset($this->ignoredLines[$filename])) {
+ $this->ignoredLines[$filename] = [];
+
+ if ($this->disableIgnoredLines) {
+ return $this->ignoredLines[$filename];
+ }
+
+ $ignore = false;
+ $stop = false;
+ $lines = file($filename);
+ $numLines = count($lines);
+
+ foreach ($lines as $index => $line) {
+ if (!trim($line)) {
+ $this->ignoredLines[$filename][] = $index + 1;
+ }
+ }
+
+ if ($this->cacheTokens) {
+ $tokens = \PHP_Token_Stream_CachingFactory::get($filename);
+ } else {
+ $tokens = new \PHP_Token_Stream($filename);
+ }
+
+ $classes = array_merge($tokens->getClasses(), $tokens->getTraits());
+ $tokens = $tokens->tokens();
+
+ foreach ($tokens as $token) {
+ switch (get_class($token)) {
+ case 'PHP_Token_COMMENT':
+ case 'PHP_Token_DOC_COMMENT':
+ $_token = trim($token);
+ $_line = trim($lines[$token->getLine() - 1]);
+
+ if ($_token == '// @codeCoverageIgnore' ||
+ $_token == '//@codeCoverageIgnore') {
+ $ignore = true;
+ $stop = true;
+ } elseif ($_token == '// @codeCoverageIgnoreStart' ||
+ $_token == '//@codeCoverageIgnoreStart') {
+ $ignore = true;
+ } elseif ($_token == '// @codeCoverageIgnoreEnd' ||
+ $_token == '//@codeCoverageIgnoreEnd') {
+ $stop = true;
+ }
+
+ if (!$ignore) {
+ $start = $token->getLine();
+ $end = $start + substr_count($token, "\n");
+
+ // Do not ignore the first line when there is a token
+ // before the comment
+ if (0 !== strpos($_token, $_line)) {
+ $start++;
+ }
+
+ for ($i = $start; $i < $end; $i++) {
+ $this->ignoredLines[$filename][] = $i;
+ }
+
+ // A DOC_COMMENT token or a COMMENT token starting with "/*"
+ // does not contain the final \n character in its text
+ if (isset($lines[$i-1]) && 0 === strpos($_token, '/*') && '*/' === substr(trim($lines[$i-1]), -2)) {
+ $this->ignoredLines[$filename][] = $i;
+ }
+ }
+ break;
+
+ case 'PHP_Token_INTERFACE':
+ case 'PHP_Token_TRAIT':
+ case 'PHP_Token_CLASS':
+ case 'PHP_Token_FUNCTION':
+ /* @var \PHP_Token_Interface $token */
+
+ $docblock = $token->getDocblock();
+
+ $this->ignoredLines[$filename][] = $token->getLine();
+
+ if (strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && strpos($docblock, '@deprecated'))) {
+ $endLine = $token->getEndLine();
+
+ for ($i = $token->getLine(); $i <= $endLine; $i++) {
+ $this->ignoredLines[$filename][] = $i;
+ }
+ } elseif ($token instanceof \PHP_Token_INTERFACE ||
+ $token instanceof \PHP_Token_TRAIT ||
+ $token instanceof \PHP_Token_CLASS) {
+ if (empty($classes[$token->getName()]['methods'])) {
+ for ($i = $token->getLine();
+ $i <= $token->getEndLine();
+ $i++) {
+ $this->ignoredLines[$filename][] = $i;
+ }
+ } else {
+ $firstMethod = array_shift(
+ $classes[$token->getName()]['methods']
+ );
+
+ do {
+ $lastMethod = array_pop(
+ $classes[$token->getName()]['methods']
+ );
+ } while ($lastMethod !== null &&
+ substr($lastMethod['signature'], 0, 18) == 'anonymous function');
+
+ if ($lastMethod === null) {
+ $lastMethod = $firstMethod;
+ }
+
+ for ($i = $token->getLine();
+ $i < $firstMethod['startLine'];
+ $i++) {
+ $this->ignoredLines[$filename][] = $i;
+ }
+
+ for ($i = $token->getEndLine();
+ $i > $lastMethod['endLine'];
+ $i--) {
+ $this->ignoredLines[$filename][] = $i;
+ }
+ }
+ }
+ break;
+
+ case 'PHP_Token_NAMESPACE':
+ $this->ignoredLines[$filename][] = $token->getEndLine();
+
+ // Intentional fallthrough
+ case 'PHP_Token_DECLARE':
+ case 'PHP_Token_OPEN_TAG':
+ case 'PHP_Token_CLOSE_TAG':
+ case 'PHP_Token_USE':
+ $this->ignoredLines[$filename][] = $token->getLine();
+ break;
+ }
+
+ if ($ignore) {
+ $this->ignoredLines[$filename][] = $token->getLine();
+
+ if ($stop) {
+ $ignore = false;
+ $stop = false;
+ }
+ }
+ }
+
+ $this->ignoredLines[$filename][] = $numLines + 1;
+
+ $this->ignoredLines[$filename] = array_unique(
+ $this->ignoredLines[$filename]
+ );
+
+ sort($this->ignoredLines[$filename]);
+ }
+
+ return $this->ignoredLines[$filename];
+ }
+
+ /**
+ * @param array $data
+ * @param array $linesToBeCovered
+ * @param array $linesToBeUsed
+ *
+ * @throws UnintentionallyCoveredCodeException
+ */
+ private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed)
+ {
+ $allowedLines = $this->getAllowedLines(
+ $linesToBeCovered,
+ $linesToBeUsed
+ );
+
+ $unintentionallyCoveredUnits = [];
+
+ foreach ($data as $file => $_data) {
+ foreach ($_data as $line => $flag) {
+ if ($flag == 1 && !isset($allowedLines[$file][$line])) {
+ $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line);
+ }
+ }
+ }
+
+ $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits);
+
+ if (!empty($unintentionallyCoveredUnits)) {
+ throw new UnintentionallyCoveredCodeException(
+ $unintentionallyCoveredUnits
+ );
+ }
+ }
+
+ /**
+ * @param array $data
+ * @param array $linesToBeCovered
+ * @param array $linesToBeUsed
+ *
+ * @throws CoveredCodeNotExecutedException
+ */
+ private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed)
+ {
+ $expectedLines = $this->getAllowedLines(
+ $linesToBeCovered,
+ $linesToBeUsed
+ );
+
+ foreach ($data as $file => $_data) {
+ foreach (array_keys($_data) as $line) {
+ if (!isset($expectedLines[$file][$line])) {
+ continue;
+ }
+
+ unset($expectedLines[$file][$line]);
+ }
+ }
+
+ $message = '';
+
+ foreach ($expectedLines as $file => $lines) {
+ if (empty($lines)) {
+ continue;
+ }
+
+ foreach (array_keys($lines) as $line) {
+ $message .= sprintf('- %s:%d' . PHP_EOL, $file, $line);
+ }
+ }
+
+ if (!empty($message)) {
+ throw new CoveredCodeNotExecutedException($message);
+ }
+ }
+
+ /**
+ * @param array $linesToBeCovered
+ * @param array $linesToBeUsed
+ *
+ * @return array
+ */
+ private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed)
+ {
+ $allowedLines = [];
+
+ foreach (array_keys($linesToBeCovered) as $file) {
+ if (!isset($allowedLines[$file])) {
+ $allowedLines[$file] = [];
+ }
+
+ $allowedLines[$file] = array_merge(
+ $allowedLines[$file],
+ $linesToBeCovered[$file]
+ );
+ }
+
+ foreach (array_keys($linesToBeUsed) as $file) {
+ if (!isset($allowedLines[$file])) {
+ $allowedLines[$file] = [];
+ }
+
+ $allowedLines[$file] = array_merge(
+ $allowedLines[$file],
+ $linesToBeUsed[$file]
+ );
+ }
+
+ foreach (array_keys($allowedLines) as $file) {
+ $allowedLines[$file] = array_flip(
+ array_unique($allowedLines[$file])
+ );
+ }
+
+ return $allowedLines;
+ }
+
+ /**
+ * @return Driver
+ *
+ * @throws RuntimeException
+ */
+ private function selectDriver()
+ {
+ $runtime = new Runtime;
+
+ if (!$runtime->canCollectCodeCoverage()) {
+ throw new RuntimeException('No code coverage driver available');
+ }
+
+ if ($runtime->isHHVM()) {
+ return new HHVM;
+ } elseif ($runtime->isPHPDBG()) {
+ return new PHPDBG;
+ } else {
+ return new Xdebug;
+ }
+ }
+
+ /**
+ * @param array $unintentionallyCoveredUnits
+ *
+ * @return array
+ */
+ private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits)
+ {
+ $unintentionallyCoveredUnits = array_unique($unintentionallyCoveredUnits);
+ sort($unintentionallyCoveredUnits);
+
+ foreach (array_keys($unintentionallyCoveredUnits) as $k => $v) {
+ $unit = explode('::', $unintentionallyCoveredUnits[$k]);
+
+ if (count($unit) != 2) {
+ continue;
+ }
+
+ $class = new \ReflectionClass($unit[0]);
+
+ foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) {
+ if ($class->isSubclassOf($whitelisted)) {
+ unset($unintentionallyCoveredUnits[$k]);
+ break;
+ }
+ }
+ }
+
+ return array_values($unintentionallyCoveredUnits);
+ }
+
+ /**
+ * If we are processing uncovered files from whitelist,
+ * we can initialize the data before we start to speed up the tests
+ */
+ protected function initializeData()
+ {
+ $this->isInitialized = true;
+
+ if ($this->processUncoveredFilesFromWhitelist) {
+ $this->shouldCheckForDeadAndUnused = false;
+
+ $this->driver->start(true);
+
+ foreach ($this->filter->getWhitelist() as $file) {
+ if ($this->filter->isFile($file)) {
+ include_once($file);
+ }
+ }
+
+ $data = [];
+ $coverage = $this->driver->stop();
+
+ foreach ($coverage as $file => $fileCoverage) {
+ if ($this->filter->isFiltered($file)) {
+ continue;
+ }
+
+ foreach (array_keys($fileCoverage) as $key) {
+ if ($fileCoverage[$key] == Driver::LINE_EXECUTED) {
+ $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED;
+ }
+ }
+
+ $data[$file] = $fileCoverage;
+ }
+
+ $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Driver;
+
+/**
+ * Interface for code coverage drivers.
+ */
+interface Driver
+{
+ /**
+ * @var int
+ *
+ * @see http://xdebug.org/docs/code_coverage
+ */
+ const LINE_EXECUTED = 1;
+
+ /**
+ * @var int
+ *
+ * @see http://xdebug.org/docs/code_coverage
+ */
+ const LINE_NOT_EXECUTED = -1;
+
+ /**
+ * @var int
+ *
+ * @see http://xdebug.org/docs/code_coverage
+ */
+ const LINE_NOT_EXECUTABLE = -2;
+
+ /**
+ * Start collection of code coverage information.
+ *
+ * @param bool $determineUnusedAndDead
+ */
+ public function start($determineUnusedAndDead = true);
+
+ /**
+ * Stop collection of code coverage information.
+ *
+ * @return array
+ */
+ public function stop();
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Driver;
+
+/**
+ * Driver for HHVM's code coverage functionality.
+ *
+ * @codeCoverageIgnore
+ */
+class HHVM extends Xdebug
+{
+ /**
+ * Start collection of code coverage information.
+ *
+ * @param bool $determineUnusedAndDead
+ */
+ public function start($determineUnusedAndDead = true)
+ {
+ xdebug_start_code_coverage();
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Driver;
+
+use SebastianBergmann\CodeCoverage\RuntimeException;
+
+/**
+ * Driver for PHPDBG's code coverage functionality.
+ *
+ * @codeCoverageIgnore
+ */
+class PHPDBG implements Driver
+{
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ if (PHP_SAPI !== 'phpdbg') {
+ throw new RuntimeException(
+ 'This driver requires the PHPDBG SAPI'
+ );
+ }
+
+ if (!function_exists('phpdbg_start_oplog')) {
+ throw new RuntimeException(
+ 'This build of PHPDBG does not support code coverage'
+ );
+ }
+ }
+
+ /**
+ * Start collection of code coverage information.
+ *
+ * @param bool $determineUnusedAndDead
+ */
+ public function start($determineUnusedAndDead = true)
+ {
+ phpdbg_start_oplog();
+ }
+
+ /**
+ * Stop collection of code coverage information.
+ *
+ * @return array
+ */
+ public function stop()
+ {
+ static $fetchedLines = [];
+
+ $dbgData = phpdbg_end_oplog();
+
+ if ($fetchedLines == []) {
+ $sourceLines = phpdbg_get_executable();
+ } else {
+ $newFiles = array_diff(
+ get_included_files(),
+ array_keys($fetchedLines)
+ );
+
+ if ($newFiles) {
+ $sourceLines = phpdbg_get_executable(
+ ['files' => $newFiles]
+ );
+ } else {
+ $sourceLines = [];
+ }
+ }
+
+ foreach ($sourceLines as $file => $lines) {
+ foreach ($lines as $lineNo => $numExecuted) {
+ $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED;
+ }
+ }
+
+ $fetchedLines = array_merge($fetchedLines, $sourceLines);
+
+ return $this->detectExecutedLines($fetchedLines, $dbgData);
+ }
+
+ /**
+ * Convert phpdbg based data into the format CodeCoverage expects
+ *
+ * @param array $sourceLines
+ * @param array $dbgData
+ *
+ * @return array
+ */
+ private function detectExecutedLines(array $sourceLines, array $dbgData)
+ {
+ foreach ($dbgData as $file => $coveredLines) {
+ foreach ($coveredLines as $lineNo => $numExecuted) {
+ // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown.
+ // make sure we only mark lines executed which are actually executable.
+ if (isset($sourceLines[$file][$lineNo])) {
+ $sourceLines[$file][$lineNo] = self::LINE_EXECUTED;
+ }
+ }
+ }
+
+ return $sourceLines;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Driver;
+
+use SebastianBergmann\CodeCoverage\RuntimeException;
+
+/**
+ * Driver for Xdebug's code coverage functionality.
+ *
+ * @codeCoverageIgnore
+ */
+class Xdebug implements Driver
+{
+ /**
+ * Cache the number of lines for each file
+ *
+ * @var array
+ */
+ private $cacheNumLines = [];
+
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ if (!extension_loaded('xdebug')) {
+ throw new RuntimeException('This driver requires Xdebug');
+ }
+
+ if (version_compare(phpversion('xdebug'), '2.2.1', '>=') &&
+ !ini_get('xdebug.coverage_enable')) {
+ throw new RuntimeException(
+ 'xdebug.coverage_enable=On has to be set in php.ini'
+ );
+ }
+ }
+
+ /**
+ * Start collection of code coverage information.
+ *
+ * @param bool $determineUnusedAndDead
+ */
+ public function start($determineUnusedAndDead = true)
+ {
+ if ($determineUnusedAndDead) {
+ xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
+ } else {
+ xdebug_start_code_coverage();
+ }
+ }
+
+ /**
+ * Stop collection of code coverage information.
+ *
+ * @return array
+ */
+ public function stop()
+ {
+ $data = xdebug_get_code_coverage();
+ xdebug_stop_code_coverage();
+
+ return $this->cleanup($data);
+ }
+
+ /**
+ * @param array $data
+ *
+ * @return array
+ */
+ private function cleanup(array $data)
+ {
+ foreach (array_keys($data) as $file) {
+ unset($data[$file][0]);
+
+ if (strpos($file, 'xdebug://debug-eval') !== 0 && file_exists($file)) {
+ $numLines = $this->getNumberOfLinesInFile($file);
+
+ foreach (array_keys($data[$file]) as $line) {
+ if ($line > $numLines) {
+ unset($data[$file][$line]);
+ }
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return int
+ */
+ private function getNumberOfLinesInFile($file)
+ {
+ if (!isset($this->cacheNumLines[$file])) {
+ $buffer = file_get_contents($file);
+ $lines = substr_count($buffer, "\n");
+
+ if (substr($buffer, -1) !== "\n") {
+ $lines++;
+ }
+
+ $this->cacheNumLines[$file] = $lines;
+ }
+
+ return $this->cacheNumLines[$file];
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+/**
+ * Exception that is raised when covered code is not executed.
+ */
+class CoveredCodeNotExecutedException extends RuntimeException
+{
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+/**
+ * Exception interface for php-code-coverage component.
+ */
+interface Exception
+{
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+class InvalidArgumentException extends \InvalidArgumentException implements Exception
+{
+ /**
+ * @param int $argument
+ * @param string $type
+ * @param mixed $value
+ *
+ * @return InvalidArgumentException
+ */
+ public static function create($argument, $type, $value = null)
+ {
+ $stack = debug_backtrace(0);
+
+ return new self(
+ sprintf(
+ 'Argument #%d%sof %s::%s() must be a %s',
+ $argument,
+ $value !== null ? ' (' . gettype($value) . '#' . $value . ')' : ' (No Value) ',
+ $stack[1]['class'],
+ $stack[1]['function'],
+ $type
+ )
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+/**
+ * Exception that is raised when @covers must be used but is not.
+ */
+class MissingCoversAnnotationException extends RuntimeException
+{
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+class RuntimeException extends \RuntimeException implements Exception
+{
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+/**
+ * Exception that is raised when code is unintentionally covered.
+ */
+class UnintentionallyCoveredCodeException extends RuntimeException
+{
+ /**
+ * @var array
+ */
+ private $unintentionallyCoveredUnits = [];
+
+ /**
+ * @param array $unintentionallyCoveredUnits
+ */
+ public function __construct(array $unintentionallyCoveredUnits)
+ {
+ $this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits;
+
+ parent::__construct($this->toString());
+ }
+
+ /**
+ * @return array
+ */
+ public function getUnintentionallyCoveredUnits()
+ {
+ return $this->unintentionallyCoveredUnits;
+ }
+
+ /**
+ * @return string
+ */
+ private function toString()
+ {
+ $message = '';
+
+ foreach ($this->unintentionallyCoveredUnits as $unit) {
+ $message .= '- ' . $unit . "\n";
+ }
+
+ return $message;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+/**
+ * Filter for whitelisting of code coverage information.
+ */
+class Filter
+{
+ /**
+ * Source files that are whitelisted.
+ *
+ * @var array
+ */
+ private $whitelistedFiles = [];
+
+ /**
+ * Adds a directory to the whitelist (recursively).
+ *
+ * @param string $directory
+ * @param string $suffix
+ * @param string $prefix
+ */
+ public function addDirectoryToWhitelist($directory, $suffix = '.php', $prefix = '')
+ {
+ $facade = new \File_Iterator_Facade;
+ $files = $facade->getFilesAsArray($directory, $suffix, $prefix);
+
+ foreach ($files as $file) {
+ $this->addFileToWhitelist($file);
+ }
+ }
+
+ /**
+ * Adds a file to the whitelist.
+ *
+ * @param string $filename
+ */
+ public function addFileToWhitelist($filename)
+ {
+ $this->whitelistedFiles[realpath($filename)] = true;
+ }
+
+ /**
+ * Adds files to the whitelist.
+ *
+ * @param array $files
+ */
+ public function addFilesToWhitelist(array $files)
+ {
+ foreach ($files as $file) {
+ $this->addFileToWhitelist($file);
+ }
+ }
+
+ /**
+ * Removes a directory from the whitelist (recursively).
+ *
+ * @param string $directory
+ * @param string $suffix
+ * @param string $prefix
+ */
+ public function removeDirectoryFromWhitelist($directory, $suffix = '.php', $prefix = '')
+ {
+ $facade = new \File_Iterator_Facade;
+ $files = $facade->getFilesAsArray($directory, $suffix, $prefix);
+
+ foreach ($files as $file) {
+ $this->removeFileFromWhitelist($file);
+ }
+ }
+
+ /**
+ * Removes a file from the whitelist.
+ *
+ * @param string $filename
+ */
+ public function removeFileFromWhitelist($filename)
+ {
+ $filename = realpath($filename);
+
+ unset($this->whitelistedFiles[$filename]);
+ }
+
+ /**
+ * Checks whether a filename is a real filename.
+ *
+ * @param string $filename
+ *
+ * @return bool
+ */
+ public function isFile($filename)
+ {
+ if ($filename == '-' ||
+ strpos($filename, 'vfs://') === 0 ||
+ strpos($filename, 'xdebug://debug-eval') !== false ||
+ strpos($filename, 'eval()\'d code') !== false ||
+ strpos($filename, 'runtime-created function') !== false ||
+ strpos($filename, 'runkit created function') !== false ||
+ strpos($filename, 'assert code') !== false ||
+ strpos($filename, 'regexp code') !== false) {
+ return false;
+ }
+
+ return file_exists($filename);
+ }
+
+ /**
+ * Checks whether or not a file is filtered.
+ *
+ * @param string $filename
+ *
+ * @return bool
+ */
+ public function isFiltered($filename)
+ {
+ if (!$this->isFile($filename)) {
+ return true;
+ }
+
+ $filename = realpath($filename);
+
+ return !isset($this->whitelistedFiles[$filename]);
+ }
+
+ /**
+ * Returns the list of whitelisted files.
+ *
+ * @return array
+ */
+ public function getWhitelist()
+ {
+ return array_keys($this->whitelistedFiles);
+ }
+
+ /**
+ * Returns whether this filter has a whitelist.
+ *
+ * @return bool
+ */
+ public function hasWhitelist()
+ {
+ return !empty($this->whitelistedFiles);
+ }
+
+ /**
+ * Returns the whitelisted files.
+ *
+ * @return array
+ */
+ public function getWhitelistedFiles()
+ {
+ return $this->whitelistedFiles;
+ }
+
+ /**
+ * Sets the whitelisted files.
+ *
+ * @param array $whitelistedFiles
+ */
+ public function setWhitelistedFiles($whitelistedFiles)
+ {
+ $this->whitelistedFiles = $whitelistedFiles;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use SebastianBergmann\CodeCoverage\Util;
+
+/**
+ * Base class for nodes in the code coverage information tree.
+ */
+abstract class AbstractNode implements \Countable
+{
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var string
+ */
+ private $path;
+
+ /**
+ * @var array
+ */
+ private $pathArray;
+
+ /**
+ * @var AbstractNode
+ */
+ private $parent;
+
+ /**
+ * @var string
+ */
+ private $id;
+
+ /**
+ * Constructor.
+ *
+ * @param string $name
+ * @param AbstractNode $parent
+ */
+ public function __construct($name, AbstractNode $parent = null)
+ {
+ if (substr($name, -1) == '/') {
+ $name = substr($name, 0, -1);
+ }
+
+ $this->name = $name;
+ $this->parent = $parent;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getId()
+ {
+ if ($this->id === null) {
+ $parent = $this->getParent();
+
+ if ($parent === null) {
+ $this->id = 'index';
+ } else {
+ $parentId = $parent->getId();
+
+ if ($parentId == 'index') {
+ $this->id = str_replace(':', '_', $this->name);
+ } else {
+ $this->id = $parentId . '/' . $this->name;
+ }
+ }
+ }
+
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPath()
+ {
+ if ($this->path === null) {
+ if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) {
+ $this->path = $this->name;
+ } else {
+ $this->path = $this->parent->getPath() . '/' . $this->name;
+ }
+ }
+
+ return $this->path;
+ }
+
+ /**
+ * @return array
+ */
+ public function getPathAsArray()
+ {
+ if ($this->pathArray === null) {
+ if ($this->parent === null) {
+ $this->pathArray = [];
+ } else {
+ $this->pathArray = $this->parent->getPathAsArray();
+ }
+
+ $this->pathArray[] = $this;
+ }
+
+ return $this->pathArray;
+ }
+
+ /**
+ * @return AbstractNode
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Returns the percentage of classes that has been tested.
+ *
+ * @param bool $asString
+ *
+ * @return int
+ */
+ public function getTestedClassesPercent($asString = true)
+ {
+ return Util::percent(
+ $this->getNumTestedClasses(),
+ $this->getNumClasses(),
+ $asString
+ );
+ }
+
+ /**
+ * Returns the percentage of traits that has been tested.
+ *
+ * @param bool $asString
+ *
+ * @return int
+ */
+ public function getTestedTraitsPercent($asString = true)
+ {
+ return Util::percent(
+ $this->getNumTestedTraits(),
+ $this->getNumTraits(),
+ $asString
+ );
+ }
+
+ /**
+ * Returns the percentage of traits that has been tested.
+ *
+ * @param bool $asString
+ *
+ * @return int
+ */
+ public function getTestedClassesAndTraitsPercent($asString = true)
+ {
+ return Util::percent(
+ $this->getNumTestedClassesAndTraits(),
+ $this->getNumClassesAndTraits(),
+ $asString
+ );
+ }
+
+ /**
+ * Returns the percentage of methods that has been tested.
+ *
+ * @param bool $asString
+ *
+ * @return int
+ */
+ public function getTestedMethodsPercent($asString = true)
+ {
+ return Util::percent(
+ $this->getNumTestedMethods(),
+ $this->getNumMethods(),
+ $asString
+ );
+ }
+
+ /**
+ * Returns the percentage of executed lines.
+ *
+ * @param bool $asString
+ *
+ * @return int
+ */
+ public function getLineExecutedPercent($asString = true)
+ {
+ return Util::percent(
+ $this->getNumExecutedLines(),
+ $this->getNumExecutableLines(),
+ $asString
+ );
+ }
+
+ /**
+ * Returns the number of classes and traits.
+ *
+ * @return int
+ */
+ public function getNumClassesAndTraits()
+ {
+ return $this->getNumClasses() + $this->getNumTraits();
+ }
+
+ /**
+ * Returns the number of tested classes and traits.
+ *
+ * @return int
+ */
+ public function getNumTestedClassesAndTraits()
+ {
+ return $this->getNumTestedClasses() + $this->getNumTestedTraits();
+ }
+
+ /**
+ * Returns the classes and traits of this node.
+ *
+ * @return array
+ */
+ public function getClassesAndTraits()
+ {
+ return array_merge($this->getClasses(), $this->getTraits());
+ }
+
+ /**
+ * Returns the classes of this node.
+ *
+ * @return array
+ */
+ abstract public function getClasses();
+
+ /**
+ * Returns the traits of this node.
+ *
+ * @return array
+ */
+ abstract public function getTraits();
+
+ /**
+ * Returns the functions of this node.
+ *
+ * @return array
+ */
+ abstract public function getFunctions();
+
+ /**
+ * Returns the LOC/CLOC/NCLOC of this node.
+ *
+ * @return array
+ */
+ abstract public function getLinesOfCode();
+
+ /**
+ * Returns the number of executable lines.
+ *
+ * @return int
+ */
+ abstract public function getNumExecutableLines();
+
+ /**
+ * Returns the number of executed lines.
+ *
+ * @return int
+ */
+ abstract public function getNumExecutedLines();
+
+ /**
+ * Returns the number of classes.
+ *
+ * @return int
+ */
+ abstract public function getNumClasses();
+
+ /**
+ * Returns the number of tested classes.
+ *
+ * @return int
+ */
+ abstract public function getNumTestedClasses();
+
+ /**
+ * Returns the number of traits.
+ *
+ * @return int
+ */
+ abstract public function getNumTraits();
+
+ /**
+ * Returns the number of tested traits.
+ *
+ * @return int
+ */
+ abstract public function getNumTestedTraits();
+
+ /**
+ * Returns the number of methods.
+ *
+ * @return int
+ */
+ abstract public function getNumMethods();
+
+ /**
+ * Returns the number of tested methods.
+ *
+ * @return int
+ */
+ abstract public function getNumTestedMethods();
+
+ /**
+ * Returns the number of functions.
+ *
+ * @return int
+ */
+ abstract public function getNumFunctions();
+
+ /**
+ * Returns the number of tested functions.
+ *
+ * @return int
+ */
+ abstract public function getNumTestedFunctions();
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+
+class Builder
+{
+ /**
+ * @param CodeCoverage $coverage
+ *
+ * @return Directory
+ */
+ public function build(CodeCoverage $coverage)
+ {
+ $files = $coverage->getData();
+ $commonPath = $this->reducePaths($files);
+ $root = new Directory(
+ $commonPath,
+ null
+ );
+
+ $this->addItems(
+ $root,
+ $this->buildDirectoryStructure($files),
+ $coverage->getTests(),
+ $coverage->getCacheTokens()
+ );
+
+ return $root;
+ }
+
+ /**
+ * @param Directory $root
+ * @param array $items
+ * @param array $tests
+ * @param bool $cacheTokens
+ */
+ private function addItems(Directory $root, array $items, array $tests, $cacheTokens)
+ {
+ foreach ($items as $key => $value) {
+ if (substr($key, -2) == '/f') {
+ $key = substr($key, 0, -2);
+
+ if (file_exists($root->getPath() . DIRECTORY_SEPARATOR . $key)) {
+ $root->addFile($key, $value, $tests, $cacheTokens);
+ }
+ } else {
+ $child = $root->addDirectory($key);
+ $this->addItems($child, $value, $tests, $cacheTokens);
+ }
+ }
+ }
+
+ /**
+ * Builds an array representation of the directory structure.
+ *
+ * For instance,
+ *
+ * <code>
+ * Array
+ * (
+ * [Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * </code>
+ *
+ * is transformed into
+ *
+ * <code>
+ * Array
+ * (
+ * [.] => Array
+ * (
+ * [Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * )
+ * </code>
+ *
+ * @param array $files
+ *
+ * @return array
+ */
+ private function buildDirectoryStructure($files)
+ {
+ $result = [];
+
+ foreach ($files as $path => $file) {
+ $path = explode('/', $path);
+ $pointer = &$result;
+ $max = count($path);
+
+ for ($i = 0; $i < $max; $i++) {
+ if ($i == ($max - 1)) {
+ $type = '/f';
+ } else {
+ $type = '';
+ }
+
+ $pointer = &$pointer[$path[$i] . $type];
+ }
+
+ $pointer = $file;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Reduces the paths by cutting the longest common start path.
+ *
+ * For instance,
+ *
+ * <code>
+ * Array
+ * (
+ * [/home/sb/Money/Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [/home/sb/Money/MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * </code>
+ *
+ * is reduced to
+ *
+ * <code>
+ * Array
+ * (
+ * [Money.php] => Array
+ * (
+ * ...
+ * )
+ *
+ * [MoneyBag.php] => Array
+ * (
+ * ...
+ * )
+ * )
+ * </code>
+ *
+ * @param array $files
+ *
+ * @return string
+ */
+ private function reducePaths(&$files)
+ {
+ if (empty($files)) {
+ return '.';
+ }
+
+ $commonPath = '';
+ $paths = array_keys($files);
+
+ if (count($files) == 1) {
+ $commonPath = dirname($paths[0]) . '/';
+ $files[basename($paths[0])] = $files[$paths[0]];
+
+ unset($files[$paths[0]]);
+
+ return $commonPath;
+ }
+
+ $max = count($paths);
+
+ for ($i = 0; $i < $max; $i++) {
+ // strip phar:// prefixes
+ if (strpos($paths[$i], 'phar://') === 0) {
+ $paths[$i] = substr($paths[$i], 7);
+ $paths[$i] = strtr($paths[$i], '/', DIRECTORY_SEPARATOR);
+ }
+ $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);
+
+ if (empty($paths[$i][0])) {
+ $paths[$i][0] = DIRECTORY_SEPARATOR;
+ }
+ }
+
+ $done = false;
+ $max = count($paths);
+
+ while (!$done) {
+ for ($i = 0; $i < $max - 1; $i++) {
+ if (!isset($paths[$i][0]) ||
+ !isset($paths[$i+1][0]) ||
+ $paths[$i][0] != $paths[$i+1][0]) {
+ $done = true;
+ break;
+ }
+ }
+
+ if (!$done) {
+ $commonPath .= $paths[0][0];
+
+ if ($paths[0][0] != DIRECTORY_SEPARATOR) {
+ $commonPath .= DIRECTORY_SEPARATOR;
+ }
+
+ for ($i = 0; $i < $max; $i++) {
+ array_shift($paths[$i]);
+ }
+ }
+ }
+
+ $original = array_keys($files);
+ $max = count($original);
+
+ for ($i = 0; $i < $max; $i++) {
+ $files[implode('/', $paths[$i])] = $files[$original[$i]];
+ unset($files[$original[$i]]);
+ }
+
+ ksort($files);
+
+ return substr($commonPath, 0, -1);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use SebastianBergmann\CodeCoverage\InvalidArgumentException;
+
+/**
+ * Represents a directory in the code coverage information tree.
+ */
+class Directory extends AbstractNode implements \IteratorAggregate
+{
+ /**
+ * @var AbstractNode[]
+ */
+ private $children = [];
+
+ /**
+ * @var Directory[]
+ */
+ private $directories = [];
+
+ /**
+ * @var File[]
+ */
+ private $files = [];
+
+ /**
+ * @var array
+ */
+ private $classes;
+
+ /**
+ * @var array
+ */
+ private $traits;
+
+ /**
+ * @var array
+ */
+ private $functions;
+
+ /**
+ * @var array
+ */
+ private $linesOfCode = null;
+
+ /**
+ * @var int
+ */
+ private $numFiles = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutableLines = -1;
+
+ /**
+ * @var int
+ */
+ private $numExecutedLines = -1;
+
+ /**
+ * @var int
+ */
+ private $numClasses = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedClasses = -1;
+
+ /**
+ * @var int
+ */
+ private $numTraits = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedTraits = -1;
+
+ /**
+ * @var int
+ */
+ private $numMethods = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedMethods = -1;
+
+ /**
+ * @var int
+ */
+ private $numFunctions = -1;
+
+ /**
+ * @var int
+ */
+ private $numTestedFunctions = -1;
+
+ /**
+ * Returns the number of files in/under this node.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ if ($this->numFiles == -1) {
+ $this->numFiles = 0;
+
+ foreach ($this->children as $child) {
+ $this->numFiles += count($child);
+ }
+ }
+
+ return $this->numFiles;
+ }
+
+ /**
+ * Returns an iterator for this node.
+ *
+ * @return \RecursiveIteratorIterator
+ */
+ public function getIterator()
+ {
+ return new \RecursiveIteratorIterator(
+ new Iterator($this),
+ \RecursiveIteratorIterator::SELF_FIRST
+ );
+ }
+
+ /**
+ * Adds a new directory.
+ *
+ * @param string $name
+ *
+ * @return Directory
+ */
+ public function addDirectory($name)
+ {
+ $directory = new self($name, $this);
+
+ $this->children[] = $directory;
+ $this->directories[] = &$this->children[count($this->children) - 1];
+
+ return $directory;
+ }
+
+ /**
+ * Adds a new file.
+ *
+ * @param string $name
+ * @param array $coverageData
+ * @param array $testData
+ * @param bool $cacheTokens
+ *
+ * @return File
+ *
+ * @throws InvalidArgumentException
+ */
+ public function addFile($name, array $coverageData, array $testData, $cacheTokens)
+ {
+ $file = new File(
+ $name,
+ $this,
+ $coverageData,
+ $testData,
+ $cacheTokens
+ );
+
+ $this->children[] = $file;
+ $this->files[] = &$this->children[count($this->children) - 1];
+
+ $this->numExecutableLines = -1;
+ $this->numExecutedLines = -1;
+
+ return $file;
+ }
+
+ /**
+ * Returns the directories in this directory.
+ *
+ * @return array
+ */
+ public function getDirectories()
+ {
+ return $this->directories;
+ }
+
+ /**
+ * Returns the files in this directory.
+ *
+ * @return array
+ */
+ public function getFiles()
+ {
+ return $this->files;
+ }
+
+ /**
+ * Returns the child nodes of this node.
+ *
+ * @return array
+ */
+ public function getChildNodes()
+ {
+ return $this->children;
+ }
+
+ /**
+ * Returns the classes of this node.
+ *
+ * @return array
+ */
+ public function getClasses()
+ {
+ if ($this->classes === null) {
+ $this->classes = [];
+
+ foreach ($this->children as $child) {
+ $this->classes = array_merge(
+ $this->classes,
+ $child->getClasses()
+ );
+ }
+ }
+
+ return $this->classes;
+ }
+
+ /**
+ * Returns the traits of this node.
+ *
+ * @return array
+ */
+ public function getTraits()
+ {
+ if ($this->traits === null) {
+ $this->traits = [];
+
+ foreach ($this->children as $child) {
+ $this->traits = array_merge(
+ $this->traits,
+ $child->getTraits()
+ );
+ }
+ }
+
+ return $this->traits;
+ }
+
+ /**
+ * Returns the functions of this node.
+ *
+ * @return array
+ */
+ public function getFunctions()
+ {
+ if ($this->functions === null) {
+ $this->functions = [];
+
+ foreach ($this->children as $child) {
+ $this->functions = array_merge(
+ $this->functions,
+ $child->getFunctions()
+ );
+ }
+ }
+
+ return $this->functions;
+ }
+
+ /**
+ * Returns the LOC/CLOC/NCLOC of this node.
+ *
+ * @return array
+ */
+ public function getLinesOfCode()
+ {
+ if ($this->linesOfCode === null) {
+ $this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0];
+
+ foreach ($this->children as $child) {
+ $linesOfCode = $child->getLinesOfCode();
+
+ $this->linesOfCode['loc'] += $linesOfCode['loc'];
+ $this->linesOfCode['cloc'] += $linesOfCode['cloc'];
+ $this->linesOfCode['ncloc'] += $linesOfCode['ncloc'];
+ }
+ }
+
+ return $this->linesOfCode;
+ }
+
+ /**
+ * Returns the number of executable lines.
+ *
+ * @return int
+ */
+ public function getNumExecutableLines()
+ {
+ if ($this->numExecutableLines == -1) {
+ $this->numExecutableLines = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutableLines += $child->getNumExecutableLines();
+ }
+ }
+
+ return $this->numExecutableLines;
+ }
+
+ /**
+ * Returns the number of executed lines.
+ *
+ * @return int
+ */
+ public function getNumExecutedLines()
+ {
+ if ($this->numExecutedLines == -1) {
+ $this->numExecutedLines = 0;
+
+ foreach ($this->children as $child) {
+ $this->numExecutedLines += $child->getNumExecutedLines();
+ }
+ }
+
+ return $this->numExecutedLines;
+ }
+
+ /**
+ * Returns the number of classes.
+ *
+ * @return int
+ */
+ public function getNumClasses()
+ {
+ if ($this->numClasses == -1) {
+ $this->numClasses = 0;
+
+ foreach ($this->children as $child) {
+ $this->numClasses += $child->getNumClasses();
+ }
+ }
+
+ return $this->numClasses;
+ }
+
+ /**
+ * Returns the number of tested classes.
+ *
+ * @return int
+ */
+ public function getNumTestedClasses()
+ {
+ if ($this->numTestedClasses == -1) {
+ $this->numTestedClasses = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedClasses += $child->getNumTestedClasses();
+ }
+ }
+
+ return $this->numTestedClasses;
+ }
+
+ /**
+ * Returns the number of traits.
+ *
+ * @return int
+ */
+ public function getNumTraits()
+ {
+ if ($this->numTraits == -1) {
+ $this->numTraits = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTraits += $child->getNumTraits();
+ }
+ }
+
+ return $this->numTraits;
+ }
+
+ /**
+ * Returns the number of tested traits.
+ *
+ * @return int
+ */
+ public function getNumTestedTraits()
+ {
+ if ($this->numTestedTraits == -1) {
+ $this->numTestedTraits = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedTraits += $child->getNumTestedTraits();
+ }
+ }
+
+ return $this->numTestedTraits;
+ }
+
+ /**
+ * Returns the number of methods.
+ *
+ * @return int
+ */
+ public function getNumMethods()
+ {
+ if ($this->numMethods == -1) {
+ $this->numMethods = 0;
+
+ foreach ($this->children as $child) {
+ $this->numMethods += $child->getNumMethods();
+ }
+ }
+
+ return $this->numMethods;
+ }
+
+ /**
+ * Returns the number of tested methods.
+ *
+ * @return int
+ */
+ public function getNumTestedMethods()
+ {
+ if ($this->numTestedMethods == -1) {
+ $this->numTestedMethods = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedMethods += $child->getNumTestedMethods();
+ }
+ }
+
+ return $this->numTestedMethods;
+ }
+
+ /**
+ * Returns the number of functions.
+ *
+ * @return int
+ */
+ public function getNumFunctions()
+ {
+ if ($this->numFunctions == -1) {
+ $this->numFunctions = 0;
+
+ foreach ($this->children as $child) {
+ $this->numFunctions += $child->getNumFunctions();
+ }
+ }
+
+ return $this->numFunctions;
+ }
+
+ /**
+ * Returns the number of tested functions.
+ *
+ * @return int
+ */
+ public function getNumTestedFunctions()
+ {
+ if ($this->numTestedFunctions == -1) {
+ $this->numTestedFunctions = 0;
+
+ foreach ($this->children as $child) {
+ $this->numTestedFunctions += $child->getNumTestedFunctions();
+ }
+ }
+
+ return $this->numTestedFunctions;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Node;
+
+use SebastianBergmann\CodeCoverage\InvalidArgumentException;
+
+/**
+ * Represents a file in the code coverage information tree.
+ */
+class File extends AbstractNode
+{
+ /**
+ * @var array
+ */
+ private $coverageData;
+
+ /**
+ * @var array
+ */
+ private $testData;
+
+ /**
+ * @var int
+ */
+ private $numExecutableLines = 0;
+
+ /**
+ * @var int
+ */
+ private $numExecutedLines = 0;
+
+ /**
+ * @var array
+ */
+ private $classes = [];
+
+ /**
+ * @var array
+ */
+ private $traits = [];
+
+ /**
+ * @var array
+ */
+ private $functions = [];
+
+ /**
+ * @var array
+ */
+ private $linesOfCode = [];
+
+ /**
+ * @var int
+ */
+ private $numClasses = null;
+
+ /**
+ * @var int
+ */
+ private $numTestedClasses = 0;
+
+ /**
+ * @var int
+ */
+ private $numTraits = null;
+
+ /**
+ * @var int
+ */
+ private $numTestedTraits = 0;
+
+ /**
+ * @var int
+ */
+ private $numMethods = null;
+
+ /**
+ * @var int
+ */
+ private $numTestedMethods = null;
+
+ /**
+ * @var int
+ */
+ private $numTestedFunctions = null;
+
+ /**
+ * @var array
+ */
+ private $startLines = [];
+
+ /**
+ * @var array
+ */
+ private $endLines = [];
+
+ /**
+ * @var bool
+ */
+ private $cacheTokens;
+
+ /**
+ * Constructor.
+ *
+ * @param string $name
+ * @param AbstractNode $parent
+ * @param array $coverageData
+ * @param array $testData
+ * @param bool $cacheTokens
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct($name, AbstractNode $parent, array $coverageData, array $testData, $cacheTokens)
+ {
+ if (!is_bool($cacheTokens)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'boolean'
+ );
+ }
+
+ parent::__construct($name, $parent);
+
+ $this->coverageData = $coverageData;
+ $this->testData = $testData;
+ $this->cacheTokens = $cacheTokens;
+
+ $this->calculateStatistics();
+ }
+
+ /**
+ * Returns the number of files in/under this node.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the code coverage data of this node.
+ *
+ * @return array
+ */
+ public function getCoverageData()
+ {
+ return $this->coverageData;
+ }
+
+ /**
+ * Returns the test data of this node.
+ *
+ * @return array
+ */
+ public function getTestData()
+ {
+ return $this->testData;
+ }
+
+ /**
+ * Returns the classes of this node.
+ *
+ * @return array
+ */
+ public function getClasses()
+ {
+ return $this->classes;
+ }
+
+ /**
+ * Returns the traits of this node.
+ *
+ * @return array
+ */
+ public function getTraits()
+ {
+ return $this->traits;
+ }
+
+ /**
+ * Returns the functions of this node.
+ *
+ * @return array
+ */
+ public function getFunctions()
+ {
+ return $this->functions;
+ }
+
+ /**
+ * Returns the LOC/CLOC/NCLOC of this node.
+ *
+ * @return array
+ */
+ public function getLinesOfCode()
+ {
+ return $this->linesOfCode;
+ }
+
+ /**
+ * Returns the number of executable lines.
+ *
+ * @return int
+ */
+ public function getNumExecutableLines()
+ {
+ return $this->numExecutableLines;
+ }
+
+ /**
+ * Returns the number of executed lines.
+ *
+ * @return int
+ */
+ public function getNumExecutedLines()
+ {
+ return $this->numExecutedLines;
+ }
+
+ /**
+ * Returns the number of classes.
+ *
+ * @return int
+ */
+ public function getNumClasses()
+ {
+ if ($this->numClasses === null) {
+ $this->numClasses = 0;
+
+ foreach ($this->classes as $class) {
+ foreach ($class['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numClasses++;
+
+ continue 2;
+ }
+ }
+ }
+ }
+
+ return $this->numClasses;
+ }
+
+ /**
+ * Returns the number of tested classes.
+ *
+ * @return int
+ */
+ public function getNumTestedClasses()
+ {
+ return $this->numTestedClasses;
+ }
+
+ /**
+ * Returns the number of traits.
+ *
+ * @return int
+ */
+ public function getNumTraits()
+ {
+ if ($this->numTraits === null) {
+ $this->numTraits = 0;
+
+ foreach ($this->traits as $trait) {
+ foreach ($trait['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numTraits++;
+
+ continue 2;
+ }
+ }
+ }
+ }
+
+ return $this->numTraits;
+ }
+
+ /**
+ * Returns the number of tested traits.
+ *
+ * @return int
+ */
+ public function getNumTestedTraits()
+ {
+ return $this->numTestedTraits;
+ }
+
+ /**
+ * Returns the number of methods.
+ *
+ * @return int
+ */
+ public function getNumMethods()
+ {
+ if ($this->numMethods === null) {
+ $this->numMethods = 0;
+
+ foreach ($this->classes as $class) {
+ foreach ($class['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numMethods++;
+ }
+ }
+ }
+
+ foreach ($this->traits as $trait) {
+ foreach ($trait['methods'] as $method) {
+ if ($method['executableLines'] > 0) {
+ $this->numMethods++;
+ }
+ }
+ }
+ }
+
+ return $this->numMethods;
+ }
+
+ /**
+ * Returns the number of tested methods.
+ *
+ * @return int
+ */
+ public function getNumTestedMethods()
+ {
+ if ($this->numTestedMethods === null) {
+ $this->numTestedMethods = 0;
+
+ foreach ($this->classes as $class) {
+ foreach ($class['methods'] as $method) {
+ if ($method['executableLines'] > 0 &&
+ $method['coverage'] == 100) {
+ $this->numTestedMethods++;
+ }
+ }
+ }
+
+ foreach ($this->traits as $trait) {
+ foreach ($trait['methods'] as $method) {
+ if ($method['executableLines'] > 0 &&
+ $method['coverage'] == 100) {
+ $this->numTestedMethods++;
+ }
+ }
+ }
+ }
+
+ return $this->numTestedMethods;
+ }
+
+ /**
+ * Returns the number of functions.
+ *
+ * @return int
+ */
+ public function getNumFunctions()
+ {
+ return count($this->functions);
+ }
+
+ /**
+ * Returns the number of tested functions.
+ *
+ * @return int
+ */
+ public function getNumTestedFunctions()
+ {
+ if ($this->numTestedFunctions === null) {
+ $this->numTestedFunctions = 0;
+
+ foreach ($this->functions as $function) {
+ if ($function['executableLines'] > 0 &&
+ $function['coverage'] == 100) {
+ $this->numTestedFunctions++;
+ }
+ }
+ }
+
+ return $this->numTestedFunctions;
+ }
+
+ /**
+ * Calculates coverage statistics for the file.
+ */
+ protected function calculateStatistics()
+ {
+ $classStack = $functionStack = [];
+
+ if ($this->cacheTokens) {
+ $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath());
+ } else {
+ $tokens = new \PHP_Token_Stream($this->getPath());
+ }
+
+ $this->processClasses($tokens);
+ $this->processTraits($tokens);
+ $this->processFunctions($tokens);
+ $this->linesOfCode = $tokens->getLinesOfCode();
+ unset($tokens);
+
+ for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) {
+ if (isset($this->startLines[$lineNumber])) {
+ // Start line of a class.
+ if (isset($this->startLines[$lineNumber]['className'])) {
+ if (isset($currentClass)) {
+ $classStack[] = &$currentClass;
+ }
+
+ $currentClass = &$this->startLines[$lineNumber];
+ } // Start line of a trait.
+ elseif (isset($this->startLines[$lineNumber]['traitName'])) {
+ $currentTrait = &$this->startLines[$lineNumber];
+ } // Start line of a method.
+ elseif (isset($this->startLines[$lineNumber]['methodName'])) {
+ $currentMethod = &$this->startLines[$lineNumber];
+ } // Start line of a function.
+ elseif (isset($this->startLines[$lineNumber]['functionName'])) {
+ if (isset($currentFunction)) {
+ $functionStack[] = &$currentFunction;
+ }
+
+ $currentFunction = &$this->startLines[$lineNumber];
+ }
+ }
+
+ if (isset($this->coverageData[$lineNumber])) {
+ if (isset($currentClass)) {
+ $currentClass['executableLines']++;
+ }
+
+ if (isset($currentTrait)) {
+ $currentTrait['executableLines']++;
+ }
+
+ if (isset($currentMethod)) {
+ $currentMethod['executableLines']++;
+ }
+
+ if (isset($currentFunction)) {
+ $currentFunction['executableLines']++;
+ }
+
+ $this->numExecutableLines++;
+
+ if (count($this->coverageData[$lineNumber]) > 0) {
+ if (isset($currentClass)) {
+ $currentClass['executedLines']++;
+ }
+
+ if (isset($currentTrait)) {
+ $currentTrait['executedLines']++;
+ }
+
+ if (isset($currentMethod)) {
+ $currentMethod['executedLines']++;
+ }
+
+ if (isset($currentFunction)) {
+ $currentFunction['executedLines']++;
+ }
+
+ $this->numExecutedLines++;
+ }
+ }
+
+ if (isset($this->endLines[$lineNumber])) {
+ // End line of a class.
+ if (isset($this->endLines[$lineNumber]['className'])) {
+ unset($currentClass);
+
+ if ($classStack) {
+ end($classStack);
+ $key = key($classStack);
+ $currentClass = &$classStack[$key];
+ unset($classStack[$key]);
+ }
+ } // End line of a trait.
+ elseif (isset($this->endLines[$lineNumber]['traitName'])) {
+ unset($currentTrait);
+ } // End line of a method.
+ elseif (isset($this->endLines[$lineNumber]['methodName'])) {
+ unset($currentMethod);
+ } // End line of a function.
+ elseif (isset($this->endLines[$lineNumber]['functionName'])) {
+ unset($currentFunction);
+
+ if ($functionStack) {
+ end($functionStack);
+ $key = key($functionStack);
+ $currentFunction = &$functionStack[$key];
+ unset($functionStack[$key]);
+ }
+ }
+ }
+ }
+
+ foreach ($this->traits as &$trait) {
+ foreach ($trait['methods'] as &$method) {
+ if ($method['executableLines'] > 0) {
+ $method['coverage'] = ($method['executedLines'] /
+ $method['executableLines']) * 100;
+ } else {
+ $method['coverage'] = 100;
+ }
+
+ $method['crap'] = $this->crap(
+ $method['ccn'],
+ $method['coverage']
+ );
+
+ $trait['ccn'] += $method['ccn'];
+ }
+
+ if ($trait['executableLines'] > 0) {
+ $trait['coverage'] = ($trait['executedLines'] /
+ $trait['executableLines']) * 100;
+
+ if ($trait['coverage'] == 100) {
+ $this->numTestedClasses++;
+ }
+ } else {
+ $trait['coverage'] = 100;
+ }
+
+ $trait['crap'] = $this->crap(
+ $trait['ccn'],
+ $trait['coverage']
+ );
+ }
+
+ foreach ($this->classes as &$class) {
+ foreach ($class['methods'] as &$method) {
+ if ($method['executableLines'] > 0) {
+ $method['coverage'] = ($method['executedLines'] /
+ $method['executableLines']) * 100;
+ } else {
+ $method['coverage'] = 100;
+ }
+
+ $method['crap'] = $this->crap(
+ $method['ccn'],
+ $method['coverage']
+ );
+
+ $class['ccn'] += $method['ccn'];
+ }
+
+ if ($class['executableLines'] > 0) {
+ $class['coverage'] = ($class['executedLines'] /
+ $class['executableLines']) * 100;
+
+ if ($class['coverage'] == 100) {
+ $this->numTestedClasses++;
+ }
+ } else {
+ $class['coverage'] = 100;
+ }
+
+ $class['crap'] = $this->crap(
+ $class['ccn'],
+ $class['coverage']
+ );
+ }
+ }
+
+ /**
+ * @param \PHP_Token_Stream $tokens
+ */
+ protected function processClasses(\PHP_Token_Stream $tokens)
+ {
+ $classes = $tokens->getClasses();
+ unset($tokens);
+
+ $link = $this->getId() . '.html#';
+
+ foreach ($classes as $className => $class) {
+ $this->classes[$className] = [
+ 'className' => $className,
+ 'methods' => [],
+ 'startLine' => $class['startLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'ccn' => 0,
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'package' => $class['package'],
+ 'link' => $link . $class['startLine']
+ ];
+
+ $this->startLines[$class['startLine']] = &$this->classes[$className];
+ $this->endLines[$class['endLine']] = &$this->classes[$className];
+
+ foreach ($class['methods'] as $methodName => $method) {
+ $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);
+
+ $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName];
+ $this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName];
+ }
+ }
+ }
+
+ /**
+ * @param \PHP_Token_Stream $tokens
+ */
+ protected function processTraits(\PHP_Token_Stream $tokens)
+ {
+ $traits = $tokens->getTraits();
+ unset($tokens);
+
+ $link = $this->getId() . '.html#';
+
+ foreach ($traits as $traitName => $trait) {
+ $this->traits[$traitName] = [
+ 'traitName' => $traitName,
+ 'methods' => [],
+ 'startLine' => $trait['startLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'ccn' => 0,
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'package' => $trait['package'],
+ 'link' => $link . $trait['startLine']
+ ];
+
+ $this->startLines[$trait['startLine']] = &$this->traits[$traitName];
+ $this->endLines[$trait['endLine']] = &$this->traits[$traitName];
+
+ foreach ($trait['methods'] as $methodName => $method) {
+ $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);
+
+ $this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName];
+ $this->endLines[$method['endLine']] = &$this->traits[$traitName]['methods'][$methodName];
+ }
+ }
+ }
+
+ /**
+ * @param \PHP_Token_Stream $tokens
+ */
+ protected function processFunctions(\PHP_Token_Stream $tokens)
+ {
+ $functions = $tokens->getFunctions();
+ unset($tokens);
+
+ $link = $this->getId() . '.html#';
+
+ foreach ($functions as $functionName => $function) {
+ $this->functions[$functionName] = [
+ 'functionName' => $functionName,
+ 'signature' => $function['signature'],
+ 'startLine' => $function['startLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'ccn' => $function['ccn'],
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'link' => $link . $function['startLine']
+ ];
+
+ $this->startLines[$function['startLine']] = &$this->functions[$functionName];
+ $this->endLines[$function['endLine']] = &$this->functions[$functionName];
+ }
+ }
+
+ /**
+ * Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code
+ * based on its cyclomatic complexity and percentage of code coverage.
+ *
+ * @param int $ccn
+ * @param float $coverage
+ *
+ * @return string
+ */
+ protected function crap($ccn, $coverage)
+ {
+ if ($coverage == 0) {
+ return (string) (pow($ccn, 2) + $ccn);
+ }
+
+ if ($coverage >= 95) {
+ return (string) $ccn;
+ }
+
+ return sprintf(
+ '%01.2F',
+ pow($ccn, 2) * pow(1 - $coverage/100, 3) + $ccn
+ );
+ }
+
+ /**
+ * @param string $methodName
+ * @param array $method
+ * @param string $link
+ *
+ * @return array
+ */
+ private function newMethod($methodName, array $method, $link)
+ {
+ return [
+ 'methodName' => $methodName,
+ 'visibility' => $method['visibility'],
+ 'signature' => $method['signature'],
+ 'startLine' => $method['startLine'],
+ 'endLine' => $method['endLine'],
+ 'executableLines' => 0,
+ 'executedLines' => 0,
+ 'ccn' => $method['ccn'],
+ 'coverage' => 0,
+ 'crap' => 0,
+ 'link' => $link . $method['startLine'],
+ ];
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Node;
+
+/**
+ * Recursive iterator for node object graphs.
+ */
+class Iterator implements \RecursiveIterator
+{
+ /**
+ * @var int
+ */
+ private $position;
+
+ /**
+ * @var AbstractNode[]
+ */
+ private $nodes;
+
+ /**
+ * @param Directory $node
+ */
+ public function __construct(Directory $node)
+ {
+ $this->nodes = $node->getChildNodes();
+ }
+
+ /**
+ * Rewinds the Iterator to the first element.
+ */
+ public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ /**
+ * Checks if there is a current element after calls to rewind() or next().
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return $this->position < count($this->nodes);
+ }
+
+ /**
+ * Returns the key of the current element.
+ *
+ * @return int
+ */
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * Returns the current element.
+ *
+ * @return \PHPUnit_Framework_Test
+ */
+ public function current()
+ {
+ return $this->valid() ? $this->nodes[$this->position] : null;
+ }
+
+ /**
+ * Moves forward to next element.
+ */
+ public function next()
+ {
+ $this->position++;
+ }
+
+ /**
+ * Returns the sub iterator for the current element.
+ *
+ * @return Iterator
+ */
+ public function getChildren()
+ {
+ return new self(
+ $this->nodes[$this->position]
+ );
+ }
+
+ /**
+ * Checks whether the current element has children.
+ *
+ * @return bool
+ */
+ public function hasChildren()
+ {
+ return $this->nodes[$this->position] instanceof Directory;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Node\File;
+
+/**
+ * Generates a Clover XML logfile from a code coverage object.
+ */
+class Clover
+{
+ /**
+ * @param CodeCoverage $coverage
+ * @param string $target
+ * @param string $name
+ *
+ * @return string
+ */
+ public function process(CodeCoverage $coverage, $target = null, $name = null)
+ {
+ $xmlDocument = new \DOMDocument('1.0', 'UTF-8');
+ $xmlDocument->formatOutput = true;
+
+ $xmlCoverage = $xmlDocument->createElement('coverage');
+ $xmlCoverage->setAttribute('generated', (int) $_SERVER['REQUEST_TIME']);
+ $xmlDocument->appendChild($xmlCoverage);
+
+ $xmlProject = $xmlDocument->createElement('project');
+ $xmlProject->setAttribute('timestamp', (int) $_SERVER['REQUEST_TIME']);
+
+ if (is_string($name)) {
+ $xmlProject->setAttribute('name', $name);
+ }
+
+ $xmlCoverage->appendChild($xmlProject);
+
+ $packages = [];
+ $report = $coverage->getReport();
+ unset($coverage);
+
+ foreach ($report as $item) {
+ if (!$item instanceof File) {
+ continue;
+ }
+
+ /* @var File $item */
+
+ $xmlFile = $xmlDocument->createElement('file');
+ $xmlFile->setAttribute('name', $item->getPath());
+
+ $classes = $item->getClassesAndTraits();
+ $coverage = $item->getCoverageData();
+ $lines = [];
+ $namespace = 'global';
+
+ foreach ($classes as $className => $class) {
+ $classStatements = 0;
+ $coveredClassStatements = 0;
+ $coveredMethods = 0;
+ $classMethods = 0;
+
+ foreach ($class['methods'] as $methodName => $method) {
+ if ($method['executableLines'] == 0) {
+ continue;
+ }
+
+ $classMethods++;
+ $classStatements += $method['executableLines'];
+ $coveredClassStatements += $method['executedLines'];
+
+ if ($method['coverage'] == 100) {
+ $coveredMethods++;
+ }
+
+ $methodCount = 0;
+
+ foreach (range($method['startLine'], $method['endLine']) as $line) {
+ if (isset($coverage[$line]) && ($coverage[$line] !== null)) {
+ $methodCount = max($methodCount, count($coverage[$line]));
+ }
+ }
+
+ $lines[$method['startLine']] = [
+ 'ccn' => $method['ccn'],
+ 'count' => $methodCount,
+ 'crap' => $method['crap'],
+ 'type' => 'method',
+ 'visibility' => $method['visibility'],
+ 'name' => $methodName
+ ];
+ }
+
+ if (!empty($class['package']['namespace'])) {
+ $namespace = $class['package']['namespace'];
+ }
+
+ $xmlClass = $xmlDocument->createElement('class');
+ $xmlClass->setAttribute('name', $className);
+ $xmlClass->setAttribute('namespace', $namespace);
+
+ if (!empty($class['package']['fullPackage'])) {
+ $xmlClass->setAttribute(
+ 'fullPackage',
+ $class['package']['fullPackage']
+ );
+ }
+
+ if (!empty($class['package']['category'])) {
+ $xmlClass->setAttribute(
+ 'category',
+ $class['package']['category']
+ );
+ }
+
+ if (!empty($class['package']['package'])) {
+ $xmlClass->setAttribute(
+ 'package',
+ $class['package']['package']
+ );
+ }
+
+ if (!empty($class['package']['subpackage'])) {
+ $xmlClass->setAttribute(
+ 'subpackage',
+ $class['package']['subpackage']
+ );
+ }
+
+ $xmlFile->appendChild($xmlClass);
+
+ $xmlMetrics = $xmlDocument->createElement('metrics');
+ $xmlMetrics->setAttribute('complexity', $class['ccn']);
+ $xmlMetrics->setAttribute('methods', $classMethods);
+ $xmlMetrics->setAttribute('coveredmethods', $coveredMethods);
+ $xmlMetrics->setAttribute('conditionals', 0);
+ $xmlMetrics->setAttribute('coveredconditionals', 0);
+ $xmlMetrics->setAttribute('statements', $classStatements);
+ $xmlMetrics->setAttribute('coveredstatements', $coveredClassStatements);
+ $xmlMetrics->setAttribute('elements', $classMethods + $classStatements /* + conditionals */);
+ $xmlMetrics->setAttribute('coveredelements', $coveredMethods + $coveredClassStatements /* + coveredconditionals */);
+ $xmlClass->appendChild($xmlMetrics);
+ }
+
+ foreach ($coverage as $line => $data) {
+ if ($data === null || isset($lines[$line])) {
+ continue;
+ }
+
+ $lines[$line] = [
+ 'count' => count($data), 'type' => 'stmt'
+ ];
+ }
+
+ ksort($lines);
+
+ foreach ($lines as $line => $data) {
+ $xmlLine = $xmlDocument->createElement('line');
+ $xmlLine->setAttribute('num', $line);
+ $xmlLine->setAttribute('type', $data['type']);
+
+ if (isset($data['name'])) {
+ $xmlLine->setAttribute('name', $data['name']);
+ }
+
+ if (isset($data['visibility'])) {
+ $xmlLine->setAttribute('visibility', $data['visibility']);
+ }
+
+ if (isset($data['ccn'])) {
+ $xmlLine->setAttribute('complexity', $data['ccn']);
+ }
+
+ if (isset($data['crap'])) {
+ $xmlLine->setAttribute('crap', $data['crap']);
+ }
+
+ $xmlLine->setAttribute('count', $data['count']);
+ $xmlFile->appendChild($xmlLine);
+ }
+
+ $linesOfCode = $item->getLinesOfCode();
+
+ $xmlMetrics = $xmlDocument->createElement('metrics');
+ $xmlMetrics->setAttribute('loc', $linesOfCode['loc']);
+ $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']);
+ $xmlMetrics->setAttribute('classes', $item->getNumClassesAndTraits());
+ $xmlMetrics->setAttribute('methods', $item->getNumMethods());
+ $xmlMetrics->setAttribute('coveredmethods', $item->getNumTestedMethods());
+ $xmlMetrics->setAttribute('conditionals', 0);
+ $xmlMetrics->setAttribute('coveredconditionals', 0);
+ $xmlMetrics->setAttribute('statements', $item->getNumExecutableLines());
+ $xmlMetrics->setAttribute('coveredstatements', $item->getNumExecutedLines());
+ $xmlMetrics->setAttribute('elements', $item->getNumMethods() + $item->getNumExecutableLines() /* + conditionals */);
+ $xmlMetrics->setAttribute('coveredelements', $item->getNumTestedMethods() + $item->getNumExecutedLines() /* + coveredconditionals */);
+ $xmlFile->appendChild($xmlMetrics);
+
+ if ($namespace == 'global') {
+ $xmlProject->appendChild($xmlFile);
+ } else {
+ if (!isset($packages[$namespace])) {
+ $packages[$namespace] = $xmlDocument->createElement(
+ 'package'
+ );
+
+ $packages[$namespace]->setAttribute('name', $namespace);
+ $xmlProject->appendChild($packages[$namespace]);
+ }
+
+ $packages[$namespace]->appendChild($xmlFile);
+ }
+ }
+
+ $linesOfCode = $report->getLinesOfCode();
+
+ $xmlMetrics = $xmlDocument->createElement('metrics');
+ $xmlMetrics->setAttribute('files', count($report));
+ $xmlMetrics->setAttribute('loc', $linesOfCode['loc']);
+ $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']);
+ $xmlMetrics->setAttribute('classes', $report->getNumClassesAndTraits());
+ $xmlMetrics->setAttribute('methods', $report->getNumMethods());
+ $xmlMetrics->setAttribute('coveredmethods', $report->getNumTestedMethods());
+ $xmlMetrics->setAttribute('conditionals', 0);
+ $xmlMetrics->setAttribute('coveredconditionals', 0);
+ $xmlMetrics->setAttribute('statements', $report->getNumExecutableLines());
+ $xmlMetrics->setAttribute('coveredstatements', $report->getNumExecutedLines());
+ $xmlMetrics->setAttribute('elements', $report->getNumMethods() + $report->getNumExecutableLines() /* + conditionals */);
+ $xmlMetrics->setAttribute('coveredelements', $report->getNumTestedMethods() + $report->getNumExecutedLines() /* + coveredconditionals */);
+ $xmlProject->appendChild($xmlMetrics);
+
+ $buffer = $xmlDocument->saveXML();
+
+ if ($target !== null) {
+ if (!is_dir(dirname($target))) {
+ mkdir(dirname($target), 0777, true);
+ }
+
+ file_put_contents($target, $buffer);
+ }
+
+ return $buffer;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Node\File;
+use SebastianBergmann\CodeCoverage\InvalidArgumentException;
+
+class Crap4j
+{
+ /**
+ * @var int
+ */
+ private $threshold;
+
+ /**
+ * @param int $threshold
+ */
+ public function __construct($threshold = 30)
+ {
+ if (!is_int($threshold)) {
+ throw InvalidArgumentException::create(
+ 1,
+ 'integer'
+ );
+ }
+
+ $this->threshold = $threshold;
+ }
+
+ /**
+ * @param CodeCoverage $coverage
+ * @param string $target
+ * @param string $name
+ *
+ * @return string
+ */
+ public function process(CodeCoverage $coverage, $target = null, $name = null)
+ {
+ $document = new \DOMDocument('1.0', 'UTF-8');
+ $document->formatOutput = true;
+
+ $root = $document->createElement('crap_result');
+ $document->appendChild($root);
+
+ $project = $document->createElement('project', is_string($name) ? $name : '');
+ $root->appendChild($project);
+ $root->appendChild($document->createElement('timestamp', date('Y-m-d H:i:s', (int) $_SERVER['REQUEST_TIME'])));
+
+ $stats = $document->createElement('stats');
+ $methodsNode = $document->createElement('methods');
+
+ $report = $coverage->getReport();
+ unset($coverage);
+
+ $fullMethodCount = 0;
+ $fullCrapMethodCount = 0;
+ $fullCrapLoad = 0;
+ $fullCrap = 0;
+
+ foreach ($report as $item) {
+ $namespace = 'global';
+
+ if (!$item instanceof File) {
+ continue;
+ }
+
+ $file = $document->createElement('file');
+ $file->setAttribute('name', $item->getPath());
+
+ $classes = $item->getClassesAndTraits();
+
+ foreach ($classes as $className => $class) {
+ foreach ($class['methods'] as $methodName => $method) {
+ $crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']);
+
+ $fullCrap += $method['crap'];
+ $fullCrapLoad += $crapLoad;
+ $fullMethodCount++;
+
+ if ($method['crap'] >= $this->threshold) {
+ $fullCrapMethodCount++;
+ }
+
+ $methodNode = $document->createElement('method');
+
+ if (!empty($class['package']['namespace'])) {
+ $namespace = $class['package']['namespace'];
+ }
+
+ $methodNode->appendChild($document->createElement('package', $namespace));
+ $methodNode->appendChild($document->createElement('className', $className));
+ $methodNode->appendChild($document->createElement('methodName', $methodName));
+ $methodNode->appendChild($document->createElement('methodSignature', htmlspecialchars($method['signature'])));
+ $methodNode->appendChild($document->createElement('fullMethod', htmlspecialchars($method['signature'])));
+ $methodNode->appendChild($document->createElement('crap', $this->roundValue($method['crap'])));
+ $methodNode->appendChild($document->createElement('complexity', $method['ccn']));
+ $methodNode->appendChild($document->createElement('coverage', $this->roundValue($method['coverage'])));
+ $methodNode->appendChild($document->createElement('crapLoad', round($crapLoad)));
+
+ $methodsNode->appendChild($methodNode);
+ }
+ }
+ }
+
+ $stats->appendChild($document->createElement('name', 'Method Crap Stats'));
+ $stats->appendChild($document->createElement('methodCount', $fullMethodCount));
+ $stats->appendChild($document->createElement('crapMethodCount', $fullCrapMethodCount));
+ $stats->appendChild($document->createElement('crapLoad', round($fullCrapLoad)));
+ $stats->appendChild($document->createElement('totalCrap', $fullCrap));
+
+ if ($fullMethodCount > 0) {
+ $crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount);
+ } else {
+ $crapMethodPercent = 0;
+ }
+
+ $stats->appendChild($document->createElement('crapMethodPercent', $crapMethodPercent));
+
+ $root->appendChild($stats);
+ $root->appendChild($methodsNode);
+
+ $buffer = $document->saveXML();
+
+ if ($target !== null) {
+ if (!is_dir(dirname($target))) {
+ mkdir(dirname($target), 0777, true);
+ }
+
+ file_put_contents($target, $buffer);
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @param float $crapValue
+ * @param int $cyclomaticComplexity
+ * @param float $coveragePercent
+ *
+ * @return float
+ */
+ private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent)
+ {
+ $crapLoad = 0;
+
+ if ($crapValue >= $this->threshold) {
+ $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100);
+ $crapLoad += $cyclomaticComplexity / $this->threshold;
+ }
+
+ return $crapLoad;
+ }
+
+ /**
+ * @param float $value
+ *
+ * @return float
+ */
+ private function roundValue($value)
+ {
+ return round($value, 2);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Html;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
+use SebastianBergmann\CodeCoverage\RuntimeException;
+
+/**
+ * Generates an HTML report from a code coverage object.
+ */
+class Facade
+{
+ /**
+ * @var string
+ */
+ private $templatePath;
+
+ /**
+ * @var string
+ */
+ private $generator;
+
+ /**
+ * @var int
+ */
+ private $lowUpperBound;
+
+ /**
+ * @var int
+ */
+ private $highLowerBound;
+
+ /**
+ * Constructor.
+ *
+ * @param int $lowUpperBound
+ * @param int $highLowerBound
+ * @param string $generator
+ */
+ public function __construct($lowUpperBound = 50, $highLowerBound = 90, $generator = '')
+ {
+ $this->generator = $generator;
+ $this->highLowerBound = $highLowerBound;
+ $this->lowUpperBound = $lowUpperBound;
+ $this->templatePath = __DIR__ . '/Renderer/Template/';
+ }
+
+ /**
+ * @param CodeCoverage $coverage
+ * @param string $target
+ */
+ public function process(CodeCoverage $coverage, $target)
+ {
+ $target = $this->getDirectory($target);
+ $report = $coverage->getReport();
+ unset($coverage);
+
+ if (!isset($_SERVER['REQUEST_TIME'])) {
+ $_SERVER['REQUEST_TIME'] = time();
+ }
+
+ $date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']);
+
+ $dashboard = new Dashboard(
+ $this->templatePath,
+ $this->generator,
+ $date,
+ $this->lowUpperBound,
+ $this->highLowerBound
+ );
+
+ $directory = new Directory(
+ $this->templatePath,
+ $this->generator,
+ $date,
+ $this->lowUpperBound,
+ $this->highLowerBound
+ );
+
+ $file = new File(
+ $this->templatePath,
+ $this->generator,
+ $date,
+ $this->lowUpperBound,
+ $this->highLowerBound
+ );
+
+ $directory->render($report, $target . 'index.html');
+ $dashboard->render($report, $target . 'dashboard.html');
+
+ foreach ($report as $node) {
+ $id = $node->getId();
+
+ if ($node instanceof DirectoryNode) {
+ if (!file_exists($target . $id)) {
+ mkdir($target . $id, 0777, true);
+ }
+
+ $directory->render($node, $target . $id . '/index.html');
+ $dashboard->render($node, $target . $id . '/dashboard.html');
+ } else {
+ $dir = dirname($target . $id);
+
+ if (!file_exists($dir)) {
+ mkdir($dir, 0777, true);
+ }
+
+ $file->render($node, $target . $id . '.html');
+ }
+ }
+
+ $this->copyFiles($target);
+ }
+
+ /**
+ * @param string $target
+ */
+ private function copyFiles($target)
+ {
+ $dir = $this->getDirectory($target . 'css');
+ copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
+ copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css');
+ copy($this->templatePath . 'css/style.css', $dir . 'style.css');
+
+ $dir = $this->getDirectory($target . 'fonts');
+ copy($this->templatePath . 'fonts/glyphicons-halflings-regular.eot', $dir . 'glyphicons-halflings-regular.eot');
+ copy($this->templatePath . 'fonts/glyphicons-halflings-regular.svg', $dir . 'glyphicons-halflings-regular.svg');
+ copy($this->templatePath . 'fonts/glyphicons-halflings-regular.ttf', $dir . 'glyphicons-halflings-regular.ttf');
+ copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff', $dir . 'glyphicons-halflings-regular.woff');
+ copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff2', $dir . 'glyphicons-halflings-regular.woff2');
+
+ $dir = $this->getDirectory($target . 'js');
+ copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
+ copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
+ copy($this->templatePath . 'js/holder.min.js', $dir . 'holder.min.js');
+ copy($this->templatePath . 'js/html5shiv.min.js', $dir . 'html5shiv.min.js');
+ copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
+ copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
+ copy($this->templatePath . 'js/respond.min.js', $dir . 'respond.min.js');
+ }
+
+ /**
+ * @param string $directory
+ *
+ * @return string
+ *
+ * @throws RuntimeException
+ */
+ private function getDirectory($directory)
+ {
+ if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) {
+ $directory .= DIRECTORY_SEPARATOR;
+ }
+
+ if (is_dir($directory)) {
+ return $directory;
+ }
+
+ if (@mkdir($directory, 0777, true)) {
+ return $directory;
+ }
+
+ throw new RuntimeException(
+ sprintf(
+ 'Directory "%s" does not exist.',
+ $directory
+ )
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Html;
+
+use SebastianBergmann\CodeCoverage\Node\AbstractNode;
+use SebastianBergmann\CodeCoverage\Node\File as FileNode;
+use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
+use SebastianBergmann\Environment\Runtime;
+use SebastianBergmann\Version;
+
+/**
+ * Base class for node renderers.
+ */
+abstract class Renderer
+{
+ /**
+ * @var string
+ */
+ protected $templatePath;
+
+ /**
+ * @var string
+ */
+ protected $generator;
+
+ /**
+ * @var string
+ */
+ protected $date;
+
+ /**
+ * @var int
+ */
+ protected $lowUpperBound;
+
+ /**
+ * @var int
+ */
+ protected $highLowerBound;
+
+ /**
+ * @var string
+ */
+ protected $version;
+
+ /**
+ * Constructor.
+ *
+ * @param string $templatePath
+ * @param string $generator
+ * @param string $date
+ * @param int $lowUpperBound
+ * @param int $highLowerBound
+ */
+ public function __construct($templatePath, $generator, $date, $lowUpperBound, $highLowerBound)
+ {
+ $version = new Version('4.0.8', dirname(dirname(dirname(dirname(__DIR__)))));
+
+ $this->templatePath = $templatePath;
+ $this->generator = $generator;
+ $this->date = $date;
+ $this->lowUpperBound = $lowUpperBound;
+ $this->highLowerBound = $highLowerBound;
+ $this->version = $version->getVersion();
+ }
+
+ /**
+ * @param \Text_Template $template
+ * @param array $data
+ *
+ * @return string
+ */
+ protected function renderItemTemplate(\Text_Template $template, array $data)
+ {
+ $numSeparator = '&nbsp;/&nbsp;';
+
+ if (isset($data['numClasses']) && $data['numClasses'] > 0) {
+ $classesLevel = $this->getColorLevel($data['testedClassesPercent']);
+
+ $classesNumber = $data['numTestedClasses'] . $numSeparator .
+ $data['numClasses'];
+
+ $classesBar = $this->getCoverageBar(
+ $data['testedClassesPercent']
+ );
+ } else {
+ $classesLevel = '';
+ $classesNumber = '0' . $numSeparator . '0';
+ $classesBar = '';
+ $data['testedClassesPercentAsString'] = 'n/a';
+ }
+
+ if ($data['numMethods'] > 0) {
+ $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']);
+
+ $methodsNumber = $data['numTestedMethods'] . $numSeparator .
+ $data['numMethods'];
+
+ $methodsBar = $this->getCoverageBar(
+ $data['testedMethodsPercent']
+ );
+ } else {
+ $methodsLevel = '';
+ $methodsNumber = '0' . $numSeparator . '0';
+ $methodsBar = '';
+ $data['testedMethodsPercentAsString'] = 'n/a';
+ }
+
+ if ($data['numExecutableLines'] > 0) {
+ $linesLevel = $this->getColorLevel($data['linesExecutedPercent']);
+
+ $linesNumber = $data['numExecutedLines'] . $numSeparator .
+ $data['numExecutableLines'];
+
+ $linesBar = $this->getCoverageBar(
+ $data['linesExecutedPercent']
+ );
+ } else {
+ $linesLevel = '';
+ $linesNumber = '0' . $numSeparator . '0';
+ $linesBar = '';
+ $data['linesExecutedPercentAsString'] = 'n/a';
+ }
+
+ $template->setVar(
+ [
+ 'icon' => isset($data['icon']) ? $data['icon'] : '',
+ 'crap' => isset($data['crap']) ? $data['crap'] : '',
+ 'name' => $data['name'],
+ 'lines_bar' => $linesBar,
+ 'lines_executed_percent' => $data['linesExecutedPercentAsString'],
+ 'lines_level' => $linesLevel,
+ 'lines_number' => $linesNumber,
+ 'methods_bar' => $methodsBar,
+ 'methods_tested_percent' => $data['testedMethodsPercentAsString'],
+ 'methods_level' => $methodsLevel,
+ 'methods_number' => $methodsNumber,
+ 'classes_bar' => $classesBar,
+ 'classes_tested_percent' => isset($data['testedClassesPercentAsString']) ? $data['testedClassesPercentAsString'] : '',
+ 'classes_level' => $classesLevel,
+ 'classes_number' => $classesNumber
+ ]
+ );
+
+ return $template->render();
+ }
+
+ /**
+ * @param \Text_Template $template
+ * @param AbstractNode $node
+ */
+ protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node)
+ {
+ $template->setVar(
+ [
+ 'id' => $node->getId(),
+ 'full_path' => $node->getPath(),
+ 'path_to_root' => $this->getPathToRoot($node),
+ 'breadcrumbs' => $this->getBreadcrumbs($node),
+ 'date' => $this->date,
+ 'version' => $this->version,
+ 'runtime' => $this->getRuntimeString(),
+ 'generator' => $this->generator,
+ 'low_upper_bound' => $this->lowUpperBound,
+ 'high_lower_bound' => $this->highLowerBound
+ ]
+ );
+ }
+
+ protected function getBreadcrumbs(AbstractNode $node)
+ {
+ $breadcrumbs = '';
+ $path = $node->getPathAsArray();
+ $pathToRoot = [];
+ $max = count($path);
+
+ if ($node instanceof FileNode) {
+ $max--;
+ }
+
+ for ($i = 0; $i < $max; $i++) {
+ $pathToRoot[] = str_repeat('../', $i);
+ }
+
+ foreach ($path as $step) {
+ if ($step !== $node) {
+ $breadcrumbs .= $this->getInactiveBreadcrumb(
+ $step,
+ array_pop($pathToRoot)
+ );
+ } else {
+ $breadcrumbs .= $this->getActiveBreadcrumb($step);
+ }
+ }
+
+ return $breadcrumbs;
+ }
+
+ protected function getActiveBreadcrumb(AbstractNode $node)
+ {
+ $buffer = sprintf(
+ ' <li class="active">%s</li>' . "\n",
+ $node->getName()
+ );
+
+ if ($node instanceof DirectoryNode) {
+ $buffer .= ' <li>(<a href="dashboard.html">Dashboard</a>)</li>' . "\n";
+ }
+
+ return $buffer;
+ }
+
+ protected function getInactiveBreadcrumb(AbstractNode $node, $pathToRoot)
+ {
+ return sprintf(
+ ' <li><a href="%sindex.html">%s</a></li>' . "\n",
+ $pathToRoot,
+ $node->getName()
+ );
+ }
+
+ protected function getPathToRoot(AbstractNode $node)
+ {
+ $id = $node->getId();
+ $depth = substr_count($id, '/');
+
+ if ($id != 'index' &&
+ $node instanceof DirectoryNode) {
+ $depth++;
+ }
+
+ return str_repeat('../', $depth);
+ }
+
+ protected function getCoverageBar($percent)
+ {
+ $level = $this->getColorLevel($percent);
+
+ $template = new \Text_Template(
+ $this->templatePath . 'coverage_bar.html',
+ '{{',
+ '}}'
+ );
+
+ $template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]);
+
+ return $template->render();
+ }
+
+ /**
+ * @param int $percent
+ *
+ * @return string
+ */
+ protected function getColorLevel($percent)
+ {
+ if ($percent <= $this->lowUpperBound) {
+ return 'danger';
+ } elseif ($percent > $this->lowUpperBound &&
+ $percent < $this->highLowerBound) {
+ return 'warning';
+ } else {
+ return 'success';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ private function getRuntimeString()
+ {
+ $runtime = new Runtime;
+
+ $buffer = sprintf(
+ '<a href="%s" target="_top">%s %s</a>',
+ $runtime->getVendorUrl(),
+ $runtime->getName(),
+ $runtime->getVersion()
+ );
+
+ if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) {
+ $buffer .= sprintf(
+ ' with <a href="https://xdebug.org/">Xdebug %s</a>',
+ phpversion('xdebug')
+ );
+ }
+
+ return $buffer;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Html;
+
+use SebastianBergmann\CodeCoverage\Node\AbstractNode;
+use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
+
+/**
+ * Renders the dashboard for a directory node.
+ */
+class Dashboard extends Renderer
+{
+ /**
+ * @param DirectoryNode $node
+ * @param string $file
+ */
+ public function render(DirectoryNode $node, $file)
+ {
+ $classes = $node->getClassesAndTraits();
+ $template = new \Text_Template(
+ $this->templatePath . 'dashboard.html',
+ '{{',
+ '}}'
+ );
+
+ $this->setCommonTemplateVariables($template, $node);
+
+ $baseLink = $node->getId() . '/';
+ $complexity = $this->complexity($classes, $baseLink);
+ $coverageDistribution = $this->coverageDistribution($classes);
+ $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink);
+ $projectRisks = $this->projectRisks($classes, $baseLink);
+
+ $template->setVar(
+ [
+ 'insufficient_coverage_classes' => $insufficientCoverage['class'],
+ 'insufficient_coverage_methods' => $insufficientCoverage['method'],
+ 'project_risks_classes' => $projectRisks['class'],
+ 'project_risks_methods' => $projectRisks['method'],
+ 'complexity_class' => $complexity['class'],
+ 'complexity_method' => $complexity['method'],
+ 'class_coverage_distribution' => $coverageDistribution['class'],
+ 'method_coverage_distribution' => $coverageDistribution['method']
+ ]
+ );
+
+ $template->renderTo($file);
+ }
+
+ /**
+ * Returns the data for the Class/Method Complexity charts.
+ *
+ * @param array $classes
+ * @param string $baseLink
+ *
+ * @return array
+ */
+ protected function complexity(array $classes, $baseLink)
+ {
+ $result = ['class' => [], 'method' => []];
+
+ foreach ($classes as $className => $class) {
+ foreach ($class['methods'] as $methodName => $method) {
+ if ($className != '*') {
+ $methodName = $className . '::' . $methodName;
+ }
+
+ $result['method'][] = [
+ $method['coverage'],
+ $method['ccn'],
+ sprintf(
+ '<a href="%s">%s</a>',
+ str_replace($baseLink, '', $method['link']),
+ $methodName
+ )
+ ];
+ }
+
+ $result['class'][] = [
+ $class['coverage'],
+ $class['ccn'],
+ sprintf(
+ '<a href="%s">%s</a>',
+ str_replace($baseLink, '', $class['link']),
+ $className
+ )
+ ];
+ }
+
+ return [
+ 'class' => json_encode($result['class']),
+ 'method' => json_encode($result['method'])
+ ];
+ }
+
+ /**
+ * Returns the data for the Class / Method Coverage Distribution chart.
+ *
+ * @param array $classes
+ *
+ * @return array
+ */
+ protected function coverageDistribution(array $classes)
+ {
+ $result = [
+ 'class' => [
+ '0%' => 0,
+ '0-10%' => 0,
+ '10-20%' => 0,
+ '20-30%' => 0,
+ '30-40%' => 0,
+ '40-50%' => 0,
+ '50-60%' => 0,
+ '60-70%' => 0,
+ '70-80%' => 0,
+ '80-90%' => 0,
+ '90-100%' => 0,
+ '100%' => 0
+ ],
+ 'method' => [
+ '0%' => 0,
+ '0-10%' => 0,
+ '10-20%' => 0,
+ '20-30%' => 0,
+ '30-40%' => 0,
+ '40-50%' => 0,
+ '50-60%' => 0,
+ '60-70%' => 0,
+ '70-80%' => 0,
+ '80-90%' => 0,
+ '90-100%' => 0,
+ '100%' => 0
+ ]
+ ];
+
+ foreach ($classes as $class) {
+ foreach ($class['methods'] as $methodName => $method) {
+ if ($method['coverage'] == 0) {
+ $result['method']['0%']++;
+ } elseif ($method['coverage'] == 100) {
+ $result['method']['100%']++;
+ } else {
+ $key = floor($method['coverage'] / 10) * 10;
+ $key = $key . '-' . ($key + 10) . '%';
+ $result['method'][$key]++;
+ }
+ }
+
+ if ($class['coverage'] == 0) {
+ $result['class']['0%']++;
+ } elseif ($class['coverage'] == 100) {
+ $result['class']['100%']++;
+ } else {
+ $key = floor($class['coverage'] / 10) * 10;
+ $key = $key . '-' . ($key + 10) . '%';
+ $result['class'][$key]++;
+ }
+ }
+
+ return [
+ 'class' => json_encode(array_values($result['class'])),
+ 'method' => json_encode(array_values($result['method']))
+ ];
+ }
+
+ /**
+ * Returns the classes / methods with insufficient coverage.
+ *
+ * @param array $classes
+ * @param string $baseLink
+ *
+ * @return array
+ */
+ protected function insufficientCoverage(array $classes, $baseLink)
+ {
+ $leastTestedClasses = [];
+ $leastTestedMethods = [];
+ $result = ['class' => '', 'method' => ''];
+
+ foreach ($classes as $className => $class) {
+ foreach ($class['methods'] as $methodName => $method) {
+ if ($method['coverage'] < $this->highLowerBound) {
+ if ($className != '*') {
+ $key = $className . '::' . $methodName;
+ } else {
+ $key = $methodName;
+ }
+
+ $leastTestedMethods[$key] = $method['coverage'];
+ }
+ }
+
+ if ($class['coverage'] < $this->highLowerBound) {
+ $leastTestedClasses[$className] = $class['coverage'];
+ }
+ }
+
+ asort($leastTestedClasses);
+ asort($leastTestedMethods);
+
+ foreach ($leastTestedClasses as $className => $coverage) {
+ $result['class'] .= sprintf(
+ ' <tr><td><a href="%s">%s</a></td><td class="text-right">%d%%</td></tr>' . "\n",
+ str_replace($baseLink, '', $classes[$className]['link']),
+ $className,
+ $coverage
+ );
+ }
+
+ foreach ($leastTestedMethods as $methodName => $coverage) {
+ list($class, $method) = explode('::', $methodName);
+
+ $result['method'] .= sprintf(
+ ' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d%%</td></tr>' . "\n",
+ str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
+ $methodName,
+ $method,
+ $coverage
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the project risks according to the CRAP index.
+ *
+ * @param array $classes
+ * @param string $baseLink
+ *
+ * @return array
+ */
+ protected function projectRisks(array $classes, $baseLink)
+ {
+ $classRisks = [];
+ $methodRisks = [];
+ $result = ['class' => '', 'method' => ''];
+
+ foreach ($classes as $className => $class) {
+ foreach ($class['methods'] as $methodName => $method) {
+ if ($method['coverage'] < $this->highLowerBound &&
+ $method['ccn'] > 1) {
+ if ($className != '*') {
+ $key = $className . '::' . $methodName;
+ } else {
+ $key = $methodName;
+ }
+
+ $methodRisks[$key] = $method['crap'];
+ }
+ }
+
+ if ($class['coverage'] < $this->highLowerBound &&
+ $class['ccn'] > count($class['methods'])) {
+ $classRisks[$className] = $class['crap'];
+ }
+ }
+
+ arsort($classRisks);
+ arsort($methodRisks);
+
+ foreach ($classRisks as $className => $crap) {
+ $result['class'] .= sprintf(
+ ' <tr><td><a href="%s">%s</a></td><td class="text-right">%d</td></tr>' . "\n",
+ str_replace($baseLink, '', $classes[$className]['link']),
+ $className,
+ $crap
+ );
+ }
+
+ foreach ($methodRisks as $methodName => $crap) {
+ list($class, $method) = explode('::', $methodName);
+
+ $result['method'] .= sprintf(
+ ' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d</td></tr>' . "\n",
+ str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
+ $methodName,
+ $method,
+ $crap
+ );
+ }
+
+ return $result;
+ }
+
+ protected function getActiveBreadcrumb(AbstractNode $node)
+ {
+ return sprintf(
+ ' <li><a href="index.html">%s</a></li>' . "\n" .
+ ' <li class="active">(Dashboard)</li>' . "\n",
+ $node->getName()
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Html;
+
+use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node;
+use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
+
+/**
+ * Renders a directory node.
+ */
+class Directory extends Renderer
+{
+ /**
+ * @param DirectoryNode $node
+ * @param string $file
+ */
+ public function render(DirectoryNode $node, $file)
+ {
+ $template = new \Text_Template($this->templatePath . 'directory.html', '{{', '}}');
+
+ $this->setCommonTemplateVariables($template, $node);
+
+ $items = $this->renderItem($node, true);
+
+ foreach ($node->getDirectories() as $item) {
+ $items .= $this->renderItem($item);
+ }
+
+ foreach ($node->getFiles() as $item) {
+ $items .= $this->renderItem($item);
+ }
+
+ $template->setVar(
+ [
+ 'id' => $node->getId(),
+ 'items' => $items
+ ]
+ );
+
+ $template->renderTo($file);
+ }
+
+ /**
+ * @param Node $node
+ * @param bool $total
+ *
+ * @return string
+ */
+ protected function renderItem(Node $node, $total = false)
+ {
+ $data = [
+ 'numClasses' => $node->getNumClassesAndTraits(),
+ 'numTestedClasses' => $node->getNumTestedClassesAndTraits(),
+ 'numMethods' => $node->getNumMethods(),
+ 'numTestedMethods' => $node->getNumTestedMethods(),
+ 'linesExecutedPercent' => $node->getLineExecutedPercent(false),
+ 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
+ 'numExecutedLines' => $node->getNumExecutedLines(),
+ 'numExecutableLines' => $node->getNumExecutableLines(),
+ 'testedMethodsPercent' => $node->getTestedMethodsPercent(false),
+ 'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(),
+ 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false),
+ 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent()
+ ];
+
+ if ($total) {
+ $data['name'] = 'Total';
+ } else {
+ if ($node instanceof DirectoryNode) {
+ $data['name'] = sprintf(
+ '<a href="%s/index.html">%s</a>',
+ $node->getName(),
+ $node->getName()
+ );
+
+ $data['icon'] = '<span class="glyphicon glyphicon-folder-open"></span> ';
+ } else {
+ $data['name'] = sprintf(
+ '<a href="%s.html">%s</a>',
+ $node->getName(),
+ $node->getName()
+ );
+
+ $data['icon'] = '<span class="glyphicon glyphicon-file"></span> ';
+ }
+ }
+
+ return $this->renderItemTemplate(
+ new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'),
+ $data
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Html;
+
+use SebastianBergmann\CodeCoverage\Node\File as FileNode;
+use SebastianBergmann\CodeCoverage\Util;
+
+/**
+ * Renders a file node.
+ */
+class File extends Renderer
+{
+ /**
+ * @var int
+ */
+ private $htmlspecialcharsFlags;
+
+ /**
+ * Constructor.
+ *
+ * @param string $templatePath
+ * @param string $generator
+ * @param string $date
+ * @param int $lowUpperBound
+ * @param int $highLowerBound
+ */
+ public function __construct($templatePath, $generator, $date, $lowUpperBound, $highLowerBound)
+ {
+ parent::__construct(
+ $templatePath,
+ $generator,
+ $date,
+ $lowUpperBound,
+ $highLowerBound
+ );
+
+ $this->htmlspecialcharsFlags = ENT_COMPAT;
+
+ $this->htmlspecialcharsFlags = $this->htmlspecialcharsFlags | ENT_HTML401 | ENT_SUBSTITUTE;
+ }
+
+ /**
+ * @param FileNode $node
+ * @param string $file
+ */
+ public function render(FileNode $node, $file)
+ {
+ $template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}');
+
+ $template->setVar(
+ [
+ 'items' => $this->renderItems($node),
+ 'lines' => $this->renderSource($node)
+ ]
+ );
+
+ $this->setCommonTemplateVariables($template, $node);
+
+ $template->renderTo($file);
+ }
+
+ /**
+ * @param FileNode $node
+ *
+ * @return string
+ */
+ protected function renderItems(FileNode $node)
+ {
+ $template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}');
+
+ $methodItemTemplate = new \Text_Template(
+ $this->templatePath . 'method_item.html',
+ '{{',
+ '}}'
+ );
+
+ $items = $this->renderItemTemplate(
+ $template,
+ [
+ 'name' => 'Total',
+ 'numClasses' => $node->getNumClassesAndTraits(),
+ 'numTestedClasses' => $node->getNumTestedClassesAndTraits(),
+ 'numMethods' => $node->getNumMethods(),
+ 'numTestedMethods' => $node->getNumTestedMethods(),
+ 'linesExecutedPercent' => $node->getLineExecutedPercent(false),
+ 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
+ 'numExecutedLines' => $node->getNumExecutedLines(),
+ 'numExecutableLines' => $node->getNumExecutableLines(),
+ 'testedMethodsPercent' => $node->getTestedMethodsPercent(false),
+ 'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(),
+ 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false),
+ 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(),
+ 'crap' => '<abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr>'
+ ]
+ );
+
+ $items .= $this->renderFunctionItems(
+ $node->getFunctions(),
+ $methodItemTemplate
+ );
+
+ $items .= $this->renderTraitOrClassItems(
+ $node->getTraits(),
+ $template,
+ $methodItemTemplate
+ );
+
+ $items .= $this->renderTraitOrClassItems(
+ $node->getClasses(),
+ $template,
+ $methodItemTemplate
+ );
+
+ return $items;
+ }
+
+ /**
+ * @param array $items
+ * @param \Text_Template $template
+ * @param \Text_Template $methodItemTemplate
+ *
+ * @return string
+ */
+ protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate)
+ {
+ if (empty($items)) {
+ return '';
+ }
+
+ $buffer = '';
+
+ foreach ($items as $name => $item) {
+ $numMethods = count($item['methods']);
+ $numTestedMethods = 0;
+
+ foreach ($item['methods'] as $method) {
+ if ($method['executedLines'] == $method['executableLines']) {
+ $numTestedMethods++;
+ }
+ }
+
+ if ($item['executableLines'] > 0) {
+ $numClasses = 1;
+ $numTestedClasses = $numTestedMethods == $numMethods ? 1 : 0;
+ $linesExecutedPercentAsString = Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ true
+ );
+ } else {
+ $numClasses = 'n/a';
+ $numTestedClasses = 'n/a';
+ $linesExecutedPercentAsString = 'n/a';
+ }
+
+ $buffer .= $this->renderItemTemplate(
+ $template,
+ [
+ 'name' => $name,
+ 'numClasses' => $numClasses,
+ 'numTestedClasses' => $numTestedClasses,
+ 'numMethods' => $numMethods,
+ 'numTestedMethods' => $numTestedMethods,
+ 'linesExecutedPercent' => Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ false
+ ),
+ 'linesExecutedPercentAsString' => $linesExecutedPercentAsString,
+ 'numExecutedLines' => $item['executedLines'],
+ 'numExecutableLines' => $item['executableLines'],
+ 'testedMethodsPercent' => Util::percent(
+ $numTestedMethods,
+ $numMethods,
+ false
+ ),
+ 'testedMethodsPercentAsString' => Util::percent(
+ $numTestedMethods,
+ $numMethods,
+ true
+ ),
+ 'testedClassesPercent' => Util::percent(
+ $numTestedMethods == $numMethods ? 1 : 0,
+ 1,
+ false
+ ),
+ 'testedClassesPercentAsString' => Util::percent(
+ $numTestedMethods == $numMethods ? 1 : 0,
+ 1,
+ true
+ ),
+ 'crap' => $item['crap']
+ ]
+ );
+
+ foreach ($item['methods'] as $method) {
+ $buffer .= $this->renderFunctionOrMethodItem(
+ $methodItemTemplate,
+ $method,
+ '&nbsp;'
+ );
+ }
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @param array $functions
+ * @param \Text_Template $template
+ *
+ * @return string
+ */
+ protected function renderFunctionItems(array $functions, \Text_Template $template)
+ {
+ if (empty($functions)) {
+ return '';
+ }
+
+ $buffer = '';
+
+ foreach ($functions as $function) {
+ $buffer .= $this->renderFunctionOrMethodItem(
+ $template,
+ $function
+ );
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @param \Text_Template $template
+ *
+ * @return string
+ */
+ protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, $indent = '')
+ {
+ $numTestedItems = $item['executedLines'] == $item['executableLines'] ? 1 : 0;
+
+ return $this->renderItemTemplate(
+ $template,
+ [
+ 'name' => sprintf(
+ '%s<a href="#%d"><abbr title="%s">%s</abbr></a>',
+ $indent,
+ $item['startLine'],
+ htmlspecialchars($item['signature']),
+ isset($item['functionName']) ? $item['functionName'] : $item['methodName']
+ ),
+ 'numMethods' => 1,
+ 'numTestedMethods' => $numTestedItems,
+ 'linesExecutedPercent' => Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ false
+ ),
+ 'linesExecutedPercentAsString' => Util::percent(
+ $item['executedLines'],
+ $item['executableLines'],
+ true
+ ),
+ 'numExecutedLines' => $item['executedLines'],
+ 'numExecutableLines' => $item['executableLines'],
+ 'testedMethodsPercent' => Util::percent(
+ $numTestedItems,
+ 1,
+ false
+ ),
+ 'testedMethodsPercentAsString' => Util::percent(
+ $numTestedItems,
+ 1,
+ true
+ ),
+ 'crap' => $item['crap']
+ ]
+ );
+ }
+
+ /**
+ * @param FileNode $node
+ *
+ * @return string
+ */
+ protected function renderSource(FileNode $node)
+ {
+ $coverageData = $node->getCoverageData();
+ $testData = $node->getTestData();
+ $codeLines = $this->loadFile($node->getPath());
+ $lines = '';
+ $i = 1;
+
+ foreach ($codeLines as $line) {
+ $trClass = '';
+ $popoverContent = '';
+ $popoverTitle = '';
+
+ if (array_key_exists($i, $coverageData)) {
+ $numTests = count($coverageData[$i]);
+
+ if ($coverageData[$i] === null) {
+ $trClass = ' class="warning"';
+ } elseif ($numTests == 0) {
+ $trClass = ' class="danger"';
+ } else {
+ $lineCss = 'covered-by-large-tests';
+ $popoverContent = '<ul>';
+
+ if ($numTests > 1) {
+ $popoverTitle = $numTests . ' tests cover line ' . $i;
+ } else {
+ $popoverTitle = '1 test covers line ' . $i;
+ }
+
+ foreach ($coverageData[$i] as $test) {
+ if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') {
+ $lineCss = 'covered-by-medium-tests';
+ } elseif ($testData[$test]['size'] == 'small') {
+ $lineCss = 'covered-by-small-tests';
+ }
+
+ switch ($testData[$test]['status']) {
+ case 0:
+ switch ($testData[$test]['size']) {
+ case 'small':
+ $testCSS = ' class="covered-by-small-tests"';
+ break;
+
+ case 'medium':
+ $testCSS = ' class="covered-by-medium-tests"';
+ break;
+
+ default:
+ $testCSS = ' class="covered-by-large-tests"';
+ break;
+ }
+ break;
+
+ case 1:
+ case 2:
+ $testCSS = ' class="warning"';
+ break;
+
+ case 3:
+ $testCSS = ' class="danger"';
+ break;
+
+ case 4:
+ $testCSS = ' class="danger"';
+ break;
+
+ default:
+ $testCSS = '';
+ }
+
+ $popoverContent .= sprintf(
+ '<li%s>%s</li>',
+ $testCSS,
+ htmlspecialchars($test)
+ );
+ }
+
+ $popoverContent .= '</ul>';
+ $trClass = ' class="' . $lineCss . ' popin"';
+ }
+ }
+
+ if (!empty($popoverTitle)) {
+ $popover = sprintf(
+ ' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"',
+ $popoverTitle,
+ htmlspecialchars($popoverContent)
+ );
+ } else {
+ $popover = '';
+ }
+
+ $lines .= sprintf(
+ ' <tr%s%s><td><div align="right"><a name="%d"></a><a href="#%d">%d</a></div></td><td class="codeLine">%s</td></tr>' . "\n",
+ $trClass,
+ $popover,
+ $i,
+ $i,
+ $i,
+ $line
+ );
+
+ $i++;
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return array
+ */
+ protected function loadFile($file)
+ {
+ $buffer = file_get_contents($file);
+ $tokens = token_get_all($buffer);
+ $result = [''];
+ $i = 0;
+ $stringFlag = false;
+ $fileEndsWithNewLine = substr($buffer, -1) == "\n";
+
+ unset($buffer);
+
+ foreach ($tokens as $j => $token) {
+ if (is_string($token)) {
+ if ($token === '"' && $tokens[$j - 1] !== '\\') {
+ $result[$i] .= sprintf(
+ '<span class="string">%s</span>',
+ htmlspecialchars($token)
+ );
+
+ $stringFlag = !$stringFlag;
+ } else {
+ $result[$i] .= sprintf(
+ '<span class="keyword">%s</span>',
+ htmlspecialchars($token)
+ );
+ }
+
+ continue;
+ }
+
+ list($token, $value) = $token;
+
+ $value = str_replace(
+ ["\t", ' '],
+ ['&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'],
+ htmlspecialchars($value, $this->htmlspecialcharsFlags)
+ );
+
+ if ($value === "\n") {
+ $result[++$i] = '';
+ } else {
+ $lines = explode("\n", $value);
+
+ foreach ($lines as $jj => $line) {
+ $line = trim($line);
+
+ if ($line !== '') {
+ if ($stringFlag) {
+ $colour = 'string';
+ } else {
+ switch ($token) {
+ case T_INLINE_HTML:
+ $colour = 'html';
+ break;
+
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ $colour = 'comment';
+ break;
+
+ case T_ABSTRACT:
+ case T_ARRAY:
+ case T_AS:
+ case T_BREAK:
+ case T_CALLABLE:
+ case T_CASE:
+ case T_CATCH:
+ case T_CLASS:
+ case T_CLONE:
+ case T_CONTINUE:
+ case T_DEFAULT:
+ case T_ECHO:
+ case T_ELSE:
+ case T_ELSEIF:
+ case T_EMPTY:
+ case T_ENDDECLARE:
+ case T_ENDFOR:
+ case T_ENDFOREACH:
+ case T_ENDIF:
+ case T_ENDSWITCH:
+ case T_ENDWHILE:
+ case T_EXIT:
+ case T_EXTENDS:
+ case T_FINAL:
+ case T_FINALLY:
+ case T_FOREACH:
+ case T_FUNCTION:
+ case T_GLOBAL:
+ case T_IF:
+ case T_IMPLEMENTS:
+ case T_INCLUDE:
+ case T_INCLUDE_ONCE:
+ case T_INSTANCEOF:
+ case T_INSTEADOF:
+ case T_INTERFACE:
+ case T_ISSET:
+ case T_LOGICAL_AND:
+ case T_LOGICAL_OR:
+ case T_LOGICAL_XOR:
+ case T_NAMESPACE:
+ case T_NEW:
+ case T_PRIVATE:
+ case T_PROTECTED:
+ case T_PUBLIC:
+ case T_REQUIRE:
+ case T_REQUIRE_ONCE:
+ case T_RETURN:
+ case T_STATIC:
+ case T_THROW:
+ case T_TRAIT:
+ case T_TRY:
+ case T_UNSET:
+ case T_USE:
+ case T_VAR:
+ case T_WHILE:
+ case T_YIELD:
+ $colour = 'keyword';
+ break;
+
+ default:
+ $colour = 'default';
+ }
+ }
+
+ $result[$i] .= sprintf(
+ '<span class="%s">%s</span>',
+ $colour,
+ $line
+ );
+ }
+
+ if (isset($lines[$jj + 1])) {
+ $result[++$i] = '';
+ }
+ }
+ }
+ }
+
+ if ($fileEndsWithNewLine) {
+ unset($result[count($result)-1]);
+ }
+
+ return $result;
+ }
+}
+ <div class="progress">
+ <div class="progress-bar progress-bar-{{level}}" role="progressbar" aria-valuenow="{{percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{percent}}%">
+ <span class="sr-only">{{percent}}% covered ({{level}})</span>
+ </div>
+ </div>
+/*!
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
+ * Copyright 2011-2016 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
+/*# sourceMappingURL=bootstrap.min.css.map */.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc}body {
+ padding-top: 10px;
+}
+
+.popover {
+ max-width: none;
+}
+
+.glyphicon {
+ margin-right:.25em;
+}
+
+.table-bordered>thead>tr>td {
+ border-bottom-width: 1px;
+}
+
+.table tbody>tr>td, .table thead>tr>td {
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+
+.table-condensed tbody>tr>td {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.table .progress {
+ margin-bottom: inherit;
+}
+
+.table-borderless th, .table-borderless td {
+ border: 0 !important;
+}
+
+.table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success {
+ background-color: #dff0d8;
+}
+
+.table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests {
+ background-color: #c3e3b5;
+}
+
+.table tbody tr.covered-by-small-tests, li.covered-by-small-tests {
+ background-color: #99cb84;
+}
+
+.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger {
+ background-color: #f2dede;
+}
+
+.table tbody td.warning, li.warning, span.warning {
+ background-color: #fcf8e3;
+}
+
+.table tbody td.info {
+ background-color: #d9edf7;
+}
+
+td.big {
+ width: 117px;
+}
+
+td.small {
+}
+
+td.codeLine {
+ font-family: monospace;
+ white-space: pre;
+}
+
+td span.comment {
+ color: #888a85;
+}
+
+td span.default {
+ color: #2e3436;
+}
+
+td span.html {
+ color: #888a85;
+}
+
+td span.keyword {
+ color: #2e3436;
+ font-weight: bold;
+}
+
+pre span.string {
+ color: #2e3436;
+}
+
+span.success, span.warning, span.danger {
+ margin-right: 2px;
+ padding-left: 10px;
+ padding-right: 10px;
+ text-align: center;
+}
+
+#classCoverageDistribution, #classComplexity {
+ height: 200px;
+ width: 475px;
+}
+
+#toplink {
+ position: fixed;
+ left: 5px;
+ bottom: 5px;
+ outline: 0;
+}
+
+svg text {
+ font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ color: #666;
+ fill: #666;
+}
+
+.scrollbox {
+ height:245px;
+ overflow-x:hidden;
+ overflow-y:scroll;
+}
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>Dashboard for {{full_path}}</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link href="{{path_to_root}}css/bootstrap.min.css" rel="stylesheet">
+ <link href="{{path_to_root}}css/nv.d3.min.css" rel="stylesheet">
+ <link href="{{path_to_root}}css/style.css" rel="stylesheet">
+ <!--[if lt IE 9]>
+ <script src="{{path_to_root}}js/html5shiv.min.js"></script>
+ <script src="{{path_to_root}}js/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+ <header>
+ <div class="container">
+ <div class="row">
+ <div class="col-md-12">
+ <ol class="breadcrumb">
+{{breadcrumbs}}
+ </ol>
+ </div>
+ </div>
+ </div>
+ </header>
+ <div class="container">
+ <div class="row">
+ <div class="col-md-12">
+ <h2>Classes</h2>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-6">
+ <h3>Coverage Distribution</h3>
+ <div id="classCoverageDistribution" style="height: 300px;">
+ <svg></svg>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <h3>Complexity</h3>
+ <div id="classComplexity" style="height: 300px;">
+ <svg></svg>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-6">
+ <h3>Insufficient Coverage</h3>
+ <div class="scrollbox">
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Class</th>
+ <th class="text-right">Coverage</th>
+ </tr>
+ </thead>
+ <tbody>
+{{insufficient_coverage_classes}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <h3>Project Risks</h3>
+ <div class="scrollbox">
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Class</th>
+ <th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
+ </tr>
+ </thead>
+ <tbody>
+{{project_risks_classes}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-12">
+ <h2>Methods</h2>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-6">
+ <h3>Coverage Distribution</h3>
+ <div id="methodCoverageDistribution" style="height: 300px;">
+ <svg></svg>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <h3>Complexity</h3>
+ <div id="methodComplexity" style="height: 300px;">
+ <svg></svg>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-6">
+ <h3>Insufficient Coverage</h3>
+ <div class="scrollbox">
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Method</th>
+ <th class="text-right">Coverage</th>
+ </tr>
+ </thead>
+ <tbody>
+{{insufficient_coverage_methods}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <h3>Project Risks</h3>
+ <div class="scrollbox">
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Method</th>
+ <th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
+ </tr>
+ </thead>
+ <tbody>
+{{project_risks_methods}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <footer>
+ <hr/>
+ <p>
+ <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
+ </p>
+ </footer>
+ </div>
+ <script src="{{path_to_root}}js/jquery.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/bootstrap.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/holder.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/d3.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/nv.d3.min.js" type="text/javascript"></script>
+ <script type="text/javascript">
+$(document).ready(function() {
+ nv.addGraph(function() {
+ var chart = nv.models.multiBarChart();
+ chart.tooltips(false)
+ .showControls(false)
+ .showLegend(false)
+ .reduceXTicks(false)
+ .staggerLabels(true)
+ .yAxis.tickFormat(d3.format('d'));
+
+ d3.select('#classCoverageDistribution svg')
+ .datum(getCoverageDistributionData({{class_coverage_distribution}}, "Class Coverage"))
+ .transition().duration(500).call(chart);
+
+ nv.utils.windowResize(chart.update);
+
+ return chart;
+ });
+
+ nv.addGraph(function() {
+ var chart = nv.models.multiBarChart();
+ chart.tooltips(false)
+ .showControls(false)
+ .showLegend(false)
+ .reduceXTicks(false)
+ .staggerLabels(true)
+ .yAxis.tickFormat(d3.format('d'));
+
+ d3.select('#methodCoverageDistribution svg')
+ .datum(getCoverageDistributionData({{method_coverage_distribution}}, "Method Coverage"))
+ .transition().duration(500).call(chart);
+
+ nv.utils.windowResize(chart.update);
+
+ return chart;
+ });
+
+ function getCoverageDistributionData(data, label) {
+ var labels = [
+ '0%',
+ '0-10%',
+ '10-20%',
+ '20-30%',
+ '30-40%',
+ '40-50%',
+ '50-60%',
+ '60-70%',
+ '70-80%',
+ '80-90%',
+ '90-100%',
+ '100%'
+ ];
+ var values = [];
+ $.each(labels, function(key) {
+ values.push({x: labels[key], y: data[key]});
+ });
+
+ return [
+ {
+ key: label,
+ values: values,
+ color: "#4572A7"
+ }
+ ];
+ }
+ nv.addGraph(function() {
+ var chart = nv.models.scatterChart()
+ .showDistX(true)
+ .showDistY(true)
+ .showLegend(false)
+ .forceX([0, 100]);
+ chart.tooltipContent(function(graph) {
+ return '<p>' + graph.point.class + '</p>';
+ });
+
+ chart.xAxis.axisLabel('Code Coverage (in percent)');
+ chart.yAxis.axisLabel('Cyclomatic Complexity');
+
+ d3.select('#classComplexity svg')
+ .datum(getComplexityData({{complexity_class}}, 'Class Complexity'))
+ .transition()
+ .duration(500)
+ .call(chart);
+
+ nv.utils.windowResize(chart.update);
+
+ return chart;
+ });
+
+ nv.addGraph(function() {
+ var chart = nv.models.scatterChart()
+ .showDistX(true)
+ .showDistY(true)
+ .showLegend(false)
+ .forceX([0, 100]);
+ chart.tooltipContent(function(graph) {
+ return '<p>' + graph.point.class + '</p>';
+ });
+
+ chart.xAxis.axisLabel('Code Coverage (in percent)');
+ chart.yAxis.axisLabel('Method Complexity');
+
+ d3.select('#methodComplexity svg')
+ .datum(getComplexityData({{complexity_method}}, 'Method Complexity'))
+ .transition()
+ .duration(500)
+ .call(chart);
+
+ nv.utils.windowResize(chart.update);
+
+ return chart;
+ });
+
+ function getComplexityData(data, label) {
+ var values = [];
+ $.each(data, function(key) {
+ var value = Math.round(data[key][0]*100) / 100;
+ values.push({
+ x: value,
+ y: data[key][1],
+ class: data[key][2],
+ size: 0.05,
+ shape: 'diamond'
+ });
+ });
+
+ return [
+ {
+ key: label,
+ values: values,
+ color: "#4572A7"
+ }
+ ];
+ }
+});
+ </script>
+ </body>
+</html>
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>Code Coverage for {{full_path}}</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link href="{{path_to_root}}css/bootstrap.min.css" rel="stylesheet">
+ <link href="{{path_to_root}}css/style.css" rel="stylesheet">
+ <!--[if lt IE 9]>
+ <script src="{{path_to_root}}js/html5shiv.min.js"></script>
+ <script src="{{path_to_root}}js/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+ <header>
+ <div class="container">
+ <div class="row">
+ <div class="col-md-12">
+ <ol class="breadcrumb">
+{{breadcrumbs}}
+ </ol>
+ </div>
+ </div>
+ </div>
+ </header>
+ <div class="container">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <td>&nbsp;</td>
+ <td colspan="9"><div align="center"><strong>Code Coverage</strong></div></td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td colspan="3"><div align="center"><strong>Lines</strong></div></td>
+ <td colspan="3"><div align="center"><strong>Functions and Methods</strong></div></td>
+ <td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
+ </tr>
+ </thead>
+ <tbody>
+{{items}}
+ </tbody>
+ </table>
+ <footer>
+ <hr/>
+ <h4>Legend</h4>
+ <p>
+ <span class="danger"><strong>Low</strong>: 0% to {{low_upper_bound}}%</span>
+ <span class="warning"><strong>Medium</strong>: {{low_upper_bound}}% to {{high_lower_bound}}%</span>
+ <span class="success"><strong>High</strong>: {{high_lower_bound}}% to 100%</span>
+ </p>
+ <p>
+ <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
+ </p>
+ </footer>
+ </div>
+ <script src="{{path_to_root}}js/jquery.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/bootstrap.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/holder.min.js" type="text/javascript"></script>
+ </body>
+</html>
+ <tr>
+ <td class="{{lines_level}}">{{icon}}{{name}}</td>
+ <td class="{{lines_level}} big">{{lines_bar}}</td>
+ <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
+ <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
+ <td class="{{methods_level}} big">{{methods_bar}}</td>
+ <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
+ <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
+ <td class="{{classes_level}} big">{{classes_bar}}</td>
+ <td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
+ <td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
+ </tr>
+
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>Code Coverage for {{full_path}}</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link href="{{path_to_root}}css/bootstrap.min.css" rel="stylesheet">
+ <link href="{{path_to_root}}css/style.css" rel="stylesheet">
+ <!--[if lt IE 9]>
+ <script src="{{path_to_root}}js/html5shiv.min.js"></script>
+ <script src="{{path_to_root}}js/respond.min.js"></script>
+ <![endif]-->
+ </head>
+ <body>
+ <header>
+ <div class="container">
+ <div class="row">
+ <div class="col-md-12">
+ <ol class="breadcrumb">
+{{breadcrumbs}}
+ </ol>
+ </div>
+ </div>
+ </div>
+ </header>
+ <div class="container">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <td>&nbsp;</td>
+ <td colspan="10"><div align="center"><strong>Code Coverage</strong></div></td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
+ <td colspan="4"><div align="center"><strong>Functions and Methods</strong></div></td>
+ <td colspan="3"><div align="center"><strong>Lines</strong></div></td>
+ </tr>
+ </thead>
+ <tbody>
+{{items}}
+ </tbody>
+ </table>
+ <table id="code" class="table table-borderless table-condensed">
+ <tbody>
+{{lines}}
+ </tbody>
+ </table>
+ <footer>
+ <hr/>
+ <h4>Legend</h4>
+ <p>
+ <span class="success"><strong>Executed</strong></span>
+ <span class="danger"><strong>Not Executed</strong></span>
+ <span class="warning"><strong>Dead Code</strong></span>
+ </p>
+ <p>
+ <small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
+ </p>
+ <a title="Back to the top" id="toplink" href="#"><span class="glyphicon glyphicon-arrow-up"></span></a>
+ </footer>
+ </div>
+ <script src="{{path_to_root}}js/jquery.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/bootstrap.min.js" type="text/javascript"></script>
+ <script src="{{path_to_root}}js/holder.min.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ $(function() {
+ var $window = $(window)
+ , $top_link = $('#toplink')
+ , $body = $('body, html')
+ , offset = $('#code').offset().top;
+
+ $top_link.hide().click(function(event) {
+ event.preventDefault();
+ $body.animate({scrollTop:0}, 800);
+ });
+
+ $window.scroll(function() {
+ if($window.scrollTop() > offset) {
+ $top_link.fadeIn();
+ } else {
+ $top_link.fadeOut();
+ }
+ }).scroll();
+
+ $('.popin').popover({trigger: 'hover'});
+ });
+ </script>
+ </body>
+</html>
+ <tr>
+ <td class="{{classes_level}}">{{name}}</td>
+ <td class="{{classes_level}} big">{{classes_bar}}</td>
+ <td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
+ <td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
+ <td class="{{methods_level}} big">{{methods_bar}}</td>
+ <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
+ <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
+ <td class="{{methods_level}} small">{{crap}}</td>
+ <td class="{{lines_level}} big">{{lines_bar}}</td>
+ <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
+ <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
+ </tr>
+
+N
+U*Ҕz
+miO1nE. hx!aC
+XTV‹ R%|IHP5"bN=r/_R_ %҄uzҘ52ġP)F7SqF{nia@Ds;}9⬥?ź R{Tk;޵ǜU\NZQ-^s7f 0S3A _n`W7Ppi!g/_pZ-=ץ~WZ#/4 KF
+$(pQAA܉A@'$ hp0V0 `se$4$"t2=f4A{Tk0|rH`L&sh]A<`R'!1N;_t3# V *veF`E O${)W=p:F`22ړC^.ćG<<?~z>.pNe2ִ+Ysl:˼ ܫu5tu^86ȄTmyQ%u~%~1rҘawߚ^_ZZa0!N`. uqYB\ᨀ[e:@J'Eہ,3ubj@pfeW9( ޅ=lG7gj SM609OˑlBa݁ <Bՙ(VRApf^+g9qMt]تpEr@]@VkV
+ud^X R@?EY2]#Ǽ4JK'dPC|mmn#$+48u'e&[n[L%{BCDL:^!bƙ:&g3-3ub iLZڂWFSId6.k5Pl77UzT:NN.")['|U"AIvwptdk9嫫9
+)qCfO 02Y293Nfp\
+hRf
+9M4c EotjVGD)l8,\w !
+ RܐP[z:XH#e s>?EWO>@I$|si
+ES)0A?9ab,@K̩o&Q% ϞLu+
++H|Ɛ?NK4CnPt 'OT.j5Ĵ8vw֜I&+`yScaO[#gQd[KI矗`ČLP # )27aTi@c\ސ 0nCpߖ運4͵x*RzYbT[\kUvHʈqp঄IIŗ) bB XPNtz 2 I== ;}bqjiކa#" >11Ap1POOuxQ
+Fϲ(h݄O'MDxLK$ȵh& 
+*ؗ5u2$f3K <PLrcI)^da>
+%ѳb (@,2f,~"7R;E;HX(42Z'
+jC.4Ya_{eA2=r+
+}YPJeGn%x1/}RgHa ^3- 5
+|qSaWK{ 1al`I1 Qf_yyCZ)L3X] W6@DMT<.uGK8DsбWr\7Z\V"ISd>CUjeD 3MtWcPӉ6#3QnቩJ\7#磱`؀K lV6 &T ~l. <BP
+*!zRZeљٷT#CLHW)DpYU#51{WJ4^f̼Zy6ӑT2d4H=BҊ}&݃,aPçv+:2~*0dɓփd ‚!"A+r
+"2}Rrz,j^q\ݖ#p+`fl:k t5EOaIJP @psEj14;6/aH.ӰT
+(]&ɜ(ՙ2:0 oΏхPKiBH4UX,[$
+0mXش f50VR 8%ާDtUs`-BPzPsvI8z-t1DiB
+"˶YTJ .?07jLN[2tĮ̎
+9*x0Bj)mHAhyЏhMm&4Ŋ4 gV&tYOCS0Yd7MvNj)wA(o "͢[
+E`7ezď-Q]6+Bca@^I:һ=sSnc 6 OB4LGpBq/<zAC A~x0 6rihhIطON,:ok/{H,zЂgfȻz΀5FTrn/t``l*H6jTtG/x@P@(Ip e!`wv,:A쑜N 4}09zqC$r
+%mL
+!I$X<T+3dq
+DMt2|fEV([]NdbD3Sp'RGmK<Tٰ
+FagAٓЉt)le
+; $9{C
+U%n3aƬJ.>t0~ePz]Ug Н=_?.j#+`li BM5 őGp7a
+֒%Y[UG9@\bDY{{ED0
+$Q+FvC`ݨ3Q E\uC9![$l 6DoDgG*+X!%#Cq ?8ZUB)U@opgީZq89|uccAќW;@">Ph_9}.6V/O:3}ZS {:~ykcO6;OB=bV. Rk
+o ^GV= }oI"+
+]wFzϷ`<30h3]Rf859s`KM8
+XUq<\ZOssM&j&  .%PBL~^Gˈ3pD:Z<\ǠiW̆"(:zX~0PG]8RQMNT
+cEb6eۏ
+[ o gFAzFPx{dxíw8ٔ{{L> d2C
+N Xx \VnJ[)Iw/鹻 |GźYDH*Sp60cJ2@W%Ѧc_^$#*:G6n>D;~`9hXB UJB_вˈ%w'$v|#T<68KMϑ-5U+'B
+ĪNbJOv'|+*Mk(d }C˱@q&aR%}
+!VЃs3w2a2awHz/Q0F ]~;ä NDP
+mK3xke_ S!V&=v_PL9؃Yi
+NU_)J69f*S  1
+2lu'h7^#S)Xi2..P
+.
+
+wE)3J&dقݶR¡S\. 5J$I&oHȳ~ lz>
+Ux/Hu;?Gt{?;TH L|F8}{p:2t͆<LCA`ʘÇ득+' oR0D?AClIZ1F?j᧴{^
+Mњ`CC^%2]u OsLTxpY!UƜ{'
+ j(*aATR?b0IKP
+M ^cYf3-Jcr;ruGuAT1?Q8Dpyy+c@6![ofZp
+p $4ق'{&M\ΰч!qi (.h' B T|{I6cL.빍iI꫿\!;g`1 j%C o3*60E؎]t.-%0 YK_nft] *VFCtJT+\WZ8gF^
+ޞf 5I=#6.@2z;W`B/ęQghjyJNAX3,
+l$S[z~Rq39钺9Q/m"%ʤ7 5MKL鑧"IߏG XTގXLFݧV jp^/Mgۻ{w
+*9Oʈ<"aAq.M
+'[ŃkZЅi,b1̇`(mHNeK/
+[(#QGduT^m%!(7KgP=hϕkɐU+.[eC"GDΨ<*<h )` AU @O]hlf2!HF#
+ F4hH s73ᖟ `RTwfͳ;6B>Ř 9&܂?)\<&Ŏ5 LJu@Y,냲ھ_w0^17p޻*>D8_)$UźR!jOF>{ t,-bP,m`D"/zA ͔إQZG&U]xejxLwv~=)@B6?!;53/ps@tOZS7ؙnlxZ?Zj a{6L41 2Qi&֥l]o=7ļ ofЖr MEV@H/aD٦HlK5)ŒZ OE3IG'г;D'zl(E$.ٜ-W R'\w+)w3꺾 @%R).~9;].šg+)%ȝk҉^NW>b1z:soD
+K2w[|>9vWMFu`axchիU`*ʆe]OV'6xd?H]_rA+zdFH ʋ<ǴkUsFzaH9-
+ qiH阇i'i$"{S*VwF/t<Q`ʒZ+pr)(.j鸫Ik5 <ʆˮ, kODTJ&^7ĪQve
+&Z ^4^sD+`WHb6 LW{ZZ @mqvɷ(D\+l0*V߇VmhƏ/S`|^\<-62N3" Tolre!H2 pA ֛{ȼ/
+xF$g9Z`WVBg#j\˂eG [.]0~X{2D? "3Bj,K~
+aE7λ/Վ%
+zI7Bs.K  *VdDlj@%
+܈ Zsﮐsh̸%^
+@8? N8gGgrXS Ap 4z*4,ít4GndS>fQCWUZ{S;Nx}H&*
+)QC+2
+d[ H"t* c*bڢq,#S#u'Ҭ:4asCDMF|ɸm_1L]Y\*X>tgDd@&[)8;<{8<+VG\H^aae-4sJA \ hM[\`#pD5Z97g;BWmqTXX%0
+-#ZI G$ƗtmH#)XwPZAD|S ofTH)>M1b 7ɆSuq
+jK4[s xL Ǣ]5 !M!AdƧN><:ǻZ(8)e /W| b<T?% :@,-ecMP8umVg9H6}=5
+v `0!$`GA"I;$^?Ke O N(սYy5BwV%ju;)lFoa7xڸ4-% $ֹ/zskǘ(sh>DDŃtT7rur0Ң`ܴh5 5
+49 ֩B}ԭ``Ӓ #Jn_F H|$OK=œi17o-Hqp[ɫ%%:Ɉi3۠G C
+ bm]e%8 P
+!s_06)Q2JB [t9'Ԝ,[f
+O 2Zq#d"@bQ, w)P\b`x
+48<'j-'ǘ<Tb2vEtq3qODd_{`/hh`’9_1hAY|/ ޷U-͕Ao("$r؆TPR;.-w>&LJiC`A^#X8tH?daĖTSTaH0@U)^e}Jb7%ܔ%:ƿ@
+HX'#k?WRmP
+nTp }1`#
+ףd-֥#Oℚt:5Ћ/<b0'moqIBFW.\kc5ߦ-vT[͂ -4:dݗu[ 8:P금BTUQ,F24lEO?Dk{ 1k6)R̘GI6Yp^U!A@{xg#^/ ETzĒʻ@:F'\Q6t,pT!i
+N!dGB^
+$@yn_uUCK_K62B|
+^TmrLDgʿf)!-och}@o[rE] /iWJ8OgbӁFe(/EΠyOLB]IkTډabV
+
+ 2ց%bjg'2-6DJZe' oBi2+]x;SP{{Jum f^L
+S0~o-SEc*vl
+TnL`)e|Ȑ!dܑ[sD\VogFG(1 OJB
+`@92 zX;]۩i%[5p8Q cd\Lo;jP/ng[qBQP;,Ve3Pr'ط4Y 8[% c
+^` PjL>ʠq:6S]K"g[ ϑHB5VEqLJX{CB!PIq9Llxʪ7>֤]@!@9H!pə$ ?)܎l/"́+@`}}:\ 8zQgS+򒤿C}R:HUF\Xg/AZ%c1wlET
+i,.R2T`5
+BxrWH JPe#Bb|-[PEh‹(5Sfr/]IƊ dE#OS39ӻ]eۮɹ.9_beM9b#e(- 0Ra9"U,%~X܀z۽{'6[@t[W%* .d'vR {h!AedCE}x=E[|B$7J* B-
+Wwy%KrIv\bV\nd{6tv/~
+7U>8rAC<jE-j牷xs)D1Ì/qp**̸$ّ,  Bȼpk MhpK7U]h&-$鎻Y;q6wzW˄֭AhD^R"s5
+]NEc,ߞ# BF:0/-EȾŒ׃F\I{tAZCORuk i)ytkdN&vA P{P'>xƆ`.%,;:Կ:aFoTQ}v#ףQk's~z5hMQʒY>Cʍ
+fv {E/IKIE> pyde
+ʾ=z:@7J|5g8x 3O
+3H1؄F.yfzWIM j[.w%i?҆Uf|}@+[8k7CxSEOޯp$Q+:<]K3T-y[Nz;y-HZY^.M
+В ?a2H,^'?^nhOƒi<Ya2+6aFa<!02]c:eKXX[UgOu5iyPcVT5RIA6OԸi C\QZMDƃB!X:\!^"{E Vax$P \$ DBBTFt~{O
+`*ـVX
+*xe§֊Z*c`VSbJU*6TK@zqPhg*ߔU(QU49L
+cM*TR!R,BȅE*C|TzpF@4*텰جXbL.
+
+PA@
+-$ !6wHGO r~e~/]V~/P~7SzKFv`;`9v#
+JBN,ӭ'
+(
+i`<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
+<font-face units-per-em="1200" ascent="960" descent="-240" />
+<missing-glyph horiz-adv-x="500" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="400" />
+<glyph unicode=" " />
+<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
+<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xa0;" />
+<glyph unicode="&#xa5;" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
+<glyph unicode="&#x2000;" horiz-adv-x="650" />
+<glyph unicode="&#x2001;" horiz-adv-x="1300" />
+<glyph unicode="&#x2002;" horiz-adv-x="650" />
+<glyph unicode="&#x2003;" horiz-adv-x="1300" />
+<glyph unicode="&#x2004;" horiz-adv-x="433" />
+<glyph unicode="&#x2005;" horiz-adv-x="325" />
+<glyph unicode="&#x2006;" horiz-adv-x="216" />
+<glyph unicode="&#x2007;" horiz-adv-x="216" />
+<glyph unicode="&#x2008;" horiz-adv-x="162" />
+<glyph unicode="&#x2009;" horiz-adv-x="260" />
+<glyph unicode="&#x200a;" horiz-adv-x="72" />
+<glyph unicode="&#x202f;" horiz-adv-x="260" />
+<glyph unicode="&#x205f;" horiz-adv-x="325" />
+<glyph unicode="&#x20ac;" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
+<glyph unicode="&#x20bd;" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
+<glyph unicode="&#x2212;" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#x231b;" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
+<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
+<glyph unicode="&#x2601;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
+<glyph unicode="&#x26fa;" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
+<glyph unicode="&#x2709;" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
+<glyph unicode="&#x270f;" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
+<glyph unicode="&#xe001;" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
+<glyph unicode="&#xe002;" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
+<glyph unicode="&#xe003;" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
+<glyph unicode="&#xe005;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
+<glyph unicode="&#xe006;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
+<glyph unicode="&#xe007;" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
+<glyph unicode="&#xe008;" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
+<glyph unicode="&#xe009;" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
+<glyph unicode="&#xe010;" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe011;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
+<glyph unicode="&#xe012;" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe013;" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
+<glyph unicode="&#xe014;" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
+<glyph unicode="&#xe015;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe016;" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe017;" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
+<glyph unicode="&#xe018;" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe019;" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
+<glyph unicode="&#xe020;" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
+<glyph unicode="&#xe021;" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe022;" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
+<glyph unicode="&#xe023;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe024;" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
+<glyph unicode="&#xe025;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe026;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
+<glyph unicode="&#xe027;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
+<glyph unicode="&#xe028;" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
+<glyph unicode="&#xe029;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe030;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
+<glyph unicode="&#xe031;" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
+<glyph unicode="&#xe032;" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe033;" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
+<glyph unicode="&#xe034;" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
+<glyph unicode="&#xe035;" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
+<glyph unicode="&#xe036;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
+<glyph unicode="&#xe037;" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
+<glyph unicode="&#xe038;" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
+<glyph unicode="&#xe039;" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
+<glyph unicode="&#xe040;" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
+<glyph unicode="&#xe041;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe042;" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
+<glyph unicode="&#xe043;" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
+<glyph unicode="&#xe044;" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe045;" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
+<glyph unicode="&#xe046;" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
+<glyph unicode="&#xe047;" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
+<glyph unicode="&#xe048;" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
+<glyph unicode="&#xe049;" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
+<glyph unicode="&#xe050;" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
+<glyph unicode="&#xe051;" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
+<glyph unicode="&#xe052;" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe053;" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe054;" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
+<glyph unicode="&#xe055;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe056;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe057;" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe058;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe059;" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
+<glyph unicode="&#xe060;" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
+<glyph unicode="&#xe062;" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
+<glyph unicode="&#xe063;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
+<glyph unicode="&#xe064;" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
+<glyph unicode="&#xe065;" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
+<glyph unicode="&#xe066;" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
+<glyph unicode="&#xe067;" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
+<glyph unicode="&#xe068;" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
+<glyph unicode="&#xe069;" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe070;" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe071;" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
+<glyph unicode="&#xe072;" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
+<glyph unicode="&#xe073;" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe074;" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe075;" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
+<glyph unicode="&#xe076;" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe077;" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe078;" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe079;" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
+<glyph unicode="&#xe080;" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
+<glyph unicode="&#xe081;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe082;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
+<glyph unicode="&#xe083;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
+<glyph unicode="&#xe084;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
+<glyph unicode="&#xe085;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe086;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe087;" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
+<glyph unicode="&#xe088;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe089;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe090;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
+<glyph unicode="&#xe091;" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
+<glyph unicode="&#xe092;" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe093;" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
+<glyph unicode="&#xe094;" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe095;" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
+<glyph unicode="&#xe096;" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
+<glyph unicode="&#xe097;" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
+<glyph unicode="&#xe101;" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe102;" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
+<glyph unicode="&#xe103;" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
+<glyph unicode="&#xe104;" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
+<glyph unicode="&#xe105;" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe106;" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
+<glyph unicode="&#xe107;" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
+<glyph unicode="&#xe108;" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
+<glyph unicode="&#xe109;" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
+<glyph unicode="&#xe110;" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
+<glyph unicode="&#xe111;" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
+<glyph unicode="&#xe112;" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
+<glyph unicode="&#xe113;" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
+<glyph unicode="&#xe114;" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
+<glyph unicode="&#xe115;" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe116;" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
+<glyph unicode="&#xe117;" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
+<glyph unicode="&#xe118;" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
+<glyph unicode="&#xe119;" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe120;" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
+<glyph unicode="&#xe121;" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
+<glyph unicode="&#xe122;" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
+<glyph unicode="&#xe123;" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
+<glyph unicode="&#xe124;" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
+<glyph unicode="&#xe125;" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe126;" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
+<glyph unicode="&#xe127;" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe128;" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe129;" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe130;" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
+<glyph unicode="&#xe131;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
+<glyph unicode="&#xe132;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
+<glyph unicode="&#xe133;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
+<glyph unicode="&#xe134;" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe135;" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
+<glyph unicode="&#xe136;" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
+<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
+<glyph unicode="&#xe138;" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
+<glyph unicode="&#xe139;" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
+<glyph unicode="&#xe140;" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
+<glyph unicode="&#xe141;" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
+<glyph unicode="&#xe142;" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
+<glyph unicode="&#xe143;" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
+<glyph unicode="&#xe144;" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
+<glyph unicode="&#xe145;" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
+<glyph unicode="&#xe146;" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
+<glyph unicode="&#xe148;" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
+<glyph unicode="&#xe149;" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
+<glyph unicode="&#xe150;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe151;" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
+<glyph unicode="&#xe152;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
+<glyph unicode="&#xe153;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
+<glyph unicode="&#xe154;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
+<glyph unicode="&#xe155;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
+<glyph unicode="&#xe156;" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
+<glyph unicode="&#xe157;" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
+<glyph unicode="&#xe158;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
+<glyph unicode="&#xe159;" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
+<glyph unicode="&#xe160;" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
+<glyph unicode="&#xe161;" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe162;" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
+<glyph unicode="&#xe163;" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
+<glyph unicode="&#xe164;" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
+<glyph unicode="&#xe165;" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
+<glyph unicode="&#xe166;" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe167;" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe168;" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
+<glyph unicode="&#xe169;" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe170;" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
+<glyph unicode="&#xe171;" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
+<glyph unicode="&#xe172;" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
+<glyph unicode="&#xe173;" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
+<glyph unicode="&#xe174;" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
+<glyph unicode="&#xe175;" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe176;" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
+<glyph unicode="&#xe177;" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
+<glyph unicode="&#xe178;" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
+<glyph unicode="&#xe179;" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
+<glyph unicode="&#xe180;" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
+<glyph unicode="&#xe181;" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
+<glyph unicode="&#xe182;" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
+<glyph unicode="&#xe183;" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
+<glyph unicode="&#xe184;" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe185;" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
+<glyph unicode="&#xe186;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe187;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
+<glyph unicode="&#xe188;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
+<glyph unicode="&#xe189;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
+<glyph unicode="&#xe190;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
+<glyph unicode="&#xe191;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe192;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
+<glyph unicode="&#xe193;" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
+<glyph unicode="&#xe194;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
+<glyph unicode="&#xe195;" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
+<glyph unicode="&#xe197;" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe198;" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
+<glyph unicode="&#xe199;" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
+<glyph unicode="&#xe200;" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
+<glyph unicode="&#xe201;" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
+<glyph unicode="&#xe202;" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
+<glyph unicode="&#xe203;" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
+<glyph unicode="&#xe204;" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
+<glyph unicode="&#xe205;" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe206;" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
+<glyph unicode="&#xe209;" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
+<glyph unicode="&#xe210;" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe211;" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe212;" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe213;" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe214;" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe215;" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe216;" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
+<glyph unicode="&#xe218;" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
+<glyph unicode="&#xe219;" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
+<glyph unicode="&#xe221;" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe223;" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
+<glyph unicode="&#xe224;" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
+<glyph unicode="&#xe225;" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
+<glyph unicode="&#xe226;" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
+<glyph unicode="&#xe227;" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
+<glyph unicode="&#xe230;" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
+<glyph unicode="&#xe231;" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe232;" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
+<glyph unicode="&#xe233;" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
+<glyph unicode="&#xe234;" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe235;" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
+<glyph unicode="&#xe236;" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
+<glyph unicode="&#xe237;" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
+<glyph unicode="&#xe238;" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe239;" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
+<glyph unicode="&#xe240;" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
+<glyph unicode="&#xe241;" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
+<glyph unicode="&#xe242;" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
+<glyph unicode="&#xe243;" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
+<glyph unicode="&#xe244;" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
+<glyph unicode="&#xe245;" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
+<glyph unicode="&#xe246;" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
+<glyph unicode="&#xe247;" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe248;" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
+<glyph unicode="&#xe249;" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
+<glyph unicode="&#xe250;" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
+<glyph unicode="&#xe251;" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
+<glyph unicode="&#xe252;" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
+<glyph unicode="&#xe253;" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
+<glyph unicode="&#xe254;" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
+<glyph unicode="&#xe255;" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
+<glyph unicode="&#xe256;" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
+<glyph unicode="&#xe257;" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
+<glyph unicode="&#xe258;" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
+<glyph unicode="&#xe259;" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
+<glyph unicode="&#xe260;" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
+<glyph unicode="&#xf8ff;" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
+<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
+<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
+</font>
+</defs></svg>
+D
+webfTP
+
+
+ / _ "#%&&' ' )9IY`iy )9FIYiy !'9IY`
+ |vpjdc]WQKED
+
+
+j
+
+j
+)"&
+j
+
+j
+)L
+j
+)"&
+j
+
+j
+)"&
+j
+
+
+x
+}
+x
+} 
+x
+}
+x
+v
+L
+  d    d  l
+
+x
+
+
+
+x
+u
+x
+u@--@$?2O*$ $*P2@%d  
+  d  
+BVT@
+Pԇ
+ $
+op zy#%**%$ p
+
+
+
+
+\l
+
+lL
+7
+
+&
+
+
+
+
+l
+
+ f &>|.hK
+
+]
+
+]
+
+Kh.|
+
+
+Ud
+
+&
+sdd dd d
+
+&
+d dd dL
+
+
+
+ddd
+
+
+^
+ddddddddddd
+
+
+^
+ddddddddd
+K
+
+K
+
+K
+
+KMbyl+MMijMXXX#
+K
+
+K
+
+K
+
+K
+
+
+
+Mbyl+MMijMXXX
+
+
+
+
+
+
+ޖ
+
+
+ޖ
+
+
+ޖ
+
+
+
+
+
+~
+p
+
+
+
+
+>
+
+
+
+
+
+
+
+
+;),,;)D);dddddddd;)d
+KK
+d);ddd);;) dDDDD
+
+
+
+
+
+j`
+
+
+w
+
+?
+
+
+
+
+[֛[[֛;rrr 
+
+2
+
+^
+
+
+
+
+2
+>p
+
+
+^
+
+
+
+
+ &
+[֛[[֛;rrr 
+
+
+
+
+
+ &[֛[[֛;rrr
+
+
+  
+W
+
+&
+
+&
+
+ W
+tW  
+ > 
+
+
+
+  
+ mtrrr[֛[[u$
+ Lrrrtu֛[[֛[
+ntr$  Krtu֛[u֛[v
+h  Lr
+
+
+
+
+
+R
+2
+
+2
+
+
+>
+2
+
+2
+
+
+>
+2
+
+2
+
+
+>
+2
+
+2
+
+
+>
+
+
+
+~
+
+
+
+R
+d
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+2
+
+d!C@1?*'),GUKx;(.9)-EgPL
+3
+0[;P$ 97W W
+2
+
+2
+c*  `  ct
+
+,rr
+
+,tޣ 4  4 
+
+Xx"xx"xx"ww".
+
+
+^
+x"xx"ww"xx"
+
+
+`Z  HN.
+
+
+^
+d  g~j
+ *o;7 * 
+
+
+`Z  HN iT "ZG !
+
+
+^
+d  g~j
+
+C;S;;S;V0
+;;T;;
+
+08D;S;;S;V0
+V08;;T;;
+
+D@
+
+
+
+K|@
+
+@
+
+J
+
+
+
+
+>SV
+N
+
+N
+
+
+
+
+
+
+
+
+&
+X
+ &
+l l-
+p 
+v
+
+
+
+
+
+
+
+
+
+
+
+_5,Ry:" *28 T2*BBW-ޑY". BB % Z
+A)Y 
+l27);;));Ȼp87cs*
+s ;) );;)2c
+.9I@F* L6;) );;)g  0!;bA4
+
+
+
+
+
+
+
+
+
+
+
+
+ 
+
+
+
+
+[֛[[֛ /O2*(8\6/H* 
+
+
+
+
+
+
+
+
+
+KK
+
+^
+
+K[֛[[֛V
+
+
+
+
+2
+
+2
+
+2
+
+
+g
+
+g
+
+g
+
+g
+
+Df
+
+fD
+
+Df
+
+f
+g
+
+g
+
+g
+
+g
+ͨ
+
+fD
+
+Df
+
+fD
+
+Df
+&F
+
+  &
+&U & U
+##
+
+
+  &&
+
+ U & U
+#
+&
+^$,[~UU & U
+#$DuMiqF
+
+
+
+[֛[[֛. 
+
+
+
+
+=
+
+2
+pp
+2
+
+=  
+
+
+353
+
+ 
+X
+
+ v  v
+!{, 
+2
+
+,ԯ
+
+2
+0y
+
+
+
+
+
+r
+w
+
+'8GB 
+
+ `H  >JS>H7 '+" NA
+5M[`/Pg!;('2"&"IbYCe\D9$ 886#1%)*J7gG:    8G\au9hoK$]54<<E"5cQ8 .@AU!UhQ)
+Y
+
+YCh:#6#:d*! GDK
+
+K
+*
+()GM~ 1==
+  VOddip &yLN(  %
+H YS(22S 
+%&jPddO (SNLy& pd(Y 
+ p &y22SY( nTjkn  T.T8
+  Vd% dd
+8%d
+
+[֛[[֛9
+
+
+
+ &
+
+[֛[[֛ &
+
+
+
+
+
+
+
+[֛[[֛@
+
+
+
+ &
+[֛[[֛
+
+
+=?1 " "/ ?9 #hu!$ 0 E.(,3)  (    
+*!A 7 ,8 !?*
+
+\՛\\՛  ' "r"v G
+ .&*
+ r$>   #1 
+   %  *
+ '"  $  g2( %
+ 
+$33$ 1~ - - - -
+ BBBBBB7._BB^*k"5._{jBBFi BBBBBB77/_
+-$aPN(?",9J0* d2>2
+""  
+7Gd/9+DAL!X
+
+
+K  
+ S#  nnV/
+
+
+' gdV^|d22
+" 'gdM !d22
+"
+ d d 1t5gD >?1) A..@  ^  ^
+88 %v% 88
+x88 %v% 8
+
+"""" '$+ 
+
+222/2 !
+
+ &
+AwasOEkdb
+
+
+
+
+
+ AwaxchsOEkdc
+
+
+?,dԢdu
+
+ 
+ 
+
+
+?,>dԪ
+|
+ ^G
+|d
+77
+P
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+L
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)HJLP;))%&!!&**22
+9:LkkL:9r*dd*22
+
+ % XW &
+dDdL D
+2
+ % XX % 
+2
+ddd
+
+w,,
+v
+
+w,
+O,T
+
+
+
+
+
+
+ )V=>8'"d 1*) "dT,| -otE
+
+
+GAkI
+! "% ,=?W7|&F@Je5&2WO_e_
+2
+
+2
+
+# =I+E( //}X&+ 5!H 
+Oh ..40:*"6-@#
+
+d
+
+
+
+KK
+
+
+
+d)  )k)  )
+m!mJ.M-(2N-;]<* K
+
+KK
+
+K
+
+X
+
+K
+
+KK
+
+"p
+"
+(Ab
+
+J
+
+
+
+J
+
+
+
+J
+L
+
+
+
+p
+
+
+
+p
+
+
+
+
+T 0I' *L
+#3{,#
+n  6F82 *<SC# 
+(#( (#
+d 2d
+ dd R ,
+W 22
+
+ # 22+."A2Vdd
+
+?
+9   9 gLR   2 2  2 2 $ 
+
+ 0
+
+)
+J 00  
+)
+ 
+ 0
+J ))   
+
+)
+ 
+ 0
+ 
+ 0
+
+
+
+(
+L
+
+ 0  X * ^ h(T*v
+8|t*<6`R.j(h6h^2Dl.vb F !2!v!"@""##"#8#z##$$0$^$$%4%`%&&~&'P''(4(p())*&*J*+
++z,,h,,---.(.f..//F/~//0>0011`112$2^223"3>3h344`445,556>6|677N7788B889
+9J99::l::;;<<P<<=2=>:>>?(?n??@H@@AA~BBBCCBCvCCDD`DDEZEFFtFFG6GvGGHH2HNHjHHII8I^IIJJ.JR
+   
+   glyph1glyph2uni00A0uni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni202Funi205FEurouni20BDuni231Buni25FCuni2601uni26FAuni2709uni270FuniE001uniE002uniE003uniE005uniE006uniE007uniE008uniE009uniE010uniE011uniE012uniE013uniE014uniE015uniE016uniE017uniE018uniE019uniE020uniE021uniE022uniE023uniE024uniE025uniE026uniE027uniE028uniE029uniE030uniE031uniE032uniE033uniE034uniE035uniE036uniE037uniE038uniE039uniE040uniE041uniE042uniE043uniE044uniE045uniE046uniE047uniE048uniE049uniE050uniE051uniE052uniE053uniE054uniE055uniE056uniE057uniE058uniE059uniE060uniE062uniE063uniE064uniE065uniE066uniE067uniE068uniE069uniE070uniE071uniE072uniE073uniE074uniE075uniE076uniE077uniE078uniE079uniE080uniE081uniE082uniE083uniE084uniE085uniE086uniE087uniE088uniE089uniE090uniE091uniE092uniE093uniE094uniE095uniE096uniE097uniE101uniE102uniE103uniE104uniE105uniE106uniE107uniE108uniE109uniE110uniE111uniE112uniE113uniE114uniE115uniE116uniE117uniE118uniE119uniE120uniE121uniE122uniE123uniE124uniE125uniE126uniE127uniE128uniE129uniE130uniE131uniE132uniE133uniE134uniE135uniE136uniE137uniE138uniE139uniE140uniE141uniE142uniE143uniE144uniE145uniE146uniE148uniE149uniE150uniE151uniE152uniE153uniE154uniE155uniE156uniE157uniE158uniE159uniE160uniE161uniE162uniE163uniE164uniE165uniE166uniE167uniE168uniE169uniE170uniE171uniE172uniE173uniE174uniE175uniE176uniE177uniE178uniE179uniE180uniE181uniE182uniE183uniE184uniE185uniE186uniE187uniE188uniE189uniE190uniE191uniE192uniE193uniE194uniE195uniE197uniE198uniE199uniE200uniE201uniE202uniE203uniE204uniE205uniE206uniE209uniE210uniE211uniE212uniE213uniE214uniE215uniE216uniE218uniE219uniE221uniE223uniE224uniE225uniE226uniE227uniE230uniE231uniE232uniE233uniE234uniE235uniE236uniE237uniE238uniE239uniE240uniE241uniE242uniE243uniE244uniE245uniE246uniE247uniE248uniE249uniE250uniE251uniE252uniE253uniE254uniE255uniE256uniE257uniE258uniE259uniE260uniF8FFu1F511u1F6AA
+Dhmtx
+Ѻ5webf
+B3.a0b ?@u" @aF$%
+
+i]DԡZJ\8wwV"FpUԯ.Χ(gK4O n;NR{g`'!PMUHEՠJʫ*Yq9c<U9!QIYׅ-KC+ դU)Q94JYp]Nq9.qyVV
+n)9[{vVכ־FWb++{>׍a|*gQ,K<'<!ɣrYw֜βy<q9{-]c]oI!0l67͍{jG,OX^PdQ{,M4c(QBX
+3w"[EtW:ӭ:$">2c 5*.lN/h]GtT
+'qh8`;aX6CF*dYc"'?hLV㗌,>ce3eVh =C~xC\((qb@ 4xK&hׁ 4\2DZ6N1|-;j Yu@jѫxi䊧mK ٍDEwq3̷.cAw@4t.gkgr{~Wl~{lW2} 276a2\6oz@$HSH gbtX70Ktc1,7B oLƏ66[,%iZ ,l>TpKSGg\> #A#3Eyk6v;u3!ZI8M
+!~dXgB(0< kOYxeƧĭ5k =d ϧ> +tC-o
+Ǫ/_koܶs+fOztpu7-}d9 se \9.H4!0S\ ʱk2"?ip7\2zlްt=W\!KyOXimUnov 6: 2 LZkAA^qCޔ &PaFI0>&Q #FQl> A·q*OȦ_@27l,sf 6p7ܩ?M1vA2]$j";vlk~va0gjzRD:gc6yw%g(þ#'uB#=_@?>FVb0a!aL4tXv:Fh9j^xތz}Wn}7}jΚiHitKSaXEEbbBQ1ftxFȮ -"dqA\~F`6i䁕+ Ԣ^Ȳ}ש׆k&Ĺ<- \;g1>w00v^x 7l<y}So9-ۮ6kбl˴no庾i[u~¬o`j{i\C4,"iW8JoVbpwC!;'7D.v֏ noZ-nePio4~LY/zmw_gϽR"tޠ&NoN)4M CG2\j8d-@>#Ot^5+xe.^]׼G8^ m(t1 sbfJ %<
+
+TG~o]VṇznАzd,/)jl. w<w ?5*FqH|<f7[6Td?C8S'N
+#0f2^~7:
+mM I`M:ӊHF9B:gSkozk#Sǫoc3A'ӹm׾iknZ-yZP = Uc'?&ȏKEul;><v3t{8-|'
+ea~H94xA-@y bT4@0b#]DDljDSio:AgSP z:;-|yH"r {B{\5RLi6AAtM]taRKC!1CgC샂 +1EG!Xzٛnzv@x
+/8VF01_K?x>}#G7т\Wp!.@bwɡ+{o#ԍPQҮnī66
+cZD(. u;nM}?vtxF{+`
+="rPπlDV̶
+?26YI5c$Cfb!X*|F^$p7p55߶6[mjgl>* KO&
+ 8ܝ:ǰokKm~oS-*4E}P/% k:e"1AJCAX8=
+׼1J[H*d÷J(RY}ҘchC;ayh&C q;7/SGny'^9wה[yF`4;upX_#6Qy'xCq/QP&Nt
+%2Wɂ\{*B{7,9.'ew U^W&$r9rcGBwll<ʷSQゅh! iѨvJ :Y?#_m4q[ },EA{VПP|Dg?9MId?{)/ /\[ Jҏ[f4G>QK^ m O -7w]„<U3jƏ,:Yq~0/mŵ@CCFq<yxh\0=RgYd((_2a_{pM T*0UT!if$ԟ(WqRC:Pa3=b rK1'-{HʽH1'`kϯex$.h{܆`F zE0c5xfM䏾}߾SSK]Nf'pPιS`BmmHv94ሄ^m D $,'܄ pWɭgdV/L;MZLꭵH>{,Θ쬷ΘQSo lsɿh?A2q`5Z&*X1L5:6ς+O]uej%?ۼ&aW?{2[}W?JbΙk-\b7sIkf&Λfx~nO-9V ~cW"ȗy)b\)2MrWf;MU7'[-c/.ؾuMl&.9) G!!W* 60Cф#qrqOKZOWq,8́/XpTȑg<>¤)[J8o`
+;S\S%h~p|J˾F~K=E0NQX*8;D7Q1QC% *Eyy} UG?>I`>'6<+3IVgϮyOQ$WBvH v[Ϗ 2+ 'ø6N߆<ɕ 2S娚9X1\┣df>B~-t>W]pPrZ['+ƌl9]8qC!' @AAOuШ
+!?M\JMͭfǞ)ߕ=w?AN>¼}jQ<ǏpǠ^(}1+2q F4RiHďITr8^!gm>'ڸhE`s̊ol!(9~ o%#)~ƃj$@ՔLpGOa{߿fé)zؔY<~^cs潺ݴNRURTY%8Ks3qd]^QTb' zx)HFҩPmUZjQ&XƁo<0jYGz]$8c&hyݼwΞ{9^sf߹m[vӣ!(ZAsۧyB8RiԣBg6{UmtyW!bpǮd n/ŷʼ@v/%cxEn:4Y²,yZ-krcH&^ȩC'Ȯ'^T5r)((IJU&#݌! +YM.JEX^|Lw@ھZsgY洺\
+ PԐ4^sfcA3,XAPbksY yHhP+bW=};"Z&x<SySVY&=4&1J5u~,ӿzeg^QB\/Pʄ%+pre|Pn TcZ>?eV"_[Q/5Y
+v >USo^1/,ēvcYGmŨ~Amz ?/40
+eERb/"M 75ul[drC&Y͐&I
+`!>p;J-b--.VM4>Fj/5σt5}>C*<'d?,cdGf2ҁ0w6Lh"fKζp;ǿ϶Pdc1EOi%Ř(DCWV2I)TiMF
+t`ߑXAD;b|pA7}q2
+@Y`~iԬK0jY( R~^ҧ8>=F"˜A[DqvQCX|ZsO<NǦcPI|։2 ů1Q|FH\[ Tk޽$3X5ˮAq_rv7@v2ˀi%m؊fP^{ovvyfVw4ew ""Zd[TCʭ"ٛ!Cƛ#^
+ZfR4x
+B~#V*px ^(o/`Dס. EOWTv6 M^~Eyl /ѫNJlQ6Mq":}Hea-EY"z"ȏVKF58/7
+tDn#D*'^IZ}pITmdL%7@C:FBy%
+2
+;gELc7"<^$g$ysL״$֠
+B\_wV] 0TOCÊQ}5{Ho*;;葞rǨMc54S
+: M7(kY:z`gp Jstˉv'eG^~iD16dA @'N ֭<?Ғ9庳bɩEÁ:h{h0vۧQ~{"HGQkl<:ʛ^g/ _i P>N.?f…1bzJD V
+o@7R@6<%IF0mj= [}Nۊ57pyv4@<mЭ9Tp?R70қQG[jzib~/)wC? רa-/Cn.ĕH j63pKrhXIƎj
+o19
+f\~:-ѓK47BY̆y%DC~em@]%rs4T G-Ug>HOpVB]{9&^6|m _PLLI7ǒi "'T }? 4|[Fǭtu/_y;Z?HK0Wzc#)~.rĥ+B&JG0[.ΡrOk;VCoX K۝S߳rt:zX\xmJhxNh5 K`;ydp.Ec4XD<-llip.^p: u/.Y[rl_4kz$~Dq]7/T_<菵4K$Ɩ &w S7|K^7MsMGhw㢴0]?fja5aiЦ6C2no• f=)d^v qNcԎl=u]?;f-E~nv}5%Oջd덿=Z%v  nKu ̓*J#1hu1Hr o}SZu=w;nϗU `FȶEn?߫k&l9YdgA8NSGD09MAK{ހK3݊
+(iGx\}~IֳFv@Tu֭J
+
+@-LwzYgw`wx-(d٢]F3_XcYmQԃWb-F K5d-0b球—֨T+_Zxcj*`}|x~LF*S*oMتAT1p71?R t>R'"Ey)oP7%$rv QeE+nzlVlFrkt''?R'ZCEIKy ga0^}pE;Kq{T/?i"%1ޒb-Ծqƛ˵+ 8]rIڣV{dȪ͜\AQvOS]0.NX9svb?OE~FPU}o[YKrA̓U%7Dw q b/h AhPbQؓJB8I ?I%=XtO;(PhLd S 'hݱ>|TV?,O"\`7.2>D fmg;-C'u, zA`-ټ$x vck2[xp\cbl΀ihsivaÛM,gĨlMz7JvˑVRWϋNo4(-XB^Cl&Vnnn D4[k6N&}f3YQw@$U$(Ǫo:-ZG#&/} ?N}ƥ7A!MhW>?iXprA١b?uϱι-h6;SB#/@ѿJ
+!%Q)Dq:{JI^ޑˡPY7UG(h?HmъvREH=N`P)QG9FMSMG@2E$Q
+$s~TkN"9Ն8cF^"?+G٠
+^*gUlFVxUpoC.XCƵ׵͉qK[k[K(l; ӡn
+6|5`#Z-svfqӟs͚>w7C{ A]Bz,iH'dv?`E
+x,mz`F[2avhp%(̒ʂ5Ԧ;Gюh\y";|"ٝʖrxzsPHCTvP$ly}iyhvMCr)#x-.(t%fu€(ۅeUUo
+pqeˡ啗syi Xk`>X@2P. 2͌>n|,/4} ?A&Jr+ɐCV]{Z0
+F$+%UZyޗٲR B)wT8(aRΣ*-sr5v !^tZ:/K,'F  9=G<
+0Q+Xw,]=bh[qBQI ;)"Ō926r?}lV =b[j4AzKkQ?T[%$KQ-l_@l/ &;차Dr?P_dE1~z^I~breufP/պ# E+S\G-R4 SSV俑; *`G*5'dL
+~ 5Fhb`
+ꁜ4[b$~GNAX$~ }[W}_z×6m&~O%j/r&|_Sy<-*Lϛ,JQzͤ𫷣|V|GVW~<mblB&̭jy\r='9Hf)ԅr w!;;vsB 7Ӏ'k*irb/K+ԔWRO h$!`1[r (a\TR"P?]Y;? хyKRXWOCzܩHjPn[忊;͇GqZ.A.*@/)WQHQUL2^$,T=Q
+HE YnH4r7P?99ߡ|O-5 %4 dzO/4L_PsT>LQD( J8F+)jCb
+Mu2Xc8$t}&<?9lW~ҿ͑n90A=&W=sԿ_V}?kU(mutE*
+K% tZpJ BWP Al(ZLzFZ}/40lV  i%L^V`jpP5QVVkzX8^sţW4U*u}L8F ~3B"I/.O
+=7BJA
+rDeTib@d4BDH T. ]K*շs\mF::4vX <;r%6aꇷܥGѧ|gуhvqtJJKH^vgp.?뜸B0^q8|fS[tCxҔ׬fй
+^FB
+PiWFpRU
+:̓D}فv}4z/F<P莣\U'c?4sJ jj>@Qr-֤U_o6q7P1ˤ+rc6I
+\ (*v24Uc(A ̣93]z;0'=*,e56Va,qh*P@wȬG/Oj|FIm #Pz;Jwʎ}< z T
+ҽr=CD5 62ZY
+T(E UJu;"}պ#LcӗVWO&CIԙu8*烞QaQ^*z(L|Jӏ^fp104~CUx*rV*N9π׳Pūsp_L3Z"}&rO|l~kC/Wj><SxMbSg(]J(Z#x\$OC68-f:{Sҳ蚨o4:)Wb"uiuh~d%BAM
+sWH.gv%4v+=¿
+SGϋjWHWu>[B{[uɶs;laziW߭\zC|\f
+ CM /@S>Tm
+G`v`?G(,zb" eAAi7QR<"iX:I܋(aV;4R]}^1vԵ7=p|[Jοeµ{)e#ief0KJq"*F#(GjJFhX#шݍk5ERP΋ ^pCeoe:{6۬5͝sƙ8X K6V[=}V+hͧJlZZ5W;TeV-@HID<͙[)֐l^bXeNN"K]@b?.HH
+gzXaْA}MOeXHNrڟW;htgttOyu3=*פؿCFGsh9JͽZ-k]L-~hii.49Qr5I,Vݓ^jf_},Q6?5NV
+ޞˍYٜN%ezqƨ>Z Nt1
+MS4(ȗh{-'ho7cCҞ?6'|ubգ@!bÙf{tz1UA?=@ t%䕉
+QGh`(# R'5XDQqM6gc'bu:'H( ?yյ6~.e[n *UyZst9R!GMM$xz$]{L<}4JZ~MVՕhy >@u +]2FqO8jѥWCQqrw.䄫ޥ\_y\On)IKGRHŁqI.
+d+u@ϴ kŤ}9Tv6*xge7?ì}S-AU OMlJ pժݧYwhi6\fAZc,rjFTMj8kO51TqW_n`7%KWsd0:`OXs$4?:SI1
+8(WI``@5a}ziV pPԽ+:d\j"=aj)W$q{͜p)V|7hj$L֡9\ځn[ k{lG.m m~TEbȭm`
+wnyP&:PLJY_pNWzVS׃]7Ed%i癬| EWM7r HB6`UGZ
+.d
+ 'VztXK2k̹d?zzK.>,BZ`q'kHqy5j>a\C#H;#p7l4} IR7ފ0$=V#_.vs{g><c_Ogx5&?̠';zaa:zӑQFꉢ ^MF
+-奐br B<9dpzIVQ:l+si #=T+R(MDC$
+a̱ ONgj19gqXk}FdcG,&..^ɷwwc>E_]3U|t{Jf窂u_.\*W=}lNo+^Ṿ vP>~sTjWz~_ogS}-DTd -TAaYf3,PATcm ռ4g}mE$BwŪ8>9JW⁩O/9PJCXA{,@c,tEJTj9
+췾d7[nlͬD@m8e cż#gHdd@~.jllɛeRcxE(( Km¼GXA7S@[l.%գnMDs]n_Q 5i?zGTG3T@e i,r
+O2<l+/,%m ۚXn|E]lí[m<|#z+5 7&\5S-{AE^tK M^rq]FmC%2vJ)W-}OM"`9l+=%"T'8zH3QҐѩYP~VزNi 7ۛ ?w1
+&]a
+I888qqpnǥ5w)^-8 ||||[5? JPKLpPa) "Z"WDmDWc3K O<H|D4 $IjHfHN<"yK򝔙Toq[dd<u͑"G\$K
+n
+w(9(MSڡ̧l\|H
+J4G& {DԞQaQFs-5-/m.* ]:otet;ti-һϧ_IA%C!u/Tf3V230f"a`䒩<fvf5fw̥'_ph8aeeayJ*j=wl$ll5}cgecw^>~/cLuNN+9K8;99/p>"k676-nܷ0h8)iʋK+
+ /"TD#J$rqr|!'O3XFާ0wY 1fg;73;3
+k`sݡ},0Ya DȵȵMyFMvYdS20~>/qJG
+i<#c0C~G9ee Kvв[ڷ{&V(Ө1j1MZqr7,gKܥX0QY{
+MYжz=a:[jEݢ BZZ=ns`+ȍ
+kk [[ ۲3Qfvd ;1qgg& nLdOboa_c@`PpHhXxNDNdNarsgrgsrsrs rsWrWs rs7r7s rswrwsr
+oo __ ?? f,˺eݳYϬW;MelP68s䘉GE{RαM 7nܺp;ڛZ[ݛƵ? ѵֵykx~yj?\3V+wE5=QMjzTӣ(vN؉k/셽d/Kd/Kdbbbbbbbbbbjjjjjjjjjj/r{^n/+v
+;NaS)ԼffffffffnnnnnnnnnnaaaaaaaChQN-ܩ?C?C?C?C?݇C}>t݇C}C?C?C?C?vNjHMp[qn???????>>=<<<<<:::::::U>::::::::=;;;;;;;;;;;;}VhSo
+$e6$t 0
+ t,3+2q FYO&>bm5ZH$Y{H jd Չ %٧y"+@]e{vNc)n?~?萤h
+G5s
+PKӦvmU ɾ{z"3`l W#Ԑ^@+,ckoAOpnuzzJ)Υ1}O=xR`J`qUs/+kv1xljlEl\nDƶVjg{Zdz7 5!xm5o[u&1ڂHBkAqrR (\gh7Ҋy=HZUPh$8RgzgͭN:
+lKGP,Nukc8mX@d̘?Y&{?P(G]Or-\LF9,&y8r3ܟ?p>~sDz1?\U5q=tzԒ&Znj%mM"}tkDwh-=mB76&:һqt" 1:Еu;"K_/Jdc0l0'^B8VCzg[ ;d
+Ybȃuu;@*}y| .'C>\g=9VŐ[o|g^ >d
+9
+*E|A*M[[*mOQz?Pn?R)YoT&[U*5S MB [
+oYDh{,}1<f&6h'ʥU#VED"TީAD9eB:%O Fun 7?%RG4"fgF꺁 a=-Qy+B,2օ5𙄌xnΪf*!l|GXQ ރUp
+Eu @-Do.6YZ-&a>f?
+0͒3B"MՎ&ۊhIڧRgME I(5UD] }b8$8>X h"l΀j.%ۀHH- Iݸ#1C4Y7YݖV o>P]6O47f ~
+G9|1-! 87[,mRu|57
+=X,aJ^tN4\fЄ]AzH^7F&k"LU>}>rBX(ۂT% JdhKPKTFaA3HHC[r;ad54 lLkjG{8h~ fR@9wB0 zS'a7@@Nƹlbj3hNXF/es'DsQ<k^׼ZASOidSJxN4DK! !٫vhA`EX
+Ucwwx$dƿ}ρ949p*T:%GQ^a'ebl-*XL%*ź.ڊ\@pR$T*Khpm-/oS3Eto}жVoeJ `<$t ]g*Z6ql~E
+S/iTtkǮWþ =?j
+}(,M= @JAd?§6PV[ dVv4jߛlH\{MȘ\Y܁`9M`Db<;a#z<x",dgCi`c:I>jw}Jz^:V.:ڋ{ͼ(ȲBɦx<Db#"S{PHuN/{r6;wUsPО<XYsMxu\b
+p8+6g_2lΡ6H
+afx}1+pB063rA$N~#d}פP7hH7bF§8 P>BtGNmx@j |{s9=wR/oDJs5z>;'xEq^r^=G?9AA_K%Dɮ:uikjkIeG՝#*)jm|t}`JZ؈H=4{g߁)qXMA,H71V"o,Y#hݨS_;a_ԗZ^cn4HE?}
+ȝ٤=}BWvުUeh
+_7Ґn+fٸY<(
+?y%wm+j&&!c^u'b&hm6¤*2 ?AIƲ5FWؙ[ƜBUzIE!m:xheǮnz|]% mrUFگ1 };!n F&gP;&$$F).tBQ3(C=Xes;iي@~NΡE SRh\BeobTnΒju g@'qQ딎nx.u6bVU& ];!C_  5*zɺmRQuqPZ0}mn^nOrT:U'h0nZp^R|DF_b\@mDE8
+9EN-v|3hCиE XT;P$=J-gݕigz~q(A<:h193N̽Q}CLWߧ׎~ b"|4u}cy62[ \d,ҎճbkD%0Tx{=;Է(i LS13Nh/6?'E^~P{sZZKĞB{Dt&z)Uoa5Q3ȗr~
+F]$<tm(} MB@[GxFh8
+7yrU>-bH)ɺz '}׶w!rXZ .:Vn;->:
+6rUcs4kVW{#5ߑ0B`ܝ0u".QdB0Cr]#Q9lqN^ֳh~NU\ 16
+~SnTl\THҲڛ-~G~)$oQ7-C}q%/avO|[q4~Bc-$N7<VHEi-RF GNM{"3 49[j<Wӭ hln QҨډGcq@w/e qg<: a钷u_P` b{EI(OWGfEyABa_;O^DQ's `D#њi:Ѵ+Y{{p&\RagϞ0gTLi<'7?X1C
+an0or1 /U o/?♯a_pHֱ G촠8ݣ?3F0`%ϑ<
+G]Խ8bl͏%-,)}%J:YjT;Ыȶ5Œ>6w{V餃.&(o*n<n9J
+"a Д+ a/;7zDZη{tM Mp iؚkNPwؑͺH`T $23f0z;"]*Y,QWlSOrW$5]KVٻBܚIk|=&[58ER0ދGksSnnnuExKr}~m`G4u{=]6f
+Bo&< ñc;2P$ǃ{mW_cª'B6Њ?$^z[CYݭjN~ ۮ0t6/)-1:p$Dꥅȗ
+
+,'yv nFTс['aMbJ]%&îlc6&IpF oi5'rr(qz6(5Eɢ՟l\Lk71Y4^)bٗ¦8yƏ
+N=9zT^[T$dk QiK%6qfO|c8$ji^vr.QQR"YrĊ krK<QI"@R9 /\&7Y}mgҊ7z6-Mu=,N3O\6aDAޮLd^r/.>
+NeRi4!3R"4nbm-y[X."!QKE\N4gՠםaNp >k)90BZBs
+yrer)vDtrv\v[>rJm
+a̼~uՏ>rMZcB<`)\yt|ۍr'<>[Îh7Z8caI! p⢟̮
+ A[.rhT pR?+;\*HsLqUf:ql-ć *6!h+ˬ
+:. Z`si(RQ|/`
+il^L#f-;-C;_*{@EMCooÂ_7TrqzF%ׯ|U<Zo[TA='DPJ];,U9Qpk4~_C^qEŮb SGsY2NAu%SD hj
+y;9$ߴIAhEO}
+g/+ Ճ5JY @Gf2Y/߼e߷|v/"p~刋T8OKr** 4hi@Q3g"j:$;:f,dzȚԌ꺳u%ˣ}O&i2U, @kj%u?4NKmd?5ݓ;0Ye}sZ>EƫUs^ݜv{fQ<ĐVPTfͦ?mpP*
+ 4JJڃ
+jƇ\ p38Я6pV?:$sDNƹ2n,HO\[ոK-)W~im?T:޺UeY-#dJe)Z5?$\dW<,Ɇ;ط5SոTT̄f(PYv=Q ~DX*8辩s- ˨΀55 XRl QC l|5{ӦT\t꼕+en۸Psl3UO[ZS3*,:ÛZLS'̵**@ı~xgno2-
+ WV
+BFUz)?60F8
+žֵu-}22EN}#䵵2H^a3rqs-S3&f퇣fwl.=W8,cHjcTWנs90ZDMC2ZMdjt"8:g{.Ʊ1Fb618"yԦ> W9 V `jT򔔑<IMԱW'%f&\yZdkʹRyjw}Ѐ[8ԍbB 'd'mo'<|E5:ڋo>r,ni <TS>d qN .g+ S Q KaB?_QE rjh>Eӛ;C׭7^q
+`Ue#-;oJċԝ>) ;Jg׭9R;OgiI7}8Kہqjeؓ+ٗ'nϷk3eFρ0V#pMAzb^PVu~1uғwn ^.II_vdW[Q,+Lbćq 9V} ΏVw4qU3&jıHYb ttT7ρarBwP9?)uT/aA19kM
+\Ps<Ta@<?M (.,'%?,%a~eU0/zQ(Ѹap:.6jdF@\V4{Ri 8ɪnuFM_=Z8Hlsy5k%|(i9 "6}ԋ~WK۟hYk\lRm& 0b]g"ހD^ތjJ*)6-Ybh
+Z=ޑA,(K# OfJ:;I!6Yi&d%m86#QW_Av}?+G cc*mg`>q+=[5͔?9W+^o^E8s)f 2aQxi& NE>"^Naa;f9]NE& t^CLz'e8ZRs&67_
+|POӌ\dR7zH9i
+
+6є7
+t C3; ,oÂx|
+/KMp1S_X.fV#U>Ȓ#B] AIVoІϵGTV1nr+OXS% ³fOZ[_9P߰ {Gln%#hdwH= ye/W>,IP,*MV~ºK&eċM콣=)qFS"GTF*LX,h[wweWQEx ?{^چExhiׂJH|^͓e*^Я.uxEb#;ԝ<]z]\wNhochqE=4Q17W̓lÕ6᧿HE_̣qy YR۫<x=cSXy!=08Ǘx ?{}F_Ǡzktɱ7ڂ|t+am<xe$eɍ<[TX[ 
+ OCnW?Nio](XЄ{Lz;g|Ǐ>9~l4sVy`Uߛ,#_u+DeM~hq벇#Yz$; 5ͯ9$ z>
+*jO$$O/xRtf-}*oɦ|3M;xިUl/.~XǎY4x3&x";$KI5dڭ ~w[M9O%4Q}S^t@w[Y;-s;bwH-* imI-1e/~TNN.p)H$W~ƦO
+(9, ]gM6r+#%/swA$q4O>
+d9}+$s?0a,>yڈs<=,c_*\D}2MT8/4g'ڦ8'}"C*\9#Y>z$7c[s|"$} ymzQx 5%o$jkp)x-:И|?ofgFr2SZq}q o,wyOgCF1l'L5T33yM92"s5uD6-JUbs
+O)wR -2/5f<BQ4kꐭG )%߼<dĪĞ32 `a]S{K%\]3&pڸCո 놶, ^T7h5ulDxڷL'Dr6v շfc\gA@
+8gz0\HkZ:h~@+#N fjyio!B R'5>`[!T`mC Iѝ}n
+>W!M}Uav43)!kcȂm? dwv!ה;Xϡۨ}8vt"Ӽ# kvXJ[l[ZݙMÀXC3l[ TaVjʻѬ"œ t:(<cZveQTqHi{銀Q埓'ÖiP■mKAIBF
+=Tᅽ(&TS?/؁A:ַОV(@wFa^]o]*99Ri_2vM`Pf{QYH#V7v7Ұq>@~uɘ׆Ax/xB3Ġtyb0nG` EDٍA: PwI7nW2ED<hD&Z Π73&)LD4;7Ѵ?$k@""L&~1ʺf14ʱ|7Os}L1;?{1$w)1}0~7# E5`q&o ow _鴊8Q1GɊ08hWe+\ԉRU?weOSxAU̞3| =WAR
+PtO%Q"1Yה!so%%^z_hn,{?"L5_D6+Sb<gfJ0b_x- ;HW:GMiEeIuvJ]~mQHLKkhbA>}.(h"U]9Ih_V@GZ0C
+pb :L 3tN*N 2!3 Cayn.ɋW`̳}QBCi 8*{57O#aTBUoi0 _^
+ChrU}~rL 1z>..=%GG o EuPPsؘ޸8Pu&;*|i&Pbțh;[|y*cVhҼ(~_AqU2GIQ3`^v=@K'ЇZ#4sJ=:sY sڥbyj S_E܃"@~>86#y[cSŬ#SJGZyvvSя扝pwaT/,
+9'Jkv%%.~o[ 衧RBjSȀ*$'腁pçSu
+ k\띲|FO 68nKzR"?/732:а>eWHU0Oק5 e3Hco>l]02cH9{Z {sO!A,7?ŷ3
+Fj8B&8U$G
+ +@/kb{(7i={l͍݂濦81g(%h/EfMҍt
+)9= _/Z(>lYVgQ#߭:Qbw$zwٮ#U?|Ghz{o$wϜ)|Vh? ZV7%Go/׆E"KӲlp76
+rr(a@6nԌ?}dLgIvqNcaƮkmLcA!hdVwc=憖s_:җsLg>1*4-%&0Ub)Eܬ*b51 ++;<`!qfM*,[/GK+{,>CLR%%c~'EGAG=h䟔8:IDN)W̻AF)ucw'qhXèL@a~6Pc2L"A2bU & 9A#QLO:E9kfKFb93tL$cˬpLz5dp۰>$`.~X=
+߈9
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
+ * Copyright 2011-2016 Twitter, Inc.
+ * Licensed under the MIT license
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",c).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",".dropdown-menu",g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){d.$element.one("mouseup.dismiss.bs.modal",function(b){a(b.target).is(d.$element)&&(d.ignoreBackdropClick=!0)})}),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in"),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$dialog.one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){document===a.target||this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a(document.createElement("div")).addClass("modal-backdrop "+e).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth<a,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",a,b)};c.VERSION="3.3.7",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-m<o.top?"bottom":"right"==h&&k.right+l>o.width?"left":"left"==h&&k.left-l<o.left?"right":h,f.removeClass(n).addClass(h)}var p=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(p,h);var q=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",q).emulateTransitionEnd(c.TRANSITION_DURATION):q()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top+=g,b.left+=h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element&&e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=a(this.$tip),g=a.Event("hide.bs."+this.type);if(this.$element.trigger(g),!g.isDefaultPrevented())return f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=window.SVGElement&&c instanceof window.SVGElement,g=d?{top:0,left:0}:f?null:b.offset(),h={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},i=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,h,i,g)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(void 0===e[a+1]||b<e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){
+this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.7",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e<c&&"top";if("bottom"==this.affixed)return null!=c?!(e+this.unpin<=f.top)&&"bottom":!(e+g<=a-d)&&"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&e<=c?"top":null!=d&&i+j>=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new c;return t.on=function(t,i){var u,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,u=e.indexOf(o)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function S(){ao.event.preventDefault()}function k(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function N(n){for(var t=new _,e=0,r=arguments.length;++e<r;)t[arguments[e]]=w(t);return t.of=function(e,r){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(e,r)}finally{ao.event=u}}},t}function E(n){return ko(n,Co),n}function A(n){return"function"==typeof n?n:function(){return No(n,this)}}function C(n){return"function"==typeof n?n:function(){return Eo(n,this)}}function z(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=ao.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?u:i}function L(n){return n.trim().replace(/\s+/g," ")}function q(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s+/)}function R(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=T(n).map(D);var i=n.length;return"function"==typeof t?r:e}function D(n){var t=q(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",L(i+" "+n))):e.setAttribute("class",L(i.replace(t," ")))}}function P(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function U(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}function j(n){function t(){var t=this.ownerDocument,e=this.namespaceURI;return e===zo&&t.documentElement.namespaceURI===zo?t.createElement(n):t.createElementNS(e,n)}function e(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?e:t}function F(){var n=this.parentNode;n&&n.removeChild(this)}function H(n){return{__data__:n}}function O(n){return function(){return Ao(this,n)}}function I(n){return arguments.length||(n=e),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function Y(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<l;);return o}}function X(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=l(t,co(arguments));r.call(this),this.addEventListener(n,this[o]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var r in this)if(t=r.match(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),l=$;a>0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t<e&&(e=t.t),t=(n=t).n):t=n?n.n=t.n:oa=t.n;return aa=n,e}function Pn(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Un(n,t){var e=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.slice(l,a)),null!=(i=ya[e=n.charAt(++a)])&&(e=n.charAt(++a)),(u=A[e])&&(e=u(t,null==i?"e"===e?" ":"0":i)),o.push(e),l=a+1);return o.push(n.slice(l,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=e(r,n,t,0);if(i!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var u=null!=r.Z&&va!==Hn,o=new(u?Hn:va);return"j"in r?o.setFullYear(r.y,0,r.j):"W"in r||"U"in r?("w"in r||(r.w="W"in r?1:0),o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),u?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var i,u,o,a=0,l=t.length,c=e.length;l>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function $n(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Bn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.U=+r[0],e+r[0].length):-1}function Wn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.W=+r[0],e+r[0].length):-1}function Jn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Gn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.y=Qn(+r[0]),e+r[0].length):-1}function Kn(n,t,e){return/^[+-]\d{4}$/.test(t=t.slice(e,e+5))?(n.Z=-t,e+5):-1}function Qn(n){return n+(n>68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ft(){}function st(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function ht(n,t){n&&wa.hasOwnProperty(n.type)&&wa[n.type](n,t)}function pt(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1],r[2]);t.lineEnd()}function gt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)pt(n[e],t,1);t.polygonEnd()}function vt(){function n(n,t){n*=Yo,t=t*Yo/2+Fo/4;var e=n-r,o=e>=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Uo&&xo(n[1]-t[1])<Uo}function St(n,t){n*=Yo;var e=Math.cos(t*=Yo);kt(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function kt(n,t,e){++Ea,Ca+=(n-Ca)/Ea,za+=(t-za)/Ea,La+=(e-La)/Ea}function Nt(){function n(n,i){n*=Yo;var u=Math.cos(i*=Yo),o=u*Math.cos(n),a=u*Math.sin(n),l=Math.sin(i),c=Math.atan2(Math.sqrt((c=e*l-r*a)*c+(c=r*o-t*l)*c+(c=t*a-e*o)*c),t*o+e*a+r*l);Aa+=c,qa+=c*(t+(t=o)),Ta+=c*(e+(e=a)),Ra+=c*(r+(r=l)),kt(t,e,r)}var t,e,r;ja.point=function(i,u){i*=Yo;var o=Math.cos(u*=Yo);t=o*Math.cos(i),e=o*Math.sin(i),r=Math.sin(u),ja.point=n,kt(t,e,r)}}function Et(){ja.point=St}function At(){function n(n,t){n*=Yo;var e=Math.cos(t*=Yo),o=e*Math.cos(n),a=e*Math.sin(n),l=Math.sin(t),c=i*l-u*a,f=u*o-r*l,s=r*a-i*o,h=Math.sqrt(c*c+f*f+s*s),p=r*o+i*a+u*l,g=h&&-nn(p)/h,v=Math.atan2(h,p);Da+=g*c,Pa+=g*f,Ua+=g*s,Aa+=v,qa+=v*(r+(r=o)),Ta+=v*(i+(i=a)),Ra+=v*(u+(u=l)),kt(r,i,u)}var t,e,r,i,u;ja.point=function(o,a){t=o,e=a,ja.point=n,o*=Yo;var l=Math.cos(a*=Yo);r=l*Math.cos(o),i=l*Math.sin(o),u=Math.sin(a),kt(r,i,u)},ja.lineEnd=function(){n(t,e),ja.lineEnd=Et,ja.point=St}}function Ct(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function zt(){return!0}function Lt(n,t,e,r,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(wt(e,r)){i.lineStart();for(var a=0;t>a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.n=e=n[r],e.p=i,i=e;i.n=e=n[0],e.p=i}}function Tt(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Rt(n,t,e,r){return function(i,u){function o(t,e){var r=i(t,e);n(t=r[0],e=r[1])&&u.point(t,e)}function a(n,t){var e=i(n,t);d.point(e[0],e[1])}function l(){m.point=a,d.lineStart()}function c(){m.point=o,d.lineEnd()}function f(n,t){v.push([n,t]);var e=i(n,t);x.point(e[0],e[1])}function s(){x.lineStart(),v=[]}function h(){f(v[0][0],v[0][1]),x.lineEnd();var n,t=x.clean(),e=M.buffer(),r=e.length;if(v.pop(),g.push(v),v=null,r)if(1&t){n=e[0];var i,r=n.length-1,o=-1;if(r>0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o<r;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else r>1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)<Uo?(n.point(e,r=(r+o)/2>0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)<Uo&&(e-=i*Uo),xo(u-a)<Uo&&(u-=a*Uo),r=Ft(e,r,u,o),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=u,r=o),i=a},lineEnd:function(){n.lineEnd(),e=r=NaN},clean:function(){return 2-t}}}function Ft(n,t,e,r){var i,u,o=Math.sin(n-e);return xo(o)>Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]<t[0]?Fo:-Fo;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Ot(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,o=0;ka.reset();for(var a=0,l=t.length;l>a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)<Uo,C=A||Uo>E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)<Uo?k:N):k<=b[1]&&b[1]<=N:E>Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)<Uo?i>0?0:3:xo(r[0]-e)<Uo?i>0?2:1:xo(r[1]-t)<Uo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){
+r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)<Uo||xo(r-h)<Uo?(r+h)/2:Math.atan2(_,b),E=n(N,k),A=E[0],C=E[1],z=A-t,L=C-e,q=M*z-m*L;(q*q/x>u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)<Uo?ce:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-K(i)*Math.sqrt(n*n+e*e)]},e)}function Ne(n,t){return[n,Math.log(Math.tan(Fo/4+t/2))]}function Ee(n){var t,e=oe(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=u.apply(e,arguments);if(o===e){if(t=null==n){var a=Fo*r(),l=i();u([[l[0]-a,l[1]-a],[l[0]+a,l[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ae(n,t){return[Math.log(Math.tan(Fo/4+t/2)),-n]}function Ce(n){return n[0]}function ze(n){return n[1]}function Le(n){for(var t=n.length,e=[0,1],r=2,i=2;t>i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)<Uo&&xo(r-l.circle.cy)<Uo;)u=l.P,a.unshift(l),je(l),l=u;a.unshift(l),Be(l);for(var c=o;c.circle&&xo(e-c.circle.x)<Uo&&xo(r-c.circle.cy)<Uo;)o=c.N,a.push(c),je(c),c=o;a.push(c),Be(c);var f,s=a.length;for(f=1;s>f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)<Uo&&g-i>Uo?{x:s,y:xo(t-s)<Uo?e:g}:xo(i-g)<Uo&&h-r>Uo?{x:xo(e-g)<Uo?t:h,y:g}:xo(r-h)<Uo&&i-p>Uo?{x:h,y:xo(t-h)<Uo?e:p}:xo(i-p)<Uo&&r-s>Uo?{x:xo(e-p)<Uo?t:s,y:p}:null),u.site,null)),++l)}function Ve(n,t){return t.angle-n.angle}function Xe(){rr(this),this.x=this.y=this.arc=this.site=this.cy=null}function $e(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,i=n.site,u=e.site;if(r!==u){var o=i.x,a=i.y,l=r.x-o,c=r.y-a,f=u.x-o,s=u.y-a,h=2*(l*s-c*f);if(!(h>=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.y<M.y||y.y===M.y&&y.x<=M.x){if(!M.L){m=M.P;break}M=M.L}else{if(!M.R){m=M;break}M=M.R}ll.insert(m,y),m||(al=y)}}}}function Be(n){var t=n.circle;t&&(t.P||(al=t.N),ll.remove(t),fl.push(t),rr(t),n.circle=null)}function We(n){for(var t,e=il,r=Yt(n[0][0],n[0][1],n[1][0],n[1][1]),i=e.length;i--;)t=e[i],(!Je(t,n)||!r(t)||xo(t.a.x-t.b.x)<Uo&&xo(t.a.y-t.b.y)<Uo)&&(t.a=t.b=null,e.splice(i,1))}function Je(n,t){var e=n.b;if(e)return!0;var r,i,u=n.a,o=t[0][0],a=t[1][0],l=t[0][1],c=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,g=s.x,v=s.y,d=(h+g)/2,y=(p+v)/2;if(v===p){if(o>d||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.y<l)return}else u={x:d,y:c};e={x:d,y:l}}}else if(r=(h-g)/(v-p),i=y-r*d,-1>r||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.y<l)return}else u={x:(c-i)/r,y:c};e={x:(l-i)/r,y:l}}else if(v>p){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:r*a+i};e={x:o,y:r*o+i}}return n.a=u,n.b=e,!0}function Ge(n,t){this.l=n,this.r=t,this.a=this.b=null}function Ke(n,t,e,r){var i=new Ge(n,t);return il.push(i),e&&nr(i,n,t,e),r&&nr(i,t,n,r),ul[n.i].edges.push(new tr(i,n,t)),ul[t.i].edges.push(new tr(i,t,n)),i}function Qe(n,t,e){var r=new Ge(n,null);return r.a=t,r.b=e,il.push(r),r}function nr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function tr(n,t,e){var r=n.a,i=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(i.x-r.x,r.y-i.y):Math.atan2(r.x-i.x,i.y-r.y)}function er(){this._=null}function rr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ir(n,t){var e=t,r=t.R,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ur(n,t){var e=t,r=t.L,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function or(n){for(;n.L;)n=n.L;return n}function ar(n,t){var e,r,i,u=n.sort(lr).pop();for(il=[],ul=new Array(n.length),ol=new er,ll=new er;;)if(i=al,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===e&&u.y===r||(ul[u.i]=new Ye(u),He(u),e=u.x,r=u.y),u=n.pop();else{if(!i)break;Fe(i.arc)}t&&(We(t),Ze(t));var o={cells:ul,edges:il};return ol=ll=il=ul=null,o}function lr(n,t){return t.y-n.y||t.x-n.x}function cr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var o=.5*(e+i),a=.5*(r+u),l=t.nodes;l[0]&&pr(n,l[0],e,r,o,a),l[1]&&pr(n,l[1],o,r,i,a),l[2]&&pr(n,l[2],e,a,o,u),l[3]&&pr(n,l[3],o,a,i,u)}}function gr(n,t,e,r,i,u,o){var a,l=1/0;return function c(n,f,s,h,p){if(!(f>u||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?l[0]?(t=l[0].x,function(n){return t(n)+""}):function(){return t}:(t=l.length,function(n){for(var e,r=0;t>r;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Zo,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Zo:0}function Fr(n,t){return n[0]*t[0]+n[1]*t[1]}function Hr(n){var t=Math.sqrt(Fr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Or(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ir(n){return n.length?n.pop()+",":""}function Yr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push("translate(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else(t[0]||t[1])&&e.push("translate("+t+")")}function Zr(n,t,e,r){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i<u;)e[(t=r[i]).i]=t.x(n);return e.join("")}}function Br(n,t){return t=(t-=n=+n)||1/t,function(e){return(e-n)/t}}function Wr(n,t){return t=(t-=n=+n)||1/t,function(e){return Math.max(0,Math.min(1,(e-n)/t))}}function Jr(n){for(var t=n.source,e=n.target,r=Kr(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Gr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Kr(n,t){if(n===t)return n;for(var e=Gr(n),r=Gr(t),i=e.pop(),u=r.pop(),o=null;i===u;)o=i,i=e.pop(),u=r.pop();return o}function Qr(n){n.fixed|=2}function ni(n){n.fixed&=-7}function ti(n){n.fixed|=4,n.px=n.x,n.py=n.y}function ei(n){n.fixed&=-5}function ri(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,l=-1;++l<a;)u=o[l],null!=u&&(ri(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var c=t*e[n.point.index];n.charge+=n.pointCharge=c,r+=c*n.point.x,i+=c*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function ii(n,t){return ao.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=fi,n}function ui(n,t){for(var e=[n];null!=(n=e.pop());)if(t(n),(i=n.children)&&(r=i.length))for(var r,i;--r>=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)e.push(u[o]);for(;null!=(n=r.pop());)t(n)}function ai(n){return n.children}function li(n){return n.value}function ci(n,t){return t.value-n.value}function fi(n){return ao.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function si(n){return n.x}function hi(n){return n.y}function pi(n,t,e){n.y0=t,n.y=e}function gi(n){return ao.range(n.length)}function vi(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function di(n){for(var t,e=1,r=0,i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.r<r.r?Si(r,i=a):Si(r=l,i),o--):(wi(r,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,M=0;for(o=0;c>o;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u<o;)Ci(i[u],t,e,r)}function zi(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var o=t.r+e.r,a=i*i+u*u;o*=o,r*=r;var l=.5+(r-o)/(2*a),c=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+l*i+c*u,e.y=n.y+l*u-c*i}else e.x=n.x+r,e.y=n.y}function Li(n,t){return n.parent==t.parent?1:2}function qi(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,e=n.children;return(t=e.length)?e[t-1]:n.t}function Ri(n,t,e){var r=e/(t.i-n.i);t.c-=r,t.s+=e,n.c+=r,t.z+=e,t.m+=e}function Di(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)i.push(e(n[o-1],n[o])),u.push(r(t[o-1],t[o]));return function(t){var e=ao.bisect(n,t,1,a)-1;return u[e](i[e](t))}}function Wi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++<f;)for(var h=s-1;h>0;h--)o.push(u(c)*h);for(c=0;o[c]<a;c++);for(f=o.length;o[f-1]>l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++o<a;)i.has(u=r[o])||i.set(u,n.push(u));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(u=n,o=0,t={t:"range",a:arguments},e):u},e.rangePoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=(l+c)/2,0):(c-l)/(n.length-1+a);return u=r(l+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=c=Math.round((l+c)/2),0):(c-l)/(n.length-1+a)|0;return u=r(l+Math.round(f*a/2+(c-l-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},e},e.rangeBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=(s-f)/(n.length-a+2*l);return u=r(f+h*l,h),c&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=Math.floor((s-f)/(n.length-a+2*l));return u=r(f+Math.round((s-f-(n.length-a)*h)/2),h),c&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function au(n,t){function u(){var e=0,r=t.length;for(a=[];++e<r;)a[e-1]=ao.quantile(n,e/r);return o}function o(n){return isNaN(n=+n)?void 0:t[ao.bisect(a,n)]}var a;return o.domain=function(t){return arguments.length?(n=t.map(r).filter(i).sort(e),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(e){return e=t.indexOf(e),0>e?[NaN,NaN]:[e>0?a[e-1]:n[0],e<a.length?a[e]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),o=e.length-1,r}var u,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s<h;)i.call(this,l=t[s],s)?f.push([+p.call(this,l,s),+g.call(this,l,s)]):f.length&&(o(),f=[]);return f.length&&o(),c.length?c.join(""):null}var e=Ce,r=ze,i=zt,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?u=n:(u=Tl.get(n)||xu).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function xu(n){return n.length>1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Su(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function ku(n,t){return n.length<4?xu(n):n[1]+Au(n.slice(1,-1),Cu(n,t))}function Nu(n,t){return n.length<3?bu(n):n[0]+Au((n.push(n[0]),n),Cu([n[n.length-2]].concat(n,[n[1]]),t))}function Eu(n,t){return n.length<3?xu(n):n[0]+Au(n,Cu(n,t))}function Au(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],o=t[0],a=o,l=1;if(e&&(r+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],l=2),t.length>1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c<t.length;c++,l++)u=n[l],a=t[c],r+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(e){var f=n[l];r+="Q"+(u[0]+2*a[0]/3)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return r}function Cu(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],o=n[1],a=1,l=n.length;++a<l;)e=u,u=o,o=n[a],r.push([i*(o[0]-e[0]),i*(o[1]-e[1])]);return r}function zu(n){if(n.length<3)return xu(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],o=[i,i,i,(r=n[1])[0]],a=[u,u,u,r[1]],l=[i,",",u,"L",Ru(Pl,o),",",Ru(Pl,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Du(l,o,a);return n.pop(),l.push("L",r),l.join("")}function Lu(n){if(n.length<4)return xu(n);for(var t,e=[],r=-1,i=n.length,u=[0],o=[0];++r<3;)t=n[r],u.push(t[0]),o.push(t[1]);for(e.push(Ru(Pl,u)+","+Ru(Pl,o)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Du(e,u,o);return e.join("")}function qu(n){for(var t,e,r=-1,i=n.length,u=i+4,o=[],a=[];++r<4;)e=n[r%i],o.push(e[0]),a.push(e[1]);for(t=[Ru(Pl,o),",",Ru(Pl,a)],--r;++r<u;)e=n[r%i],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Du(t,o,a);return t.join("")}function Tu(n,t){var e=n.length-1;if(e)for(var r,i,u=n[0][0],o=n[0][1],a=n[e][0]-u,l=n[e][1]-o,c=-1;++c<=e;)r=n[c],i=c/e,r[0]=t*r[0]+(1-t)*(u+i*a),r[1]=t*r[1]+(1-t)*(o+i*l);return zu(n)}function Ru(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Du(n,t,e){n.push("C",Ru(Rl,t),",",Ru(Rl,e),",",Ru(Dl,t),",",Ru(Dl,e),",",Ru(Pl,t),",",Ru(Pl,e))}function Pu(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Uu(n){for(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],o=r[0]=Pu(i,u);++t<e;)r[t]=(o+(o=Pu(i=u,u=n[t+1])))/2;return r[t]=o,r}function ju(n){for(var t,e,r,i,u=[],o=Uu(n),a=-1,l=n.length-1;++a<l;)t=Pu(n[a],n[a+1]),xo(t)<Uo?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]-Io,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Ou(n){function t(t){function l(){v.push("M",a(n(y),s),f,c(n(d.reverse()),s),"Z")}for(var h,p,g,v=[],d=[],y=[],m=-1,M=t.length,x=En(e),b=En(i),_=e===r?function(){
+return p}:En(r),w=i===u?function(){return g}:En(u);++m<M;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),g=+b.call(this,h,m)]),y.push([+_.call(this,h,m),+w.call(this,h,m)])):d.length&&(l(),d=[],y=[]);return d.length&&l(),v.length?v.join(""):null}var e=Ce,r=Ce,i=0,u=ze,o=zt,a=xu,l=a.key,c=a,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(l="function"==typeof n?a=n:(a=Tl.get(n)||xu).key,c=a.reverse||a,f=a.closed?"M":"L",t):l},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Iu(n){return n.radius}function Yu(n){return[n.x,n.y]}function Zu(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]-Io;return[e*Math.cos(r),e*Math.sin(r)]}}function Vu(){return 64}function Xu(){return"circle"}function $u(n){var t=Math.sqrt(n/Fo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Bu(n){return function(){var t,e,r;(t=this[n])&&(r=t[e=t.active])&&(r.timer.c=null,r.timer.t=NaN,--t.count?delete t[e]:delete this[n],t.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Wu(n,t,e){return ko(n,Yl),n.namespace=t,n.id=e,n}function Ju(n,t,e,r){var i=n.id,u=n.namespace;return Y(n,"function"==typeof e?function(n,o,a){n[u][i].tween.set(t,r(e.call(n,n.__data__,o,a)))}:(e=r(e),function(n){n[u][i].tween.set(t,e)}))}function Gu(n){return null==n&&(n=""),function(){this.textContent=n}}function Ku(n){return null==n?"__transition__":"__transition_"+n+"__"}function Qu(n,t,e,r,i){function u(n){var t=v.delay;return f.t=t+l,n>=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]<Kl[u]/i?u-1:u]:[tc,Ki(n,e)[2]]}return r.invert=function(t){return io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,io(+e+1),t).length}var u=r.domain(),o=Yi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),r.domain(Xi(u,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&r>e&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(r=n[u])&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<o;)if(null!=(r=t.call(n,n[u],u))&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(e=+n[o])&&(r+=e);else for(;++o<u;)i(e=+t.call(n,n[o],o))&&(r+=e);return r},ao.mean=function(n,t){var e,u=0,o=n.length,a=-1,l=o;if(1===arguments.length)for(;++a<o;)i(e=r(n[a]))?u+=e:--l;else for(;++a<o;)i(e=r(t.call(n,n[a],a)))?u+=e:--l;return l?u/l:void 0},ao.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,l=-1;if(1===arguments.length)for(;++l<a;)i(u=r(n[l]))&&o.push(u);else for(;++l<a;)i(u=r(t.call(n,n[l],l)))&&o.push(u);return o.length?ao.quantile(o.sort(e),.5):void 0},ao.variance=function(n,t){var e,u,o=n.length,a=0,l=0,c=-1,f=0;if(1===arguments.length)for(;++c<o;)i(e=r(n[c]))&&(u=e-a,a+=u/++f,l+=u*(e-a));else for(;++c<o;)i(e=r(t.call(n,n[c],c)))&&(u=e-a,a+=u/++f,l+=u*(e-a));return f>1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t<e;)for(var i,u=-1,a=r[t]=new Array(i);++u<i;)a[u]=n[u][t];return r},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var e in n)t.push(e);return t},ao.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},ao.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},ao.merge=function(n){for(var t,e,r,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(e=new Array(o);--i>=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)<t;)i.push(r/u);return i},ao.map=function(n,t){var e=new c;if(n instanceof c)n.forEach(function(n,t){e.set(n,t)});else if(Array.isArray(n)){var r,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)e.set(i,n[i]);else for(;++i<u;)e.set(t.call(n,r=n[i],i),r)}else for(var o in n)e.set(o,n[o]);return e};var bo="__proto__",_o="\x00";l(c,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:g,values:function(){var n=[];for(var t in this._)n.push(this._[t]);return n},entries:function(){var n=[];for(var t in this._)n.push({key:s(t),value:this._[t]});return n},size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t),this._[t])}}),ao.nest=function(){function n(t,o,a){if(a>=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p<g;)(h=d.get(l=v(f=o[p])))?h.push(f):d.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,a))}):(f={},s=function(e,r){f[e]=n(t,r,a)}),d.forEach(s),f}function t(n,e){if(e>=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=M(n,t,t[e]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new _,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=w(n);return n},_.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var l=-1,c=r.length;++l<c;)(i=r[l])?(t.push(e=n.call(i,i.__data__,l,o)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return E(u)},Co.selectAll=function(n){var t,e,r=[];n=C(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,l=o.length;++a<l;)(e=o[a])&&(r.push(t=co(n.call(e,e.__data__,a,i))),t.parentNode=e);return E(r)};var zo="http://www.w3.org/1999/xhtml",Lo={svg:"http://www.w3.org/2000/svg",xhtml:zo,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};ao.ns={prefix:Lo,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!q(n[i]).test(t))return!1;return!0}for(t in n)this.each(R(t,n[t]));return this}return this.each(R(n,t))},Co.style=function(n,e,r){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++r<o;)(i=n[r])&&(y.has(d=t.call(i,i.__data__,r))?v[r]=i:y.set(d,i),m[r]=d);for(r=-1;++r<s;)(i=y.get(d=t.call(e,u=e[r],r)))?i!==!0&&(p[r]=i,i.__data__=u):g[r]=H(u),y.set(d,!0);for(r=-1;++r<o;)r in m&&y.get(m[r])!==!0&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],u=e[r],i?(i.__data__=u,p[r]=i):g[r]=H(u);for(;s>r;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++u<o;)(i=r[u])&&(n[u]=i.__data__);return n}var a=Z([]),l=E([]),f=E([]);if("function"==typeof n)for(;++u<o;)e(r=this[u],n.call(r,r.parentNode.__data__,u));else for(;++u<o;)e(r=this[u],n);return l.enter=function(){return a},l.exit=function(){return f},l},Co.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Co.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Co.each=function(n){return Y(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Co.call=function(n){var t=co(arguments);return n.apply(t[0]=this,t),this},Co.empty=function(){return!this.node()},Co.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++a<l;){r=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var c=-1,f=i.length;++c<f;)(u=i[c])?(t.push(r[c]=e=n.call(i.parentNode,u.__data__,c,a)),e.__data__=u.__data__):t.push(null)}return E(o)},qo.insert=function(n,t){return arguments.length<2&&(t=V(this)),Co.insert.call(this,n,t)},ao.select=function(t){var e;return"string"==typeof t?(e=[No(t,fo)],e.parentNode=fo.documentElement):(e=[t],e.parentNode=n(t)),E([e])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=co(Eo(n,fo)),t.parentNode=fo.documentElement):(t=co(n),t.parentNode=null),E([t])},Co.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}f=e+2;var r=n.charCodeAt(e+1);return 13===r?(i=!0,10===n.charCodeAt(e+2)&&++f):10===r&&(i=!0),n.slice(t+1,e).replace(/""/g,'"')}for(;c>f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
+shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++r<i;)ht(e[r].geometry,t)}},wa={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){pt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)pt(e[r],t,0)},Polygon:function(n,t){gt(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)gt(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,i=e.length;++r<i;)ht(e[r],t)}};ao.geo.area=function(n){return Sa=0,ao.geo.stream(n,Na),Sa};var Sa,ka=new ft,Na={sphere:function(){Sa+=4*Fo},point:b,lineStart:b,lineEnd:b,polygonStart:function(){ka.reset(),Na.lineStart=vt},polygonEnd:function(){var n=2*ka;Sa+=0>n?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,p,g,v,d,y,m,M,x,b={point:n,lineStart:e,lineEnd:r,polygonStart:function(){b.point=i,b.lineStart=u,b.lineEnd=o,m=0,Na.polygonStart()},polygonEnd:function(){Na.polygonEnd(),b.point=n,b.lineStart=e,b.lineEnd=r,0>ka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t<f.length-h;++t)p.push(n[a[f[t]][2]]);return p}var e=Ce,r=ze;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},ao.geom.polygon=function(n){return ko(n,rl),n};var rl=ao.geom.polygon.prototype=[];rl.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],i=0;++t<e;)n=r,r=this[t],i+=n[1]*r[0]-n[0]*r[1];return.5*i},rl.centroid=function(n){var t,e,r=-1,i=this.length,u=0,o=0,a=this[i-1];for(arguments.length||(n=-1/(6*this.area()));++r<i;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],u+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[u*n,o*n]},rl.clip=function(n){for(var t,e,r,i,u,o,a=De(n),l=-1,c=this.length-De(this),f=this[c-1];++l<c;){for(t=n.slice(),n.length=0,i=this[l],u=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Te(o,f,i)?(Te(u,f,i)||n.push(Re(u,o,f,i)),n.push(o)):Te(u,f,i)&&n.push(Re(u,o,f,i)),u=o;a&&n.push(n[0]),f=i}return n};var il,ul,ol,al,ll,cl=[],fl=[];Ye.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(Ve),t.length},tr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},er.prototype={insert:function(n,t){var e,r,i;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=or(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(i=r.R,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.R&&(ir(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ur(this,r))):(i=r.L,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.L&&(ur(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ir(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,i=n.U,u=n.L,o=n.R;if(e=u?o?or(o):u:o,i?i.L===n?i.L=e:i.R=e:this._=e,u&&o?(r=e.C,e.C=n.C,e.L=u,u.U=e,e!==o?(i=e.U,e.U=n.U,n=e.R,i.L=n,e.R=o,o.U=e):(e.U=i,i=e,n=e.R)):(r=n.C,n=e),n&&(n.U=i),!r){if(n&&n.C)return void(n.C=!1);do{if(n===this._)break;if(n===i.L){if(t=i.R,t.C&&(t.C=!1,i.C=!0,ir(this,i),t=i.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ur(this,t),t=i.R),t.C=i.C,i.C=t.R.C=!1,ir(this,i),n=this._;break}}else if(t=i.L,t.C&&(t.C=!1,i.C=!0,ur(this,i),t=i.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,ir(this,t),t=i.L),t.C=i.C,i.C=t.L.C=!1,ur(this,i),n=this._;break}t.C=!0,n=i,i=i.U}while(!n.C);n&&(n.C=!1)}}},ao.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],i=a[0][1],u=a[1][0],o=a[1][1];return ar(e(n),a).cells.forEach(function(e,a){var l=e.edges,c=e.site,f=t[a]=l.length?l.map(function(n){var t=n.start();return[t.x,t.y]}):c.x>=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l<c;)i=f,u=s,f=a[l].edge,s=f.l===o?f.r:f.l,r<u.i&&r<s.i&&cr(o,u,s)<0&&t.push([n[r],n[u.i],n[s.i]])}),t},t.x=function(n){return arguments.length?(u=En(r=n),t):r},t.y=function(n){return arguments.length?(o=En(i=n),t):i},t.clipExtent=function(n){return arguments.length?(a=null==n?sl:n,t):a===sl?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===sl?null:a&&a[1]},t)};var sl=[[-1e6,-1e6],[1e6,1e6]];ao.geom.delaunay=function(n){return ao.geom.voronoi().triangles(n)},ao.geom.quadtree=function(n,t,e,r,i){function u(n){function u(n,t,e,r,i,u,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var l=n.x,f=n.y;if(null!=l)if(xo(l-e)+xo(f-r)<.01)c(n,t,e,r,i,u,o,a);else{var s=n.point;n.x=n.y=n.point=null,c(n,s,l,f,i,u,o,a),c(n,t,e,r,i,u,o,a)}else n.x=e,n.y=r,n.point=t}else c(n,t,e,r,i,u,o,a)}function c(n,t,e,r,i,o,a,l){var c=.5*(i+a),f=.5*(o+l),s=e>=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.x<v&&(v=f.x),f.y<d&&(d=f.y),f.x>y&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p<g;)u(k,n[p],s[p],h[p],v,d,y,m);--p}else n.forEach(k.add);return s=h=n=f=null,k}var o,a=Ce,l=ze;return(o=arguments.length)?(a=fr,l=sr,3===o&&(i=e,r=t,e=t=0),u(n)):(u.x=function(n){return arguments.length?(a=n,u):a},u.y=function(n){return arguments.length?(l=n,u):l},u.extent=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,e],[r,i]]},u.size=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=e=0,r=+n[0],i=+n[1]),u):null==t?null:[r-t,i-e]},u)},ao.interpolateRgb=vr,ao.interpolateObject=dr,ao.interpolateNumber=yr,ao.interpolateString=mr;var hl=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pl=new RegExp(hl.source,"g");ao.interpolate=Mr,ao.interpolators=[function(n,t){var e=typeof t;return("string"===e?ua.has(t.toLowerCase())||/^(#|rgb\(|hsl\()/i.test(t)?vr:mr:t instanceof an?vr:Array.isArray(t)?xr:"object"===e&&isNaN(t)?dr:yr)(n,t)}],ao.interpolateArray=xr;var gl=function(){return m},vl=ao.map({linear:gl,poly:Er,quad:function(){return Sr},cubic:function(){return kr},sin:function(){return Ar},exp:function(){return Cr},circle:function(){return zr},elastic:Lr,back:qr,bounce:function(){return Tr}}),dl=ao.map({"in":m,out:_r,"in-out":wr,"out-in":function(n){return wr(_r(n))}});ao.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Jr(n[e]));return t}},ao.layout.chord=function(){function n(){var n,c,s,h,p,g={},v=[],d=ao.range(u),y=[];for(e=[],r=[],n=0,h=-1;++h<u;){for(c=0,p=-1;++p<u;)c+=i[h][p];v.push(c),y.push(ao.range(u)),n+=c}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&y.forEach(function(n,t){n.sort(function(n,e){return a(i[t][n],i[t][e])})}),n=(Ho-f*u)/n,c=0,h=-1;++h<u;){for(s=c,p=-1;++p<u;){var m=d[h],M=y[m][p],x=i[m][M],b=c,_=c+=x*n;g[m+"-"+M]={index:m,subindex:M,startAngle:b,endAngle:_,value:x}}r[m]={index:m,startAngle:s,endAngle:c,value:v[m]},c+=f}for(h=-1;++h<u;)for(p=h-1;++p<u;){var w=g[h+"-"+p],S=g[p+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}l&&t()}function t(){e.sort(function(n,t){return l((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,i,u,o,a,l,c={},f=0;return c.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,e=r=null,c):i},c.padding=function(n){return arguments.length?(f=n,e=r=null,c):f},c.sortGroups=function(n){return arguments.length?(o=n,e=r=null,c):o},c.sortSubgroups=function(n){return arguments.length?(a=n,e=null,c):a},c.sortChords=function(n){return arguments.length?(l=n,e&&t(),c):l},c.chords=function(){return e||n(),e},c.groups=function(){return r||n(),r},c},ao.layout.force=function(){function n(n){return function(t,e,r,i){if(t.point!==n){var u=t.cx-n.x,o=t.cy-n.y,a=i-e,l=u*u+o*o;if(l>a*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++l<f;)if(!isNaN(o=a[l][n]))return o;return Math.random()*r}var t,e,r,i=M.length,c=x.length,s=f[0],v=f[1];for(t=0;i>t;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++c<o;)n(a=u[c],e,l=a.value*r,i),e+=l}}function t(n){var e=n.children,r=0;if(e&&(i=e.length))for(var i,u=-1;++u<i;)r=Math.max(r,t(e[u]));return 1+r}function e(e,u){var o=r.call(this,e,u);return n(o[0],0,i[0],i[1]/t(o[0])),o}var r=ao.layout.hierarchy(),i=[1,1];return e.size=function(n){return arguments.length?(i=n,e):i},ii(e,r)},ao.layout.pie=function(){function n(o){var a,l=o.length,c=o.map(function(e,r){return+t.call(n,e,r)}),f=+("function"==typeof r?r.apply(this,arguments):r),s=("function"==typeof i?i.apply(this,arguments):i)-f,h=Math.min(Math.abs(s)/l,+("function"==typeof u?u.apply(this,arguments):u)),p=h*(0>s?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u<p;)o=l[u]=[],o.dx=s[u+1]-(o.x=s[u]),o.y=0;if(p>0)for(u=-1;++u<h;)a=c[u],a>=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.x<p.x&&(p=n),n.x>g.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++i<u;)r=(e=n[i]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(u>e&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0;
+if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++u<o;)i=n[u],i.x=a,i.y=c,i.dy=f,a+=i.dx=Math.min(e.x+e.dx-a,f?l(i.area/f):0);i.z=!0,i.dx+=e.x+e.dx-a,e.y+=f,e.dy-=f}else{for((r||f>e.dx)&&(f=e.dx);++u<o;)i=n[u],i.x=a,i.y=c,i.dx=f,c+=i.dy=Math.min(e.y+e.dy-c,f?l(i.area/f):0);i.z=!1,i.dy+=e.y+e.dy-c,e.x+=f,e.dx-=f}}function u(r){var i=o||a(r),u=i[0];return u.x=u.y=0,u.value?(u.dx=c[0],u.dy=c[1]):u.dx=u.dy=0,o&&a.revalue(u),n([u],u.dx*u.dy/u.value),(o?e:t)(u),h&&(o=i),i}var o,a=ao.layout.hierarchy(),l=Math.round,c=[1,1],f=null,s=Oi,h=!1,p="squarify",g=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(c=n,u):c},u.padding=function(n){function t(t){var e=n.call(u,t,t.depth);return null==e?Oi(t):Ii(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return Ii(t,n)}if(!arguments.length)return f;var r;return s=null==(f=n)?Oi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,u},u.round=function(n){return arguments.length?(l=n?Math.round:Number,u):l!=Number},u.sticky=function(n){return arguments.length?(h=n,o=null,u):h},u.ratio=function(n){return arguments.length?(g=n,u):g},u.mode=function(n){return arguments.length?(p=n+"",u):p},ii(u,a)},ao.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++a<l;){u.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(e=c[f])&&Qu(e,f,i,r,o),t.push(e)}return Wu(u,i,r)},Co.interrupt=function(n){return this.each(null==n?Il:Bu(Ku(n)))};var Hl,Ol,Il=Bu(Ku()),Yl=[],Zl=0;Yl.call=Co.call,Yl.empty=Co.empty,Yl.node=Co.node,Yl.size=Co.size,ao.transition=function(n,t){return n&&n.transition?Hl?n.transition(t):n:ao.selection().transition(n)},ao.transition.prototype=Yl,Yl.select=function(n){var t,e,r,i=this.id,u=this.namespace,o=[];n=A(n);for(var a=-1,l=this.length;++a<l;){o.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(r=c[f])&&(e=n.call(r,r.__data__,f,a))?("__data__"in r&&(e.__data__=r.__data__),Qu(e,f,u,i,r[u][i]),t.push(e)):t.push(null)}return Wu(o,u,i)},Yl.selectAll=function(n){var t,e,r,i,u,o=this.id,a=this.namespace,l=[];n=C(n);for(var c=-1,f=this.length;++c<f;)for(var s=this[c],h=-1,p=s.length;++h<p;)if(r=s[h]){u=r[a][o],e=n.call(r,r.__data__,h,c),l.push(t=[]);for(var g=-1,v=e.length;++g<v;)(i=e[g])&&Qu(i,g,a,o,u),t.push(i)}return Wu(l,a,o)},Yl.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]<M[0])],L[1]=h[+(n[1]<M[1])]):M=null),E&&y(n,c,0)&&(r(k),t=!0),A&&y(n,f,1)&&(i(k),t=!0),t&&(e(k),w({type:"brush",mode:C?"move":"resize"}))}function y(n,t,e){var r,i,u=Zi(t),l=u[0],c=u[1],f=L[e],v=e?h:s,d=v[1]-v[0];return C&&(l-=f,c-=d+f),r=(e?g:p)?Math.max(l,Math.min(c,n[e])):n[e],C?i=(r+=f)+d:(M&&(f=Math.max(l,Math.min(c,2*M[e]-r))),r>f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();/*!
+
+Holder - client side image placeholders
+Version 2.7.1+6hydf
+© 2015 Ivan Malopinsky - http://imsky.co
+
+Site: http://holderjs.com
+Issues: https://github.com/imsky/holder/issues
+License: http://opensource.org/licenses/MIT
+
+*/
+!function(a){if(a.document){var b=a.document;b.querySelectorAll||(b.querySelectorAll=function(c){var d,e=b.createElement("style"),f=[];for(b.documentElement.firstChild.appendChild(e),b._qsa=[],e.styleSheet.cssText=c+"{x-qsa:expression(document._qsa && document._qsa.push(this))}",a.scrollBy(0,0),e.parentNode.removeChild(e);b._qsa.length;)d=b._qsa.shift(),d.style.removeAttribute("x-qsa"),f.push(d);return b._qsa=null,f}),b.querySelector||(b.querySelector=function(a){var c=b.querySelectorAll(a);return c.length?c[0]:null}),b.getElementsByClassName||(b.getElementsByClassName=function(a){return a=String(a).replace(/^|\s+/g,"."),b.querySelectorAll(a)}),Object.keys||(Object.keys=function(a){if(a!==Object(a))throw TypeError("Object.keys called on non-object");var b,c=[];for(b in a)Object.prototype.hasOwnProperty.call(a,b)&&c.push(b);return c}),function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a.atob=a.atob||function(a){a=String(a);var c,d=0,e=[],f=0,g=0;if(a=a.replace(/\s/g,""),a.length%4===0&&(a=a.replace(/=+$/,"")),a.length%4===1)throw Error("InvalidCharacterError");if(/[^+/0-9A-Za-z]/.test(a))throw Error("InvalidCharacterError");for(;d<a.length;)c=b.indexOf(a.charAt(d)),f=f<<6|c,g+=6,24===g&&(e.push(String.fromCharCode(f>>16&255)),e.push(String.fromCharCode(f>>8&255)),e.push(String.fromCharCode(255&f)),g=0,f=0),d+=1;return 12===g?(f>>=4,e.push(String.fromCharCode(255&f))):18===g&&(f>>=2,e.push(String.fromCharCode(f>>8&255)),e.push(String.fromCharCode(255&f))),e.join("")},a.btoa=a.btoa||function(a){a=String(a);var c,d,e,f,g,h,i,j=0,k=[];if(/[^\x00-\xFF]/.test(a))throw Error("InvalidCharacterError");for(;j<a.length;)c=a.charCodeAt(j++),d=a.charCodeAt(j++),e=a.charCodeAt(j++),f=c>>2,g=(3&c)<<4|d>>4,h=(15&d)<<2|e>>6,i=63&e,j===a.length+2?(h=64,i=64):j===a.length+1&&(i=64),k.push(b.charAt(f),b.charAt(g),b.charAt(h),b.charAt(i));return k.join("")}}(a),Object.prototype.hasOwnProperty||(Object.prototype.hasOwnProperty=function(a){var b=this.__proto__||this.constructor.prototype;return a in this&&(!(a in b)||b[a]!==this[a])}),function(){if("performance"in a==!1&&(a.performance={}),Date.now=Date.now||function(){return(new Date).getTime()},"now"in a.performance==!1){var b=Date.now();performance.timing&&performance.timing.navigationStart&&(b=performance.timing.navigationStart),a.performance.now=function(){return Date.now()-b}}}(),a.requestAnimationFrame||(a.webkitRequestAnimationFrame?!function(a){a.requestAnimationFrame=function(b){return webkitRequestAnimationFrame(function(){b(a.performance.now())})},a.cancelAnimationFrame=webkitCancelAnimationFrame}(a):a.mozRequestAnimationFrame?!function(a){a.requestAnimationFrame=function(b){return mozRequestAnimationFrame(function(){b(a.performance.now())})},a.cancelAnimationFrame=mozCancelAnimationFrame}(a):!function(a){a.requestAnimationFrame=function(b){return a.setTimeout(b,1e3/60)},a.cancelAnimationFrame=a.clearTimeout}(a))}}(this),function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):"object"==typeof exports?exports.Holder=b():a.Holder=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){(function(b){function d(a,b,c,d){var f=e(c.substr(c.lastIndexOf(a.domain)),a);f&&h({mode:null,el:d,flags:f,engineSettings:b})}function e(a,b){var c={theme:B(J.settings.themes.gray,null),stylesheets:b.stylesheets,instanceOptions:b};return a.match(/([\d]+p?)x([\d]+p?)(?:\?|$)/)?f(a,c):g(a,c)}function f(a,b){var c=a.split("?"),d=c[0].split("/");b.holderURL=a;var e=d[1],f=e.match(/([\d]+p?)x([\d]+p?)/);if(!f)return!1;if(b.fluid=-1!==e.indexOf("p"),b.dimensions={width:f[1].replace("p","%"),height:f[2].replace("p","%")},2===c.length){var g=A.parse(c[1]);if(g.bg&&(b.theme.background=(-1===g.bg.indexOf("#")?"#":"")+g.bg),g.fg&&(b.theme.foreground=(-1===g.fg.indexOf("#")?"#":"")+g.fg),g.theme&&b.instanceOptions.themes.hasOwnProperty(g.theme)&&(b.theme=B(b.instanceOptions.themes[g.theme],null)),g.text&&(b.text=g.text),g.textmode&&(b.textmode=g.textmode),g.size&&(b.size=g.size),g.font&&(b.font=g.font),g.align&&(b.align=g.align),b.nowrap=z.truthy(g.nowrap),b.auto=z.truthy(g.auto),z.truthy(g.random)){J.vars.cache.themeKeys=J.vars.cache.themeKeys||Object.keys(b.instanceOptions.themes);var h=J.vars.cache.themeKeys[0|Math.random()*J.vars.cache.themeKeys.length];b.theme=B(b.instanceOptions.themes[h],null)}}return b}function g(a,b){var c=!1,d=String.fromCharCode(11),e=a.replace(/([^\\])\//g,"$1"+d).split(d),f=/%[0-9a-f]{2}/gi,g=b.instanceOptions;b.holderURL=[];for(var h=e.length,i=0;h>i;i++){var j=e[i];if(j.match(f))try{j=decodeURIComponent(j)}catch(k){j=e[i]}var l=!1;if(J.flags.dimensions.match(j))c=!0,b.dimensions=J.flags.dimensions.output(j),l=!0;else if(J.flags.fluid.match(j))c=!0,b.dimensions=J.flags.fluid.output(j),b.fluid=!0,l=!0;else if(J.flags.textmode.match(j))b.textmode=J.flags.textmode.output(j),l=!0;else if(J.flags.colors.match(j)){var m=J.flags.colors.output(j);b.theme=B(b.theme,m),l=!0}else if(g.themes[j])g.themes.hasOwnProperty(j)&&(b.theme=B(g.themes[j],null)),l=!0;else if(J.flags.font.match(j))b.font=J.flags.font.output(j),l=!0;else if(J.flags.auto.match(j))b.auto=!0,l=!0;else if(J.flags.text.match(j))b.text=J.flags.text.output(j),l=!0;else if(J.flags.size.match(j))b.size=J.flags.size.output(j),l=!0;else if(J.flags.random.match(j)){null==J.vars.cache.themeKeys&&(J.vars.cache.themeKeys=Object.keys(g.themes));var n=J.vars.cache.themeKeys[0|Math.random()*J.vars.cache.themeKeys.length];b.theme=B(g.themes[n],null),l=!0}l&&b.holderURL.push(j)}return b.holderURL.unshift(g.domain),b.holderURL=b.holderURL.join("/"),c?b:!1}function h(a){var b=a.mode,c=a.el,d=a.flags,e=a.engineSettings,f=d.dimensions,g=d.theme,h=f.width+"x"+f.height;if(b=null==b?d.fluid?"fluid":"image":b,null!=d.text&&(g.text=d.text,"object"===c.nodeName.toLowerCase())){for(var j=g.text.split("\\n"),k=0;k<j.length;k++)j[k]=z.encodeHtmlEntity(j[k]);g.text=j.join("\\n")}var n=d.holderURL,o=B(e,null);if(d.font&&(g.font=d.font,!o.noFontFallback&&"img"===c.nodeName.toLowerCase()&&J.setup.supportsCanvas&&"svg"===o.renderer&&(o=B(o,{renderer:"canvas"}))),d.font&&"canvas"==o.renderer&&(o.reRender=!0),"background"==b)null==c.getAttribute("data-background-src")&&r(c,{"data-background-src":n});else{var p={};p[J.vars.dataAttr]=n,r(c,p)}d.theme=g,c.holderData={flags:d,engineSettings:o},("image"==b||"fluid"==b)&&r(c,{alt:g.text?g.text+" ["+h+"]":h});var q={mode:b,el:c,holderSettings:{dimensions:f,theme:g,flags:d},engineSettings:o};"image"==b?("html"!=o.renderer&&d.auto||(c.style.width=f.width+"px",c.style.height=f.height+"px"),"html"==o.renderer?c.style.backgroundColor=g.background:(i(q),"exact"==d.textmode&&(c.holderData.resizeUpdate=!0,J.vars.resizableImages.push(c),l(c)))):"background"==b&&"html"!=o.renderer?i(q):"fluid"==b&&(c.holderData.resizeUpdate=!0,"%"==f.height.slice(-1)?c.style.height=f.height:null!=d.auto&&d.auto||(c.style.height=f.height+"px"),"%"==f.width.slice(-1)?c.style.width=f.width:null!=d.auto&&d.auto||(c.style.width=f.width+"px"),("inline"==c.style.display||""===c.style.display||"none"==c.style.display)&&(c.style.display="block"),m(c),"html"==o.renderer?c.style.backgroundColor=g.background:(J.vars.resizableImages.push(c),l(c)))}function i(a){function c(){var b=null;switch(h.renderer){case"canvas":b=L(k,a);break;case"svg":b=M(k,a);break;default:throw"Holder: invalid renderer: "+h.renderer}return b}var d=null,e=a.mode,f=a.holderSettings,g=a.el,h=a.engineSettings;switch(h.renderer){case"svg":if(!J.setup.supportsSVG)return;break;case"canvas":if(!J.setup.supportsCanvas)return;break;default:return}var i={width:f.dimensions.width,height:f.dimensions.height,theme:f.theme,flags:f.flags},k=j(i);if(d=c(),null==d)throw"Holder: couldn't render placeholder";"background"==e?(g.style.backgroundImage="url("+d+")",g.style.backgroundSize=i.width+"px "+i.height+"px"):("img"===g.nodeName.toLowerCase()?r(g,{src:d}):"object"===g.nodeName.toLowerCase()&&(r(g,{data:d}),r(g,{type:"image/svg+xml"})),h.reRender&&b.setTimeout(function(){var a=c();if(null==a)throw"Holder: couldn't render placeholder";"img"===g.nodeName.toLowerCase()?r(g,{src:a}):"object"===g.nodeName.toLowerCase()&&(r(g,{data:a}),r(g,{type:"image/svg+xml"}))},100)),r(g,{"data-holder-rendered":!0})}function j(a){function b(a,b,c,d){b.width=c,b.height=d,a.width=Math.max(a.width,b.width),a.height+=b.height}var c=J.defaults.size;switch(parseFloat(a.theme.size)?c=a.theme.size:parseFloat(a.flags.size)&&(c=a.flags.size),a.font={family:a.theme.font?a.theme.font:"Arial, Helvetica, Open Sans, sans-serif",size:k(a.width,a.height,c),units:a.theme.units?a.theme.units:J.defaults.units,weight:a.theme.fontweight?a.theme.fontweight:"bold"},a.text=a.theme.text||Math.floor(a.width)+"x"+Math.floor(a.height),a.noWrap=a.theme.nowrap||a.flags.nowrap,a.align=a.theme.align||a.flags.align||"center",a.flags.textmode){case"literal":a.text=a.flags.dimensions.width+"x"+a.flags.dimensions.height;break;case"exact":if(!a.flags.exactDimensions)break;a.text=Math.floor(a.flags.exactDimensions.width)+"x"+Math.floor(a.flags.exactDimensions.height)}var d=new y({width:a.width,height:a.height}),e=d.Shape,f=new e.Rect("holderBg",{fill:a.theme.background});f.resize(a.width,a.height),d.root.add(f);var g=new e.Group("holderTextGroup",{text:a.text,align:a.align,font:a.font,fill:a.theme.foreground});g.moveTo(null,null,1),d.root.add(g);var h=g.textPositionData=K(d);if(!h)throw"Holder: staging fallback not supported yet.";g.properties.leading=h.boundingBox.height;var i=null,j=null,l=a.width*J.setup.lineWrapRatio,m=l;if(h.lineCount>1){var n,o=0,p=0,q=0;j=new e.Group("line"+q),("left"===a.align||"right"===a.align)&&(m=a.width*(1-2*(1-J.setup.lineWrapRatio)));for(var r=0;r<h.words.length;r++){var s=h.words[r];i=new e.Text(s.text);var t="\\n"==s.text;!a.noWrap&&(o+s.width>=m||t===!0)&&(b(g,j,o,g.properties.leading),g.add(j),o=0,p+=g.properties.leading,q+=1,j=new e.Group("line"+q),j.y=p),t!==!0&&(i.moveTo(o,0),o+=h.spaceWidth+s.width,j.add(i))}if(b(g,j,o,g.properties.leading),g.add(j),"left"===a.align)g.moveTo(a.width-l,null,null);else if("right"===a.align){for(n in g.children)j=g.children[n],j.moveTo(a.width-j.width,null,null);g.moveTo(0-(a.width-l),null,null)}else{for(n in g.children)j=g.children[n],j.moveTo((g.width-j.width)/2,null,null);g.moveTo((a.width-g.width)/2,null,null)}g.moveTo(null,(a.height-g.height)/2,null),(a.height-g.height)/2<0&&g.moveTo(null,0,null)}else i=new e.Text(a.text),j=new e.Group("line0"),j.add(i),g.add(j),"left"===a.align?g.moveTo(a.width-l,null,null):"right"===a.align?g.moveTo(0-(a.width-l),null,null):g.moveTo((a.width-h.boundingBox.width)/2,null,null),g.moveTo(null,(a.height-h.boundingBox.height)/2,null);return d}function k(a,b,c){var d=parseInt(a,10),e=parseInt(b,10),f=Math.max(d,e),g=Math.min(d,e),h=.8*Math.min(g,f*J.defaults.scale);return Math.round(Math.max(c,h))}function l(a){var b;b=null==a||null==a.nodeType?J.vars.resizableImages:[a];for(var c=0,d=b.length;d>c;c++){var e=b[c];if(e.holderData){var f=e.holderData.flags,g=D(e);if(g){if(!e.holderData.resizeUpdate)continue;if(f.fluid&&f.auto){var h=e.holderData.fluidConfig;switch(h.mode){case"width":g.height=g.width/h.ratio;break;case"height":g.width=g.height*h.ratio}}var j={mode:"image",holderSettings:{dimensions:g,theme:f.theme,flags:f},el:e,engineSettings:e.holderData.engineSettings};"exact"==f.textmode&&(f.exactDimensions=g,j.holderSettings.dimensions=f.dimensions),i(j)}else p(e)}}}function m(a){if(a.holderData){var b=D(a);if(b){var c=a.holderData.flags,d={fluidHeight:"%"==c.dimensions.height.slice(-1),fluidWidth:"%"==c.dimensions.width.slice(-1),mode:null,initialDimensions:b};d.fluidWidth&&!d.fluidHeight?(d.mode="width",d.ratio=d.initialDimensions.width/parseFloat(c.dimensions.height)):!d.fluidWidth&&d.fluidHeight&&(d.mode="height",d.ratio=parseFloat(c.dimensions.width)/d.initialDimensions.height),a.holderData.fluidConfig=d}else p(a)}}function n(){for(var a,c=[],d=Object.keys(J.vars.invisibleImages),e=0,f=d.length;f>e;e++)a=J.vars.invisibleImages[d[e]],D(a)&&"img"==a.nodeName.toLowerCase()&&(c.push(a),delete J.vars.invisibleImages[d[e]]);c.length&&I.run({images:c}),b.requestAnimationFrame(n)}function o(){J.vars.visibilityCheckStarted||(b.requestAnimationFrame(n),J.vars.visibilityCheckStarted=!0)}function p(a){a.holderData.invisibleId||(J.vars.invisibleId+=1,J.vars.invisibleImages["i"+J.vars.invisibleId]=a,a.holderData.invisibleId=J.vars.invisibleId)}function q(a,b){return null==b?document.createElement(a):document.createElementNS(b,a)}function r(a,b){for(var c in b)a.setAttribute(c,b[c])}function s(a,b,c){var d,e;null==a?(a=q("svg",E),d=q("defs",E),e=q("style",E),r(e,{type:"text/css"}),d.appendChild(e),a.appendChild(d)):e=a.querySelector("style"),a.webkitMatchesSelector&&a.setAttribute("xmlns",E);for(var f=0;f<a.childNodes.length;f++)a.childNodes[f].nodeType===F&&a.removeChild(a.childNodes[f]);for(;e.childNodes.length;)e.removeChild(e.childNodes[0]);return r(a,{width:b,height:c,viewBox:"0 0 "+b+" "+c,preserveAspectRatio:"none"}),a}function t(a,c){if(b.XMLSerializer){var d=new XMLSerializer,e="",f=c.stylesheets;if(c.svgXMLStylesheet){for(var g=u(),h=f.length-1;h>=0;h--){var i=g.createProcessingInstruction("xml-stylesheet",'href="'+f[h]+'" rel="stylesheet"');g.insertBefore(i,g.firstChild)}g.removeChild(g.documentElement),e=d.serializeToString(g)}var j=d.serializeToString(a);return j=j.replace(/\&amp;(\#[0-9]{2,}\;)/g,"&$1"),e+j}}function u(){return b.DOMParser?(new DOMParser).parseFromString("<xml />","application/xml"):void 0}function v(a){J.vars.debounceTimer||a.call(this),J.vars.debounceTimer&&b.clearTimeout(J.vars.debounceTimer),J.vars.debounceTimer=b.setTimeout(function(){J.vars.debounceTimer=null,a.call(this)},J.setup.debounce)}function w(){v(function(){l(null)})}var x=c(1),y=c(2),z=c(3),A=c(4),B=z.extend,C=z.getNodeArray,D=z.dimensionCheck,E="http://www.w3.org/2000/svg",F=8,G="2.7.1",H="\nCreated with Holder.js "+G+".\nLearn more at http://holderjs.com\n(c) 2012-2015 Ivan Malopinsky - http://imsky.co\n",I={version:G,addTheme:function(a,b){return null!=a&&null!=b&&(J.settings.themes[a]=b),delete J.vars.cache.themeKeys,this},addImage:function(a,b){var c=document.querySelectorAll(b);if(c.length)for(var d=0,e=c.length;e>d;d++){var f=q("img"),g={};g[J.vars.dataAttr]=a,r(f,g),c[d].appendChild(f)}return this},setResizeUpdate:function(a,b){a.holderData&&(a.holderData.resizeUpdate=!!b,a.holderData.resizeUpdate&&l(a))},run:function(a){a=a||{};var c={},f=B(J.settings,a);J.vars.preempted=!0,J.vars.dataAttr=f.dataAttr||J.vars.dataAttr,c.renderer=f.renderer?f.renderer:J.setup.renderer,-1===J.setup.renderers.join(",").indexOf(c.renderer)&&(c.renderer=J.setup.supportsSVG?"svg":J.setup.supportsCanvas?"canvas":"html");var g=C(f.images),i=C(f.bgnodes),j=C(f.stylenodes),k=C(f.objects);c.stylesheets=[],c.svgXMLStylesheet=!0,c.noFontFallback=f.noFontFallback?f.noFontFallback:!1;for(var l=0;l<j.length;l++){var m=j[l];if(m.attributes.rel&&m.attributes.href&&"stylesheet"==m.attributes.rel.value){var n=m.attributes.href.value,o=q("a");o.href=n;var p=o.protocol+"//"+o.host+o.pathname+o.search;c.stylesheets.push(p)}}for(l=0;l<i.length;l++)if(b.getComputedStyle){var r=b.getComputedStyle(i[l],null).getPropertyValue("background-image"),s=i[l].getAttribute("data-background-src"),t=null;t=null==s?r:s;var u=null,v="?"+f.domain+"/";if(0===t.indexOf(v))u=t.slice(1);else if(-1!=t.indexOf(v)){var w=t.substr(t.indexOf(v)).slice(1),x=w.match(/([^\"]*)"?\)/);null!=x&&(u=x[1])}if(null!=u){var y=e(u,f);y&&h({mode:"background",el:i[l],flags:y,engineSettings:c})}}for(l=0;l<k.length;l++){var A=k[l],D={};try{D.data=A.getAttribute("data"),D.dataSrc=A.getAttribute(J.vars.dataAttr)}catch(E){}var F=null!=D.data&&0===D.data.indexOf(f.domain),G=null!=D.dataSrc&&0===D.dataSrc.indexOf(f.domain);F?d(f,c,D.data,A):G&&d(f,c,D.dataSrc,A)}for(l=0;l<g.length;l++){var H=g[l],I={};try{I.src=H.getAttribute("src"),I.dataSrc=H.getAttribute(J.vars.dataAttr),I.rendered=H.getAttribute("data-holder-rendered")}catch(E){}var K=null!=I.src,L=null!=I.dataSrc&&0===I.dataSrc.indexOf(f.domain),M=null!=I.rendered&&"true"==I.rendered;K?0===I.src.indexOf(f.domain)?d(f,c,I.src,H):L&&(M?d(f,c,I.dataSrc,H):!function(a,b,c,e,f){z.imageExists(a,function(a){a||d(b,c,e,f)})}(I.src,f,c,I.dataSrc,H)):L&&d(f,c,I.dataSrc,H)}return this}},J={settings:{domain:"holder.js",images:"img",objects:"object",bgnodes:"body .holderjs",stylenodes:"head link.holderjs",stylesheets:[],themes:{gray:{background:"#EEEEEE",foreground:"#AAAAAA"},social:{background:"#3a5a97",foreground:"#FFFFFF"},industrial:{background:"#434A52",foreground:"#C2F200"},sky:{background:"#0D8FDB",foreground:"#FFFFFF"},vine:{background:"#39DBAC",foreground:"#1E292C"},lava:{background:"#F8591A",foreground:"#1C2846"}}},defaults:{size:10,units:"pt",scale:1/16},flags:{dimensions:{regex:/^(\d+)x(\d+)$/,output:function(a){var b=this.regex.exec(a);return{width:+b[1],height:+b[2]}}},fluid:{regex:/^([0-9]+%?)x([0-9]+%?)$/,output:function(a){var b=this.regex.exec(a);return{width:b[1],height:b[2]}}},colors:{regex:/(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,output:function(a){var b=this.regex.exec(a);return{foreground:"#"+b[2],background:"#"+b[1]}}},text:{regex:/text\:(.*)/,output:function(a){return this.regex.exec(a)[1].replace("\\/","/")}},font:{regex:/font\:(.*)/,output:function(a){return this.regex.exec(a)[1]}},auto:{regex:/^auto$/},textmode:{regex:/textmode\:(.*)/,output:function(a){return this.regex.exec(a)[1]}},random:{regex:/^random$/},size:{regex:/size\:(\d+)/,output:function(a){return this.regex.exec(a)[1]}}}},K=function(){var a=null,b=null,c=null;return function(d){var e=d.root;if(J.setup.supportsSVG){var f=!1,g=function(a){return document.createTextNode(a)};(null==a||a.parentNode!==document.body)&&(f=!0),a=s(a,e.properties.width,e.properties.height),a.style.display="block",f&&(b=q("text",E),c=g(null),r(b,{x:0}),b.appendChild(c),a.appendChild(b),document.body.appendChild(a),a.style.visibility="hidden",a.style.position="absolute",a.style.top="-100%",a.style.left="-100%");var h=e.children.holderTextGroup,i=h.properties;r(b,{y:i.font.size,style:z.cssProps({"font-weight":i.font.weight,"font-size":i.font.size+i.font.units,"font-family":i.font.family})}),c.nodeValue=i.text;var j=b.getBBox(),k=Math.ceil(j.width/(e.properties.width*J.setup.lineWrapRatio)),l=i.text.split(" "),m=i.text.match(/\\n/g);k+=null==m?0:m.length,c.nodeValue=i.text.replace(/[ ]+/g,"");var n=b.getComputedTextLength(),o=j.width-n,p=Math.round(o/Math.max(1,l.length-1)),t=[];if(k>1){c.nodeValue="";for(var u=0;u<l.length;u++)if(0!==l[u].length){c.nodeValue=z.decodeHtmlEntity(l[u]);var v=b.getBBox();t.push({text:l[u],width:v.width})}}return a.style.display="none",{spaceWidth:p,lineCount:k,boundingBox:j,words:t}}return!1}}(),L=function(){var a=q("canvas"),b=null;return function(c){null==b&&(b=a.getContext("2d"));var d=c.root;a.width=J.dpr(d.properties.width),a.height=J.dpr(d.properties.height),b.textBaseline="middle",b.fillStyle=d.children.holderBg.properties.fill,b.fillRect(0,0,J.dpr(d.children.holderBg.width),J.dpr(d.children.holderBg.height));{var e=d.children.holderTextGroup;e.properties}b.font=e.properties.font.weight+" "+J.dpr(e.properties.font.size)+e.properties.font.units+" "+e.properties.font.family+", monospace",b.fillStyle=e.properties.fill;for(var f in e.children){var g=e.children[f];for(var h in g.children){var i=g.children[h],j=J.dpr(e.x+g.x+i.x),k=J.dpr(e.y+g.y+i.y+e.properties.leading/2);b.fillText(i.properties.text,j,k)}}return a.toDataURL("image/png")}}(),M=function(){if(b.XMLSerializer){var a=u(),c=s(null,0,0),d=q("rect",E);return c.appendChild(d),function(b,e){var f=b.root;s(c,f.properties.width,f.properties.height);for(var g=c.querySelectorAll("g"),h=0;h<g.length;h++)g[h].parentNode.removeChild(g[h]);var i=e.holderSettings.flags.holderURL,j="holder_"+(Number(new Date)+32768+(0|32768*Math.random())).toString(16),k=q("g",E),l=f.children.holderTextGroup,m=l.properties,n=q("g",E),o=l.textPositionData,p="#"+j+" text { "+z.cssProps({fill:m.fill,"font-weight":m.font.weight,"font-family":m.font.family+", monospace","font-size":m.font.size+m.font.units})+" } ",u=a.createComment("\nSource URL: "+i+H),v=a.createCDATASection(p),w=c.querySelector("style");r(k,{id:j}),c.insertBefore(u,c.firstChild),w.appendChild(v),k.appendChild(d),k.appendChild(n),c.appendChild(k),r(d,{width:f.children.holderBg.width,height:f.children.holderBg.height,fill:f.children.holderBg.properties.fill}),l.y+=.8*o.boundingBox.height;for(var x in l.children){var y=l.children[x];for(var A in y.children){var B=y.children[A],C=l.x+y.x+B.x,D=l.y+y.y+B.y,F=q("text",E),G=document.createTextNode(null);r(F,{x:C,y:D}),G.nodeValue=B.properties.text,F.appendChild(G),n.appendChild(F)}}var I=N(t(c,e.engineSettings),"background"===e.mode);return I}}}(),N=function(){var a="data:image/svg+xml;charset=UTF-8,",b="data:image/svg+xml;charset=UTF-8;base64,";return function(c,d){return d?b+btoa(unescape(encodeURIComponent(c))):a+encodeURIComponent(c)}}();for(var O in J.flags)J.flags.hasOwnProperty(O)&&(J.flags[O].match=function(a){return a.match(this.regex)});J.setup={renderer:"html",debounce:100,ratio:1,supportsCanvas:!1,supportsSVG:!1,lineWrapRatio:.9,renderers:["html","canvas","svg"]},J.dpr=function(a){return a*J.setup.ratio},J.vars={preempted:!1,resizableImages:[],invisibleImages:{},invisibleId:0,visibilityCheckStarted:!1,debounceTimer:null,cache:{},dataAttr:"data-src"},function(){var a=1,c=1,d=q("canvas"),e=null;d.getContext&&-1!=d.toDataURL("image/png").indexOf("data:image/png")&&(J.setup.renderer="canvas",e=d.getContext("2d"),J.setup.supportsCanvas=!0),J.setup.supportsCanvas&&(a=b.devicePixelRatio||1,c=e.webkitBackingStorePixelRatio||e.mozBackingStorePixelRatio||e.msBackingStorePixelRatio||e.oBackingStorePixelRatio||e.backingStorePixelRatio||1),J.setup.ratio=a/c,document.createElementNS&&document.createElementNS(E,"svg").createSVGRect&&(J.setup.renderer="svg",J.setup.supportsSVG=!0)}(),o(),x&&x(function(){J.vars.preempted||I.run(),b.addEventListener?(b.addEventListener("resize",w,!1),b.addEventListener("orientationchange",w,!1)):b.attachEvent("onresize",w),"object"==typeof b.Turbolinks&&b.document.addEventListener("page:change",function(){I.run()})}),a.exports=I}).call(b,function(){return this}())},function(a){function b(a){function b(a){if(!v){if(!g.body)return e(b);for(v=!0;a=w.shift();)e(a)}}function c(a){(t||a.type===i||g[m]===l)&&(d(),b())}function d(){t?(g[s](q,c,j),a[s](i,c,j)):(g[o](r,c),a[o](k,c))}function e(a,b){setTimeout(a,+b>=0?b:1)}function f(a){v?e(a):w.push(a)}null==document.readyState&&document.addEventListener&&(document.addEventListener("DOMContentLoaded",function y(){document.removeEventListener("DOMContentLoaded",y,!1),document.readyState="complete"},!1),document.readyState="loading");var g=a.document,h=g.documentElement,i="load",j=!1,k="on"+i,l="complete",m="readyState",n="attachEvent",o="detachEvent",p="addEventListener",q="DOMContentLoaded",r="onreadystatechange",s="removeEventListener",t=p in g,u=j,v=j,w=[];if(g[m]===l)e(b);else if(t)g[p](q,c,j),a[p](i,c,j);else{g[n](r,c),a[n](k,c);try{u=null==a.frameElement&&h}catch(x){}u&&u.doScroll&&!function z(){if(!v){try{u.doScroll("left")}catch(a){return e(z,50)}d(),b()}}()}return f.version="1.4.0",f.isReady=function(){return v},f}a.exports="undefined"!=typeof window&&b(window)},function(a,b,c){var d=c(5),e=function(a){function b(a,b){for(var c in b)a[c]=b[c];return a}var c=1,e=d.defclass({constructor:function(a){c++,this.parent=null,this.children={},this.id=c,this.name="n"+c,null!=a&&(this.name=a),this.x=0,this.y=0,this.z=0,this.width=0,this.height=0},resize:function(a,b){null!=a&&(this.width=a),null!=b&&(this.height=b)},moveTo:function(a,b,c){this.x=null!=a?a:this.x,this.y=null!=b?b:this.y,this.z=null!=c?c:this.z},add:function(a){var b=a.name;if(null!=this.children[b])throw"SceneGraph: child with that name already exists: "+b;this.children[b]=a,a.parent=this}}),f=d(e,function(b){this.constructor=function(){b.constructor.call(this,"root"),this.properties=a}}),g=d(e,function(a){function c(c,d){if(a.constructor.call(this,c),this.properties={fill:"#000"},null!=d)b(this.properties,d);else if(null!=c&&"string"!=typeof c)throw"SceneGraph: invalid node name"}this.Group=d.extend(this,{constructor:c,type:"group"}),this.Rect=d.extend(this,{constructor:c,type:"rect"}),this.Text=d.extend(this,{constructor:function(a){c.call(this),this.properties.text=a},type:"text"})}),h=new f;return this.Shape=g,this.root=h,this};a.exports=e},function(a,b){(function(a){b.extend=function(a,b){var c={};for(var d in a)a.hasOwnProperty(d)&&(c[d]=a[d]);if(null!=b)for(var e in b)b.hasOwnProperty(e)&&(c[e]=b[e]);return c},b.cssProps=function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c+":"+a[c]);return b.join(";")},b.encodeHtmlEntity=function(a){for(var b=[],c=0,d=a.length-1;d>=0;d--)c=a.charCodeAt(d),b.unshift(c>128?["&#",c,";"].join(""):a[d]);return b.join("")},b.getNodeArray=function(b){var c=null;return"string"==typeof b?c=document.querySelectorAll(b):a.NodeList&&b instanceof a.NodeList?c=b:a.Node&&b instanceof a.Node?c=[b]:a.HTMLCollection&&b instanceof a.HTMLCollection?c=b:b instanceof Array?c=b:null===b&&(c=[]),c},b.imageExists=function(a,b){var c=new Image;c.onerror=function(){b.call(this,!1)},c.onload=function(){b.call(this,!0)},c.src=a},b.decodeHtmlEntity=function(a){return a.replace(/&#(\d+);/g,function(a,b){return String.fromCharCode(b)})},b.dimensionCheck=function(a){var b={height:a.clientHeight,width:a.clientWidth};return b.height&&b.width?b:!1},b.truthy=function(a){return"string"==typeof a?"true"===a||"yes"===a||"1"===a||"on"===a||"✓"===a:!!a}}).call(b,function(){return this}())},function(a,b,c){var d=encodeURIComponent,e=decodeURIComponent,f=c(6),g=c(7),h=/(\w+)\[(\d+)\]/,i=/\w+\.\w+/;b.parse=function(a){if("string"!=typeof a)return{};if(a=f(a),""===a)return{};"?"===a.charAt(0)&&(a=a.slice(1));for(var b={},c=a.split("&"),d=0;d<c.length;d++){var g,j,k,l=c[d].split("="),m=e(l[0]);if(g=h.exec(m))b[g[1]]=b[g[1]]||[],b[g[1]][g[2]]=e(l[1]);else if(g=i.test(m)){for(g=m.split("."),j=b;g.length;)if(k=g.shift(),k.length){if(j[k]){if(j[k]&&"object"!=typeof j[k])break}else j[k]={};g.length||(j[k]=e(l[1])),j=j[k]}}else b[l[0]]=null==l[1]?"":e(l[1])}return b},b.stringify=function(a){if(!a)return"";var b=[];for(var c in a){var e=a[c];if("array"!=g(e))b.push(d(c)+"="+d(a[c]));else for(var f=0;f<e.length;++f)b.push(d(c+"["+f+"]")+"="+d(e[f]))}return b.join("&")}},function(a){var b=function(){},c=Array.prototype.slice,d=function(a,d){var e=b.prototype="function"==typeof a?a.prototype:a,f=new b,g=d.apply(f,c.call(arguments,2).concat(e));if("object"==typeof g)for(var h in g)f[h]=g[h];if(!f.hasOwnProperty("constructor"))return f;var i=f.constructor;return i.prototype=f,i};d.defclass=function(a){var b=a.constructor;return b.prototype=a,b},d.extend=function(a,b){return d(a,function(a){return this.uber=a,b})},a.exports=d},function(a,b){function c(a){return a.replace(/^\s*|\s*$/g,"")}b=a.exports=c,b.left=function(a){return a.replace(/^\s*/,"")},b.right=function(a){return a.replace(/\s*$/,"")}},function(a){var b=Object.prototype.toString;a.exports=function(a){switch(b.call(a)){case"[object Date]":return"date";case"[object RegExp]":return"regexp";case"[object Arguments]":return"arguments";case"[object Array]":return"array";case"[object Error]":return"error"}return null===a?"null":void 0===a?"undefined":a!==a?"nan":a&&1===a.nodeType?"element":(a=a.valueOf?a.valueOf():Object.prototype.valueOf.apply(a),typeof a)}}])}),function(a,b){b&&(Holder=a.Holder)}(this,"undefined"!=typeof Meteor&&"undefined"!=typeof Package);
+/**
+* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
+*/
+;(function(window, document) {
+/*jshint evil:true */
+ /** version */
+ var version = '3.7.3';
+
+ /** Preset options */
+ var options = window.html5 || {};
+
+ /** Used to skip problem elements */
+ var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
+
+ /** Not all elements can be cloned in IE **/
+ var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
+
+ /** Detect whether the browser supports default html5 styles */
+ var supportsHtml5Styles;
+
+ /** Name of the expando, to work with multiple documents or to re-shiv one document */
+ var expando = '_html5shiv';
+
+ /** The id for the the documents expando */
+ var expanID = 0;
+
+ /** Cached data for each document */
+ var expandoData = {};
+
+ /** Detect whether the browser supports unknown elements */
+ var supportsUnknownElements;
+
+ (function() {
+ try {
+ var a = document.createElement('a');
+ a.innerHTML = '<xyz></xyz>';
+ //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
+ supportsHtml5Styles = ('hidden' in a);
+
+ supportsUnknownElements = a.childNodes.length == 1 || (function() {
+ // assign a false positive if unable to shiv
+ (document.createElement)('a');
+ var frag = document.createDocumentFragment();
+ return (
+ typeof frag.cloneNode == 'undefined' ||
+ typeof frag.createDocumentFragment == 'undefined' ||
+ typeof frag.createElement == 'undefined'
+ );
+ }());
+ } catch(e) {
+ // assign a false positive if detection fails => unable to shiv
+ supportsHtml5Styles = true;
+ supportsUnknownElements = true;
+ }
+
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Creates a style sheet with the given CSS text and adds it to the document.
+ * @private
+ * @param {Document} ownerDocument The document.
+ * @param {String} cssText The CSS text.
+ * @returns {StyleSheet} The style element.
+ */
+ function addStyleSheet(ownerDocument, cssText) {
+ var p = ownerDocument.createElement('p'),
+ parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
+
+ p.innerHTML = 'x<style>' + cssText + '</style>';
+ return parent.insertBefore(p.lastChild, parent.firstChild);
+ }
+
+ /**
+ * Returns the value of `html5.elements` as an array.
+ * @private
+ * @returns {Array} An array of shived element node names.
+ */
+ function getElements() {
+ var elements = html5.elements;
+ return typeof elements == 'string' ? elements.split(' ') : elements;
+ }
+
+ /**
+ * Extends the built-in list of html5 elements
+ * @memberOf html5
+ * @param {String|Array} newElements whitespace separated list or array of new element names to shiv
+ * @param {Document} ownerDocument The context document.
+ */
+ function addElements(newElements, ownerDocument) {
+ var elements = html5.elements;
+ if(typeof elements != 'string'){
+ elements = elements.join(' ');
+ }
+ if(typeof newElements != 'string'){
+ newElements = newElements.join(' ');
+ }
+ html5.elements = elements +' '+ newElements;
+ shivDocument(ownerDocument);
+ }
+
+ /**
+ * Returns the data associated to the given document
+ * @private
+ * @param {Document} ownerDocument The document.
+ * @returns {Object} An object of data.
+ */
+ function getExpandoData(ownerDocument) {
+ var data = expandoData[ownerDocument[expando]];
+ if (!data) {
+ data = {};
+ expanID++;
+ ownerDocument[expando] = expanID;
+ expandoData[expanID] = data;
+ }
+ return data;
+ }
+
+ /**
+ * returns a shived element for the given nodeName and document
+ * @memberOf html5
+ * @param {String} nodeName name of the element
+ * @param {Document|DocumentFragment} ownerDocument The context document.
+ * @returns {Object} The shived element.
+ */
+ function createElement(nodeName, ownerDocument, data){
+ if (!ownerDocument) {
+ ownerDocument = document;
+ }
+ if(supportsUnknownElements){
+ return ownerDocument.createElement(nodeName);
+ }
+ if (!data) {
+ data = getExpandoData(ownerDocument);
+ }
+ var node;
+
+ if (data.cache[nodeName]) {
+ node = data.cache[nodeName].cloneNode();
+ } else if (saveClones.test(nodeName)) {
+ node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
+ } else {
+ node = data.createElem(nodeName);
+ }
+
+ // Avoid adding some elements to fragments in IE < 9 because
+ // * Attributes like `name` or `type` cannot be set/changed once an element
+ // is inserted into a document/fragment
+ // * Link elements with `src` attributes that are inaccessible, as with
+ // a 403 response, will cause the tab/window to crash
+ // * Script elements appended to fragments will execute when their `src`
+ // or `text` property is set
+ return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
+ }
+
+ /**
+ * returns a shived DocumentFragment for the given document
+ * @memberOf html5
+ * @param {Document} ownerDocument The context document.
+ * @returns {Object} The shived DocumentFragment.
+ */
+ function createDocumentFragment(ownerDocument, data){
+ if (!ownerDocument) {
+ ownerDocument = document;
+ }
+ if(supportsUnknownElements){
+ return ownerDocument.createDocumentFragment();
+ }
+ data = data || getExpandoData(ownerDocument);
+ var clone = data.frag.cloneNode(),
+ i = 0,
+ elems = getElements(),
+ l = elems.length;
+ for(;i<l;i++){
+ clone.createElement(elems[i]);
+ }
+ return clone;
+ }
+
+ /**
+ * Shivs the `createElement` and `createDocumentFragment` methods of the document.
+ * @private
+ * @param {Document|DocumentFragment} ownerDocument The document.
+ * @param {Object} data of the document.
+ */
+ function shivMethods(ownerDocument, data) {
+ if (!data.cache) {
+ data.cache = {};
+ data.createElem = ownerDocument.createElement;
+ data.createFrag = ownerDocument.createDocumentFragment;
+ data.frag = data.createFrag();
+ }
+
+
+ ownerDocument.createElement = function(nodeName) {
+ //abort shiv
+ if (!html5.shivMethods) {
+ return data.createElem(nodeName);
+ }
+ return createElement(nodeName, ownerDocument, data);
+ };
+
+ ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
+ 'var n=f.cloneNode(),c=n.createElement;' +
+ 'h.shivMethods&&(' +
+ // unroll the `createElement` calls
+ getElements().join().replace(/[\w\-:]+/g, function(nodeName) {
+ data.createElem(nodeName);
+ data.frag.createElement(nodeName);
+ return 'c("' + nodeName + '")';
+ }) +
+ ');return n}'
+ )(html5, data.frag);
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * Shivs the given document.
+ * @memberOf html5
+ * @param {Document} ownerDocument The document to shiv.
+ * @returns {Document} The shived document.
+ */
+ function shivDocument(ownerDocument) {
+ if (!ownerDocument) {
+ ownerDocument = document;
+ }
+ var data = getExpandoData(ownerDocument);
+
+ if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
+ data.hasCSS = !!addStyleSheet(ownerDocument,
+ // corrects block display not defined in IE6/7/8/9
+ 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
+ // adds styling not present in IE6/7/8/9
+ 'mark{background:#FF0;color:#000}' +
+ // hides non-rendered elements
+ 'template{display:none}'
+ );
+ }
+ if (!supportsUnknownElements) {
+ shivMethods(ownerDocument, data);
+ }
+ return ownerDocument;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * The `html5` object is exposed so that more elements can be shived and
+ * existing shiving can be detected on iframes.
+ * @type Object
+ * @example
+ *
+ * // options can be changed before the script is included
+ * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
+ */
+ var html5 = {
+
+ /**
+ * An array or space separated string of node names of the elements to shiv.
+ * @memberOf html5
+ * @type Array|String
+ */
+ 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video',
+
+ /**
+ * current version of html5shiv
+ */
+ 'version': version,
+
+ /**
+ * A flag to indicate that the HTML5 style sheet should be inserted.
+ * @memberOf html5
+ * @type Boolean
+ */
+ 'shivCSS': (options.shivCSS !== false),
+
+ /**
+ * Is equal to true if a browser supports creating unknown/HTML5 elements
+ * @memberOf html5
+ * @type boolean
+ */
+ 'supportsUnknownElements': supportsUnknownElements,
+
+ /**
+ * A flag to indicate that the document's `createElement` and `createDocumentFragment`
+ * methods should be overwritten.
+ * @memberOf html5
+ * @type Boolean
+ */
+ 'shivMethods': (options.shivMethods !== false),
+
+ /**
+ * A string to describe the type of `html5` object ("default" or "default print").
+ * @memberOf html5
+ * @type String
+ */
+ 'type': 'default',
+
+ // shivs the document according to the specified `html5` object options
+ 'shivDocument': shivDocument,
+
+ //creates a shived element
+ createElement: createElement,
+
+ //creates a shived documentFragment
+ createDocumentFragment: createDocumentFragment,
+
+ //extends list of elements
+ addElements: addElements
+ };
+
+ /*--------------------------------------------------------------------------*/
+
+ // expose html5
+ window.html5 = html5;
+
+ // shiv the document
+ shivDocument(document);
+
+ if(typeof module == 'object' && module.exports){
+ module.exports = html5;
+ }
+
+}(typeof window !== "undefined" ? window : this, document));
+/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=r.isArray(d)))?(e?(e=!1,f=c&&r.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,M,e),g(f,c,N,e)):(f++,j.call(a,g(f,c,M,e),g(f,c,N,e),g(f,c,M,c.notifyWith))):(d!==M&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),
+a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},T=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function U(){this.expando=r.expando+U.uid++}U.uid=1,U.prototype={cache:function(a){var b=a[this.expando];return b||(b={},T(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){r.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(K)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var V=new U,W=new U,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Y=/[A-Z]/g;function Z(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:X.test(a)?JSON.parse(a):a)}function $(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Y,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=Z(c)}catch(e){}W.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return W.hasData(a)||V.hasData(a)},data:function(a,b,c){return W.access(a,b,c)},removeData:function(a,b){W.remove(a,b)},_data:function(a,b,c){return V.access(a,b,c)},_removeData:function(a,b){V.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=W.get(f),1===f.nodeType&&!V.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),$(f,d,e[d])));V.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){W.set(this,a)}):S(this,function(b){var c;if(f&&void 0===b){if(c=W.get(f,a),void 0!==c)return c;if(c=$(f,a),void 0!==c)return c}else this.each(function(){W.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=V.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var _=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,aa=new RegExp("^(?:([+-])=|)("+_+")([a-z%]*)$","i"),ba=["Top","Right","Bottom","Left"],ca=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},da=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function ea(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&aa.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var fa={};function ga(a){var b,c=a.ownerDocument,d=a.nodeName,e=fa[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),fa[d]=e,e)}function ha(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=V.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&ca(d)&&(e[f]=ga(d))):"none"!==c&&(e[f]="none",V.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ha(this,!0)},hide:function(){return ha(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){ca(this)?r(this).show():r(this).hide()})}});var ia=/^(?:checkbox|radio)$/i,ja=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c<d;c++)V.set(a[c],"globalEval",!b||V.get(b[c],"globalEval"))}var oa=/<|&#?\w+;/;function pa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(oa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ja.exec(f)||["",""])[1].toLowerCase(),i=la[h]||la._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==wa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===wa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&r.nodeName(this,"input"))return this.click(),!1},_default:function(a){return r.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ua:va,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:va,isPropagationStopped:va,isImmediatePropagationStopped:va,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ua,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ua,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ua,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&ra.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&sa.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return xa(this,a,b,c,d)},one:function(a,b,c,d){return xa(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=va),this.each(function(){r.event.remove(this,a,c,b)})}});var ya=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/<script|<style|<link/i,Aa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ba=/^true\/(.*)/,Ca=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}W.hasData(a)&&(h=W.access(a),i=r.extend({},h),W.set(b,i))}}function Ha(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ia.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ia(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,ma(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Fa),l=0;l<i;l++)j=h[l],ka.test(j.type||"")&&!V.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Ca,""),k))}return a}function Ja(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(ma(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&na(ma(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(ya,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);if(b)if(c)for(f=f||ma(a),g=g||ma(h),d=0,e=f.length;d<e;d++)Ga(f[d],g[d]);else Ga(a,h);return g=ma(h,"script"),g.length>0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(ma(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ia(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(ma(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var Ka=/^margin/,La=new RegExp("^("+_+")(?!px)[a-z%]+$","i"),Ma=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",qa.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,qa.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Na(a,b,c){var d,e,f,g,h=a.style;return c=c||Ma(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&La.test(g)&&Ka.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Oa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Pa=/^(none|table(?!-c[ea]).+)/,Qa={position:"absolute",visibility:"hidden",display:"block"},Ra={letterSpacing:"0",fontWeight:"400"},Sa=["Webkit","Moz","ms"],Ta=d.createElement("div").style;function Ua(a){if(a in Ta)return a;var b=a[0].toUpperCase()+a.slice(1),c=Sa.length;while(c--)if(a=Sa[c]+b,a in Ta)return a}function Va(a,b,c){var d=aa.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Wa(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ba[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ba[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ba[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ba[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ba[f]+"Width",!0,e)));return g}function Xa(a,b,c){var d,e=!0,f=Ma(a),g="border-box"===r.css(a,"boxSizing",!1,f);if(a.getClientRects().length&&(d=a.getBoundingClientRect()[b]),d<=0||null==d){if(d=Na(a,b,f),(d<0||null==d)&&(d=a.style[b]),La.test(d))return d;e=g&&(o.boxSizingReliable()||d===a.style[b]),d=parseFloat(d)||0}return d+Wa(a,b,c||(g?"border":"content"),e,f)+"px"}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Na(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=a.style;return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=aa.exec(c))&&e[1]&&(c=ea(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b);return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Na(a,b,d)),"normal"===e&&b in Ra&&(e=Ra[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Pa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?Xa(a,b,d):da(a,Qa,function(){return Xa(a,b,d)})},set:function(a,c,d){var e,f=d&&Ma(a),g=d&&Wa(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=aa.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Va(a,c,g)}}}),r.cssHooks.marginLeft=Oa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Na(a,"marginLeft"))||a.getBoundingClientRect().left-da(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ba[d]+b]=f[d]||f[d-2]||f[0];return e}},Ka.test(a)||(r.cssHooks[a+b].set=Va)}),r.fn.extend({css:function(a,b){return S(this,function(a,b,c){var d,e,f={},g=0;if(r.isArray(b)){for(d=Ma(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function fb(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&ca(a),q=V.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],_a.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=V.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ha([a],!0),j=a.style.display||j,k=r.css(a,"display"),ha([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=V.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ha([a],!0),m.done(function(){p||ha([a]),V.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=eb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function gb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],r.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function hb(a,b,c){var d,e,f=0,g=hb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Za||cb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:Za||cb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(gb(k,j.opts.specialEasing);f<g;f++)if(d=hb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,eb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}r.Animation=r.extend(hb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return ea(c.elem,a,aa.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(K);for(var c,d=0,e=a.length;d<e;d++)c=a[d],hb.tweeners[c]=hb.tweeners[c]||[],hb.tweeners[c].unshift(b)},prefilters:[fb],prefilter:function(a,b){b?hb.prefilters.unshift(a):hb.prefilters.push(a)}}),r.speed=function(a,b,c){var e=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off||d.hidden?e.duration=0:"number"!=typeof e.duration&&(e.duration in r.fx.speeds?e.duration=r.fx.speeds[e.duration]:e.duration=r.fx.speeds._default),null!=e.queue&&e.queue!==!0||(e.queue="fx"),e.old=e.complete,e.complete=function(){r.isFunction(e.old)&&e.old.call(this),e.queue&&r.dequeue(this,e.queue)},e},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(ca).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=hb(this,r.extend({},a),f);(e||V.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=V.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&ab.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=V.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(db(b,!0),a,d,e)}}),r.each({slideDown:db("show"),slideUp:db("hide"),slideToggle:db("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(Za=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),Za=void 0},r.fx.timer=function(a){r.timers.push(a),a()?r.fx.start():r.timers.pop()},r.fx.interval=13,r.fx.start=function(){$a||($a=a.requestAnimationFrame?a.requestAnimationFrame(bb):a.setInterval(r.fx.tick,r.fx.interval))},r.fx.stop=function(){a.cancelAnimationFrame?a.cancelAnimationFrame($a):a.clearInterval($a),$a=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var ib,jb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return S(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)),
+void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!r.nodeName(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Qb=[],Rb=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Qb.pop()||r.expando+"_"+rb++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Rb.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Rb.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Rb,"$1"+e):b.jsonp!==!1&&(b.url+=(sb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Qb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=B.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=pa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=mb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length};function Sb(a){return r.isWindow(a)?a:9===a.nodeType&&a.defaultView}r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),d.width||d.height?(e=f.ownerDocument,c=Sb(e),b=e.documentElement,{top:d.top+c.pageYOffset-b.clientTop,left:d.left+c.pageXOffset-b.clientLeft}):d):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),r.nodeName(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||qa})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return S(this,function(a,d,e){var f=Sb(a);return void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Oa(o.pixelPosition,function(a,c){if(c)return c=Na(a,b),La.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return S(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.parseJSON=JSON.parse,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Tb=a.jQuery,Ub=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Ub),b&&a.jQuery===r&&(a.jQuery=Tb),r},b||(a.jQuery=a.$=r),r});
+/* nvd3 version 1.8.1 (https://github.com/novus/nvd3) 2015-06-15 */
+!function(){var a={};a.dev=!1,a.tooltip=a.tooltip||{},a.utils=a.utils||{},a.models=a.models||{},a.charts={},a.logs={},a.dom={},a.dispatch=d3.dispatch("render_start","render_end"),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),a.dev&&(a.dispatch.on("render_start",function(){a.logs.startTime=+new Date}),a.dispatch.on("render_end",function(){a.logs.endTime=+new Date,a.logs.totalTime=a.logs.endTime-a.logs.startTime,a.log("total",a.logs.totalTime)})),a.log=function(){if(a.dev&&window.console&&console.log&&console.log.apply)console.log.apply(console,arguments);else if(a.dev&&window.console&&"function"==typeof console.log&&Function.prototype.bind){var b=Function.prototype.bind.call(console.log,console);b.apply(console,arguments)}return arguments[arguments.length-1]},a.deprecated=function(a,b){console&&console.warn&&console.warn("nvd3 warning: `"+a+"` has been deprecated. ",b||"")},a.render=function(b){b=b||1,a.render.active=!0,a.dispatch.render_start();var c=function(){for(var d,e,f=0;b>f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+="<div class='footer'>"+a.footer+"</div>"),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;e<c.length;e+=1){var f=c[e]&&c[e].values?c[e].values.length:0;d=f>d?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)<d.range()[1]+10||d(a)>d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)<E[0]||d(a)>E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g");
+x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f);
+var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&C<B.length;)E[C]=B[C],D+=B[C++];for(0===C&&(C=1);D>p&&C>1;){E=[],C--;for(var F=0;F<B.length;F++)B[F]>(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,d<c.left+c.right+J+a&&(L=J=5,K+=A),L+=a,L>M&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open<c.close?"2ca02c":"d62728";return'<h3 style="color: #'+d+'">'+a.value+"</h3><table><tr><td>open:</td><td>"+b.yAxis.tickFormat()(c.open)+"</td></tr><tr><td>close:</td><td>"+b.yAxis.tickFormat()(c.close)+"</td></tr><tr><td>high</td><td>"+b.yAxis.tickFormat()(c.high)+"</td></tr><tr><td>low:</td><td>"+b.yAxis.tickFormat()(c.low)+"</td></tr></table>"}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open<c.close?"2ca02c":"d62728";return'<h3 style="color: #'+d+'">'+a.value+"</h3><table><tr><td>open:</td><td>"+b.yAxis.tickFormat()(c.open)+"</td></tr><tr><td>close:</td><td>"+b.yAxis.tickFormat()(c.close)+"</td></tr><tr><td>high</td><td>"+b.yAxis.tickFormat()(c.high)+"</td></tr><tr><td>low:</td><td>"+b.yAxis.tickFormat()(c.low)+"</td></tr></table>"}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&E<D.length;)F[E]=D[E],C+=D[E++];for(0===E&&(E=1);C>g&&E>1;){F=[],E--;for(var G=0;G<D.length;G++)D[G]>(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,d<c.left+c.right+K+a&&(M=K=5,L+=x),M+=a,M>N&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D]
+}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();
+var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left
+}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;M<b[0].length;M++)I.push(K),J.push(L);else I=z.map(function(a){return(a.outer-a.outer/5)*H}),J=z.map(function(a){return(a.inner-a.inner/5)*H}),y=d3.min(z.map(function(a){return a.inner-a.inner/5}));a.utils.initSVG(i);var N=i.selectAll(".nv-wrap.nv-pie").data(b),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-pie nv-chart-"+h),P=O.append("g"),Q=N.select("g"),R=P.append("g").attr("class","nv-pie");P.append("g").attr("class","nv-pieLabels"),N.attr("transform","translate("+c.left+","+c.top+")"),Q.select(".nv-pie").attr("transform","translate("+F/2+","+G/2+")"),Q.select(".nv-pieLabels").attr("transform","translate("+F/2+","+G/2+")"),i.on("click",function(a,b){A.chartClick({data:a,index:b,pos:d3.event,id:h})}),B=[],C=[];for(var M=0;M<b[0].length;M++){var S=d3.svg.arc().outerRadius(I[M]),T=d3.svg.arc().outerRadius(I[M]+5);u!==!1&&(S.startAngle(u),T.startAngle(u)),w!==!1&&(S.endAngle(w),T.endAngle(w)),p&&(S.innerRadius(J[M]),T.innerRadius(J[M])),S.cornerRadius&&x&&(S.cornerRadius(x),T.cornerRadius(x)),B.push(S),C.push(T)}var U=d3.layout.pie().sort(null).value(function(a){return a.disabled?0:g(a)});U.padAngle&&v&&U.padAngle(v),p&&q&&(R.append("text").attr("class","nv-pie-title"),N.select(".nv-pie-title").style("text-anchor","middle").text(function(){return q}).style("font-size",Math.min(F,G)*y*2/(q.length+2)+"px").attr("dy","0.35em").attr("transform",function(){return"translate(0, "+s+")"}));var V=N.select(".nv-pie").selectAll(".nv-slice").data(U),W=N.select(".nv-pieLabels").selectAll(".nv-label").data(U);V.exit().remove(),W.exit().remove();var X=V.enter().append("g");X.attr("class","nv-slice"),X.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),r&&d3.select(this).select("path").transition().duration(70).attr("d",C[b]),A.elementMouseover({data:a.data,index:b,color:d3.select(this).style("fill")})}),X.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),r&&d3.select(this).select("path").transition().duration(50).attr("d",B[b]),A.elementMouseout({data:a.data,index:b})}),X.on("mousemove",function(a,b){A.elementMousemove({data:a.data,index:b})}),X.on("click",function(a,b){A.elementClick({data:a.data,index:b,color:d3.select(this).style("fill")})}),X.on("dblclick",function(a,b){A.elementDblClick({data:a.data,index:b,color:d3.select(this).style("fill")})}),V.attr("fill",function(a,b){return j(a.data,b)}),V.attr("stroke",function(a,b){return j(a.data,b)});X.append("path").each(function(a){this._current=a});if(V.select("path").transition().attr("d",function(a,b){return B[b](a)}).attrTween("d",E),l){for(var Y=[],M=0;M<b[0].length;M++)Y.push(B[M]),m?p&&(Y[M]=d3.svg.arc().outerRadius(B[M].outerRadius()),u!==!1&&Y[M].startAngle(u),w!==!1&&Y[M].endAngle(w)):p||Y[M].innerRadius(0);W.enter().append("g").classed("nv-label",!0).each(function(a){var b=d3.select(this);b.attr("transform",function(a,b){if(t){a.outerRadius=I[b]+10,a.innerRadius=I[b]+15;var c=(a.startAngle+a.endAngle)/2*(180/Math.PI);return(a.startAngle+a.endAngle)/2<Math.PI?c-=90:c+=90,"translate("+Y[b].centroid(a)+") rotate("+c+")"}return a.outerRadius=H+10,a.innerRadius=H+15,"translate("+Y[b].centroid(a)+")"}),b.append("rect").style("stroke","#fff").style("fill","#fff").attr("rx",3).attr("ry",3),b.append("text").style("text-anchor",t?(a.startAngle+a.endAngle)/2<Math.PI?"start":"end":"middle").style("fill","#000")});var Z={},$=14,_=140,ab=function(a){return Math.floor(a[0]/_)*_+","+Math.floor(a[1]/$)*$};W.watchTransition(D,"pie labels").attr("transform",function(a,b){if(t){a.outerRadius=I[b]+10,a.innerRadius=I[b]+15;var c=(a.startAngle+a.endAngle)/2*(180/Math.PI);return(a.startAngle+a.endAngle)/2<Math.PI?c-=90:c+=90,"translate("+Y[b].centroid(a)+") rotate("+c+")"}a.outerRadius=H+10,a.innerRadius=H+15;var d=Y[b].centroid(a);if(a.value){var e=ab(d);Z[e]&&(d[1]-=$),Z[ab(d)]=!0}return"translate("+d+")"}),W.select(".nv-label text").style("text-anchor",function(a){return t?(a.startAngle+a.endAngle)/2<Math.PI?"start":"end":"middle"}).text(function(a,b){var c=(a.endAngle-a.startAngle)/(2*Math.PI),d="";if(!a.value||o>c)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b)
+}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;f<a.length;f++)Math.abs(e.x()(a[f],f)-b)<c&&(c=Math.abs(e.x()(a[f],f)-b),d=f);return d}if(!j){var b=d3.mouse(this)[0]-f.left;i=[a(p,Math.round(c.invert(b)))],q()}}var s=d3.select(this);a.utils.initSVG(s);var t=a.utils.availableWidth(g,s,f),u=a.utils.availableHeight(h,s,f);if(b.update=function(){s.call(b)},b.container=this,!p||!p.length)return a.utils.noData(b,s),b;s.selectAll(".nv-noData").remove();var v=e.y()(p[p.length-1],p.length-1);c=e.xScale(),d=e.yScale();var w=s.selectAll("g.nv-wrap.nv-sparklineplus").data([p]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-sparklineplus"),y=x.append("g"),z=w.select("g");y.append("g").attr("class","nv-sparklineWrap"),y.append("g").attr("class","nv-valueWrap"),y.append("g").attr("class","nv-hoverArea"),w.attr("transform","translate("+f.left+","+f.top+")");var A=z.select(".nv-sparklineWrap");if(e.width(t).height(u),A.call(e),m){var B=z.select(".nv-valueWrap"),C=B.selectAll(".nv-currentValue").data([v]);C.enter().append("text").attr("class","nv-currentValue").attr("dx",o?-8:8).attr("dy",".9em").style("text-anchor",o?"end":"start"),C.attr("x",t+(o?f.right:0)).attr("y",n?function(a){return d(a)}:0).style("fill",e.color()(p[p.length-1],p.length-1)).text(l(v))}y.select(".nv-hoverArea").append("rect").on("mousemove",r).on("click",function(){j=!j}).on("mouseout",function(){i=[],q()}),z.select(".nv-hoverArea rect").attr("transform",function(){return"translate("+-f.left+","+-f.top+")"}).attr("width",t+f.left+f.right).attr("height",u+f.top)}),b}var c,d,e=a.models.sparkline(),f={top:15,right:100,bottom:10,left:50},g=null,h=null,i=[],j=!1,k=d3.format(",r"),l=d3.format(",.2f"),m=!0,n=!0,o=!1,p=null;return b.sparkline=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},xTickFormat:{get:function(){return k},set:function(a){k=a}},yTickFormat:{get:function(){return l},set:function(a){l=a}},showLastValue:{get:function(){return m},set:function(a){m=a}},alignValue:{get:function(){return n},set:function(a){n=a}},rightAlignValue:{get:function(){return o},set:function(a){o=a}},noData:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.stackedArea=function(){"use strict";function b(m){return u.reset(),u.models(r),m.each(function(m){var s=f-e.left-e.right,v=g-e.top-e.bottom;j=d3.select(this),a.utils.initSVG(j),c=r.xScale(),d=r.yScale();var w=m;m.forEach(function(a,b){a.seriesIndex=b,a.values=a.values.map(function(a,c){return a.index=c,a.seriesIndex=b,a})});var x=m.filter(function(a){return!a.disabled});m=d3.layout.stack().order(o).offset(n).values(function(a){return a.values}).x(k).y(l).out(function(a,b,c){a.display={y:c,y0:b}})(x);var y=j.selectAll("g.nv-wrap.nv-stackedarea").data([m]),z=y.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedarea"),A=z.append("defs"),B=z.append("g"),C=y.select("g");B.append("g").attr("class","nv-areaWrap"),B.append("g").attr("class","nv-scatterWrap"),y.attr("transform","translate("+e.left+","+e.top+")"),0==r.forceY().length&&r.forceY().push(0),r.width(s).height(v).x(k).y(function(a){return a.display.y+a.display.y0}).forceY([0]).color(m.map(function(a){return a.color||h(a,a.seriesIndex)}));var D=C.select(".nv-scatterWrap").datum(m);D.call(r),A.append("clipPath").attr("id","nv-edge-clip-"+i).append("rect"),y.select("#nv-edge-clip-"+i+" rect").attr("width",s).attr("height",v),C.attr("clip-path",q?"url(#nv-edge-clip-"+i+")":"");var E=d3.svg.area().x(function(a,b){return c(k(a,b))}).y0(function(a){return d(a.display.y0)}).y1(function(a){return d(a.display.y+a.display.y0)}).interpolate(p),F=d3.svg.area().x(function(a,b){return c(k(a,b))}).y0(function(a){return d(a.display.y0)}).y1(function(a){return d(a.display.y0)}),G=C.select(".nv-areaWrap").selectAll("path.nv-area").data(function(a){return a});G.enter().append("path").attr("class",function(a,b){return"nv-area nv-area-"+b}).attr("d",function(a){return F(a.values,a.seriesIndex)}).on("mouseover",function(a){d3.select(this).classed("hover",!0),t.areaMouseover({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}).on("mouseout",function(a){d3.select(this).classed("hover",!1),t.areaMouseout({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}).on("click",function(a){d3.select(this).classed("hover",!1),t.areaClick({point:a,series:a.key,pos:[d3.event.pageX,d3.event.pageY],seriesIndex:a.seriesIndex})}),G.exit().remove(),G.style("fill",function(a){return a.color||h(a,a.seriesIndex)}).style("stroke",function(a){return a.color||h(a,a.seriesIndex)}),G.watchTransition(u,"stackedArea path").attr("d",function(a,b){return E(a.values,b)}),r.dispatch.on("elementMouseover.area",function(a){C.select(".nv-chart-"+i+" .nv-area-"+a.seriesIndex).classed("hover",!0)}),r.dispatch.on("elementMouseout.area",function(a){C.select(".nv-chart-"+i+" .nv-area-"+a.seriesIndex).classed("hover",!1)}),b.d3_stackedOffset_stackPercent=function(a){var b,c,d,e=a.length,f=a[0].length,g=[];for(c=0;f>c;++c){for(b=0,d=0;b<w.length;b++)d+=l(w[b].values[c]);if(d)for(b=0;e>b;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
+ * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
+ * */
+
+!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='&shy;<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this); <tr>
+ <td class="{{methods_level}}" colspan="4">{{name}}</td>
+ <td class="{{methods_level}} big">{{methods_bar}}</td>
+ <td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
+ <td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
+ <td class="{{methods_level}} small">{{crap}}</td>
+ <td class="{{lines_level}} big">{{lines_bar}}</td>
+ <td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
+ <td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
+ </tr>
+
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+
+/**
+ * Uses var_export() to write a SebastianBergmann\CodeCoverage\CodeCoverage object to a file.
+ */
+class PHP
+{
+ /**
+ * @param CodeCoverage $coverage
+ * @param string $target
+ *
+ * @return string
+ */
+ public function process(CodeCoverage $coverage, $target = null)
+ {
+ $filter = $coverage->filter();
+
+ $output = sprintf(
+ '<?php
+$coverage = new SebastianBergmann\CodeCoverage\CodeCoverage;
+$coverage->setData(%s);
+$coverage->setTests(%s);
+
+$filter = $coverage->filter();
+$filter->setWhitelistedFiles(%s);
+
+return $coverage;',
+ var_export($coverage->getData(true), 1),
+ var_export($coverage->getTests(), 1),
+ var_export($filter->getWhitelistedFiles(), 1)
+ );
+
+ if ($target !== null) {
+ return file_put_contents($target, $output);
+ } else {
+ return $output;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Node\File;
+use SebastianBergmann\CodeCoverage\Util;
+
+/**
+ * Generates human readable output from a code coverage object.
+ *
+ * The output gets put into a text file our written to the CLI.
+ */
+class Text
+{
+ private $lowUpperBound;
+ private $highLowerBound;
+ private $showUncoveredFiles;
+ private $showOnlySummary;
+
+ private $colors = [
+ 'green' => "\x1b[30;42m",
+ 'yellow' => "\x1b[30;43m",
+ 'red' => "\x1b[37;41m",
+ 'header' => "\x1b[1;37;40m",
+ 'reset' => "\x1b[0m",
+ 'eol' => "\x1b[2K",
+ ];
+
+ /**
+ * @param int $lowUpperBound
+ * @param int $highLowerBound
+ * @param bool $showUncoveredFiles
+ * @param bool $showOnlySummary
+ */
+ public function __construct($lowUpperBound = 50, $highLowerBound = 90, $showUncoveredFiles = false, $showOnlySummary = false)
+ {
+ $this->lowUpperBound = $lowUpperBound;
+ $this->highLowerBound = $highLowerBound;
+ $this->showUncoveredFiles = $showUncoveredFiles;
+ $this->showOnlySummary = $showOnlySummary;
+ }
+
+ /**
+ * @param CodeCoverage $coverage
+ * @param bool $showColors
+ *
+ * @return string
+ */
+ public function process(CodeCoverage $coverage, $showColors = false)
+ {
+ $output = PHP_EOL . PHP_EOL;
+ $report = $coverage->getReport();
+ unset($coverage);
+
+ $colors = [
+ 'header' => '',
+ 'classes' => '',
+ 'methods' => '',
+ 'lines' => '',
+ 'reset' => '',
+ 'eol' => ''
+ ];
+
+ if ($showColors) {
+ $colors['classes'] = $this->getCoverageColor(
+ $report->getNumTestedClassesAndTraits(),
+ $report->getNumClassesAndTraits()
+ );
+ $colors['methods'] = $this->getCoverageColor(
+ $report->getNumTestedMethods(),
+ $report->getNumMethods()
+ );
+ $colors['lines'] = $this->getCoverageColor(
+ $report->getNumExecutedLines(),
+ $report->getNumExecutableLines()
+ );
+ $colors['reset'] = $this->colors['reset'];
+ $colors['header'] = $this->colors['header'];
+ $colors['eol'] = $this->colors['eol'];
+ }
+
+ $classes = sprintf(
+ ' Classes: %6s (%d/%d)',
+ Util::percent(
+ $report->getNumTestedClassesAndTraits(),
+ $report->getNumClassesAndTraits(),
+ true
+ ),
+ $report->getNumTestedClassesAndTraits(),
+ $report->getNumClassesAndTraits()
+ );
+
+ $methods = sprintf(
+ ' Methods: %6s (%d/%d)',
+ Util::percent(
+ $report->getNumTestedMethods(),
+ $report->getNumMethods(),
+ true
+ ),
+ $report->getNumTestedMethods(),
+ $report->getNumMethods()
+ );
+
+ $lines = sprintf(
+ ' Lines: %6s (%d/%d)',
+ Util::percent(
+ $report->getNumExecutedLines(),
+ $report->getNumExecutableLines(),
+ true
+ ),
+ $report->getNumExecutedLines(),
+ $report->getNumExecutableLines()
+ );
+
+ $padding = max(array_map('strlen', [$classes, $methods, $lines]));
+
+ if ($this->showOnlySummary) {
+ $title = 'Code Coverage Report Summary:';
+ $padding = max($padding, strlen($title));
+
+ $output .= $this->format($colors['header'], $padding, $title);
+ } else {
+ $date = date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
+ $title = 'Code Coverage Report:';
+
+ $output .= $this->format($colors['header'], $padding, $title);
+ $output .= $this->format($colors['header'], $padding, $date);
+ $output .= $this->format($colors['header'], $padding, '');
+ $output .= $this->format($colors['header'], $padding, ' Summary:');
+ }
+
+ $output .= $this->format($colors['classes'], $padding, $classes);
+ $output .= $this->format($colors['methods'], $padding, $methods);
+ $output .= $this->format($colors['lines'], $padding, $lines);
+
+ if ($this->showOnlySummary) {
+ return $output . PHP_EOL;
+ }
+
+ $classCoverage = [];
+
+ foreach ($report as $item) {
+ if (!$item instanceof File) {
+ continue;
+ }
+
+ $classes = $item->getClassesAndTraits();
+
+ foreach ($classes as $className => $class) {
+ $classStatements = 0;
+ $coveredClassStatements = 0;
+ $coveredMethods = 0;
+ $classMethods = 0;
+
+ foreach ($class['methods'] as $method) {
+ if ($method['executableLines'] == 0) {
+ continue;
+ }
+
+ $classMethods++;
+ $classStatements += $method['executableLines'];
+ $coveredClassStatements += $method['executedLines'];
+ if ($method['coverage'] == 100) {
+ $coveredMethods++;
+ }
+ }
+
+ if (!empty($class['package']['namespace'])) {
+ $namespace = '\\' . $class['package']['namespace'] . '::';
+ } elseif (!empty($class['package']['fullPackage'])) {
+ $namespace = '@' . $class['package']['fullPackage'] . '::';
+ } else {
+ $namespace = '';
+ }
+
+ $classCoverage[$namespace . $className] = [
+ 'namespace' => $namespace,
+ 'className ' => $className,
+ 'methodsCovered' => $coveredMethods,
+ 'methodCount' => $classMethods,
+ 'statementsCovered' => $coveredClassStatements,
+ 'statementCount' => $classStatements,
+ ];
+ }
+ }
+
+ ksort($classCoverage);
+
+ $methodColor = '';
+ $linesColor = '';
+ $resetColor = '';
+
+ foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
+ if ($classInfo['statementsCovered'] != 0 ||
+ $this->showUncoveredFiles) {
+ if ($showColors) {
+ $methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
+ $linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
+ $resetColor = $colors['reset'];
+ }
+
+ $output .= PHP_EOL . $fullQualifiedPath . PHP_EOL
+ . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '
+ . ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor
+ ;
+ }
+ }
+
+ return $output . PHP_EOL;
+ }
+
+ protected function getCoverageColor($numberOfCoveredElements, $totalNumberOfElements)
+ {
+ $coverage = Util::percent(
+ $numberOfCoveredElements,
+ $totalNumberOfElements
+ );
+
+ if ($coverage >= $this->highLowerBound) {
+ return $this->colors['green'];
+ } elseif ($coverage > $this->lowUpperBound) {
+ return $this->colors['yellow'];
+ }
+
+ return $this->colors['red'];
+ }
+
+ protected function printCoverageCounts($numberOfCoveredElements, $totalNumberOfElements, $precision)
+ {
+ $format = '%' . $precision . 's';
+
+ return Util::percent(
+ $numberOfCoveredElements,
+ $totalNumberOfElements,
+ true,
+ true
+ ) .
+ ' (' . sprintf($format, $numberOfCoveredElements) . '/' .
+ sprintf($format, $totalNumberOfElements) . ')';
+ }
+
+ private function format($color, $padding, $string)
+ {
+ $reset = $color ? $this->colors['reset'] : '';
+
+ return $color . str_pad($string, $padding) . $reset . PHP_EOL;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+use SebastianBergmann\CodeCoverage\RuntimeException;
+
+class Coverage
+{
+ /**
+ * @var \XMLWriter
+ */
+ private $writer;
+
+ /**
+ * @var \DOMElement
+ */
+ private $contextNode;
+
+ /**
+ * @var bool
+ */
+ private $finalized = false;
+
+ public function __construct(\DOMElement $context, $line)
+ {
+ $this->contextNode = $context;
+
+ $this->writer = new \XMLWriter;
+ $this->writer->openMemory();
+ $this->writer->startElementNs(null, $context->nodeName, 'http://schema.phpunit.de/coverage/1.0');
+ $this->writer->writeAttribute('nr', $line);
+ }
+
+ public function addTest($test)
+ {
+ if ($this->finalized) {
+ throw new RuntimeException('Coverage Report already finalized');
+ }
+
+ $this->writer->startElement('covered');
+ $this->writer->writeAttribute('by', $test);
+ $this->writer->endElement();
+ }
+
+ public function finalize()
+ {
+ $this->writer->endElement();
+
+ $fragment = $this->contextNode->ownerDocument->createDocumentFragment();
+ $fragment->appendXML($this->writer->outputMemory());
+
+ $this->contextNode->parentNode->replaceChild(
+ $fragment,
+ $this->contextNode
+ );
+
+ $this->finalized = true;
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class Directory extends Node
+{
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Node\AbstractNode;
+use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
+use SebastianBergmann\CodeCoverage\Node\File as FileNode;
+use SebastianBergmann\CodeCoverage\RuntimeException;
+
+class Facade
+{
+ /**
+ * @var string
+ */
+ private $target;
+
+ /**
+ * @var Project
+ */
+ private $project;
+
+ /**
+ * @param CodeCoverage $coverage
+ * @param string $target
+ *
+ * @throws RuntimeException
+ */
+ public function process(CodeCoverage $coverage, $target)
+ {
+ if (substr($target, -1, 1) != DIRECTORY_SEPARATOR) {
+ $target .= DIRECTORY_SEPARATOR;
+ }
+
+ $this->target = $target;
+ $this->initTargetDirectory($target);
+
+ $report = $coverage->getReport();
+
+ $this->project = new Project(
+ $coverage->getReport()->getName()
+ );
+
+ $this->processTests($coverage->getTests());
+ $this->processDirectory($report, $this->project);
+
+ $index = $this->project->asDom();
+ $index->formatOutput = true;
+ $index->preserveWhiteSpace = false;
+ $index->save($target . '/index.xml');
+ }
+
+ /**
+ * @param string $directory
+ */
+ private function initTargetDirectory($directory)
+ {
+ if (file_exists($directory)) {
+ if (!is_dir($directory)) {
+ throw new RuntimeException(
+ "'$directory' exists but is not a directory."
+ );
+ }
+
+ if (!is_writable($directory)) {
+ throw new RuntimeException(
+ "'$directory' exists but is not writable."
+ );
+ }
+ } elseif (!@mkdir($directory, 0777, true)) {
+ throw new RuntimeException(
+ "'$directory' could not be created."
+ );
+ }
+ }
+
+ private function processDirectory(DirectoryNode $directory, Node $context)
+ {
+ $dirObject = $context->addDirectory($directory->getName());
+
+ $this->setTotals($directory, $dirObject->getTotals());
+
+ foreach ($directory->getDirectories() as $node) {
+ $this->processDirectory($node, $dirObject);
+ }
+
+ foreach ($directory->getFiles() as $node) {
+ $this->processFile($node, $dirObject);
+ }
+ }
+
+ private function processFile(FileNode $file, Directory $context)
+ {
+ $fileObject = $context->addFile(
+ $file->getName(),
+ $file->getId() . '.xml'
+ );
+
+ $this->setTotals($file, $fileObject->getTotals());
+
+ $fileReport = new Report($file->getName());
+
+ $this->setTotals($file, $fileReport->getTotals());
+
+ foreach ($file->getClassesAndTraits() as $unit) {
+ $this->processUnit($unit, $fileReport);
+ }
+
+ foreach ($file->getFunctions() as $function) {
+ $this->processFunction($function, $fileReport);
+ }
+
+ foreach ($file->getCoverageData() as $line => $tests) {
+ if (!is_array($tests) || count($tests) == 0) {
+ continue;
+ }
+
+ $coverage = $fileReport->getLineCoverage($line);
+
+ foreach ($tests as $test) {
+ $coverage->addTest($test);
+ }
+
+ $coverage->finalize();
+ }
+
+ $this->initTargetDirectory(
+ $this->target . dirname($file->getId()) . '/'
+ );
+
+ $fileDom = $fileReport->asDom();
+ $fileDom->formatOutput = true;
+ $fileDom->preserveWhiteSpace = false;
+ $fileDom->save($this->target . $file->getId() . '.xml');
+ }
+
+ private function processUnit($unit, Report $report)
+ {
+ if (isset($unit['className'])) {
+ $unitObject = $report->getClassObject($unit['className']);
+ } else {
+ $unitObject = $report->getTraitObject($unit['traitName']);
+ }
+
+ $unitObject->setLines(
+ $unit['startLine'],
+ $unit['executableLines'],
+ $unit['executedLines']
+ );
+
+ $unitObject->setCrap($unit['crap']);
+
+ $unitObject->setPackage(
+ $unit['package']['fullPackage'],
+ $unit['package']['package'],
+ $unit['package']['subpackage'],
+ $unit['package']['category']
+ );
+
+ $unitObject->setNamespace($unit['package']['namespace']);
+
+ foreach ($unit['methods'] as $method) {
+ $methodObject = $unitObject->addMethod($method['methodName']);
+ $methodObject->setSignature($method['signature']);
+ $methodObject->setLines($method['startLine'], $method['endLine']);
+ $methodObject->setCrap($method['crap']);
+ $methodObject->setTotals(
+ $method['executableLines'],
+ $method['executedLines'],
+ $method['coverage']
+ );
+ }
+ }
+
+ private function processFunction($function, Report $report)
+ {
+ $functionObject = $report->getFunctionObject($function['functionName']);
+
+ $functionObject->setSignature($function['signature']);
+ $functionObject->setLines($function['startLine']);
+ $functionObject->setCrap($function['crap']);
+ $functionObject->setTotals($function['executableLines'], $function['executedLines'], $function['coverage']);
+ }
+
+ private function processTests(array $tests)
+ {
+ $testsObject = $this->project->getTests();
+
+ foreach ($tests as $test => $result) {
+ if ($test == 'UNCOVERED_FILES_FROM_WHITELIST') {
+ continue;
+ }
+
+ $testsObject->addTest($test, $result);
+ }
+ }
+
+ private function setTotals(AbstractNode $node, Totals $totals)
+ {
+ $loc = $node->getLinesOfCode();
+
+ $totals->setNumLines(
+ $loc['loc'],
+ $loc['cloc'],
+ $loc['ncloc'],
+ $node->getNumExecutableLines(),
+ $node->getNumExecutedLines()
+ );
+
+ $totals->setNumClasses(
+ $node->getNumClasses(),
+ $node->getNumTestedClasses()
+ );
+
+ $totals->setNumTraits(
+ $node->getNumTraits(),
+ $node->getNumTestedTraits()
+ );
+
+ $totals->setNumMethods(
+ $node->getNumMethods(),
+ $node->getNumTestedMethods()
+ );
+
+ $totals->setNumFunctions(
+ $node->getNumFunctions(),
+ $node->getNumTestedFunctions()
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class File
+{
+ /**
+ * @var \DOMDocument
+ */
+ protected $dom;
+
+ /**
+ * @var \DOMElement
+ */
+ protected $contextNode;
+
+ public function __construct(\DOMElement $context)
+ {
+ $this->dom = $context->ownerDocument;
+ $this->contextNode = $context;
+ }
+
+ public function getTotals()
+ {
+ $totalsContainer = $this->contextNode->firstChild;
+
+ if (!$totalsContainer) {
+ $totalsContainer = $this->contextNode->appendChild(
+ $this->dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'totals'
+ )
+ );
+ }
+
+ return new Totals($totalsContainer);
+ }
+
+ public function getLineCoverage($line)
+ {
+ $coverage = $this->contextNode->getElementsByTagNameNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'coverage'
+ )->item(0);
+
+ if (!$coverage) {
+ $coverage = $this->contextNode->appendChild(
+ $this->dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'coverage'
+ )
+ );
+ }
+
+ $lineNode = $coverage->appendChild(
+ $this->dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'line'
+ )
+ );
+
+ return new Coverage($lineNode, $line);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class Method
+{
+ /**
+ * @var \DOMElement
+ */
+ private $contextNode;
+
+ public function __construct(\DOMElement $context, $name)
+ {
+ $this->contextNode = $context;
+
+ $this->setName($name);
+ }
+
+ private function setName($name)
+ {
+ $this->contextNode->setAttribute('name', $name);
+ }
+
+ public function setSignature($signature)
+ {
+ $this->contextNode->setAttribute('signature', $signature);
+ }
+
+ public function setLines($start, $end = null)
+ {
+ $this->contextNode->setAttribute('start', $start);
+
+ if ($end !== null) {
+ $this->contextNode->setAttribute('end', $end);
+ }
+ }
+
+ public function setTotals($executable, $executed, $coverage)
+ {
+ $this->contextNode->setAttribute('executable', $executable);
+ $this->contextNode->setAttribute('executed', $executed);
+ $this->contextNode->setAttribute('coverage', $coverage);
+ }
+
+ public function setCrap($crap)
+ {
+ $this->contextNode->setAttribute('crap', $crap);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class Node
+{
+ /**
+ * @var \DOMDocument
+ */
+ private $dom;
+
+ /**
+ * @var \DOMElement
+ */
+ private $contextNode;
+
+ public function __construct(\DOMElement $context)
+ {
+ $this->setContextNode($context);
+ }
+
+ protected function setContextNode(\DOMElement $context)
+ {
+ $this->dom = $context->ownerDocument;
+ $this->contextNode = $context;
+ }
+
+ public function getDom()
+ {
+ return $this->dom;
+ }
+
+ protected function getContextNode()
+ {
+ return $this->contextNode;
+ }
+
+ public function getTotals()
+ {
+ $totalsContainer = $this->getContextNode()->firstChild;
+
+ if (!$totalsContainer) {
+ $totalsContainer = $this->getContextNode()->appendChild(
+ $this->dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'totals'
+ )
+ );
+ }
+
+ return new Totals($totalsContainer);
+ }
+
+ public function addDirectory($name)
+ {
+ $dirNode = $this->getDom()->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'directory'
+ );
+
+ $dirNode->setAttribute('name', $name);
+ $this->getContextNode()->appendChild($dirNode);
+
+ return new Directory($dirNode);
+ }
+
+ public function addFile($name, $href)
+ {
+ $fileNode = $this->getDom()->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'file'
+ );
+
+ $fileNode->setAttribute('name', $name);
+ $fileNode->setAttribute('href', $href);
+ $this->getContextNode()->appendChild($fileNode);
+
+ return new File($fileNode);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class Project extends Node
+{
+ public function __construct($name)
+ {
+ $this->init();
+ $this->setProjectName($name);
+ }
+
+ private function init()
+ {
+ $dom = new \DOMDocument;
+ $dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="http://schema.phpunit.de/coverage/1.0"><project/></phpunit>');
+
+ $this->setContextNode(
+ $dom->getElementsByTagNameNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'project'
+ )->item(0)
+ );
+ }
+
+ private function setProjectName($name)
+ {
+ $this->getContextNode()->setAttribute('name', $name);
+ }
+
+ public function getTests()
+ {
+ $testsNode = $this->getContextNode()->getElementsByTagNameNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'tests'
+ )->item(0);
+
+ if (!$testsNode) {
+ $testsNode = $this->getContextNode()->appendChild(
+ $this->getDom()->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'tests'
+ )
+ );
+ }
+
+ return new Tests($testsNode);
+ }
+
+ public function asDom()
+ {
+ return $this->getDom();
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class Report extends File
+{
+ public function __construct($name)
+ {
+ $this->dom = new \DOMDocument;
+ $this->dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="http://schema.phpunit.de/coverage/1.0"><file /></phpunit>');
+
+ $this->contextNode = $this->dom->getElementsByTagNameNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'file'
+ )->item(0);
+
+ $this->setName($name);
+ }
+
+ private function setName($name)
+ {
+ $this->contextNode->setAttribute('name', $name);
+ }
+
+ public function asDom()
+ {
+ return $this->dom;
+ }
+
+ public function getFunctionObject($name)
+ {
+ $node = $this->contextNode->appendChild(
+ $this->dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'function'
+ )
+ );
+
+ return new Method($node, $name);
+ }
+
+ public function getClassObject($name)
+ {
+ return $this->getUnitObject('class', $name);
+ }
+
+ public function getTraitObject($name)
+ {
+ return $this->getUnitObject('trait', $name);
+ }
+
+ private function getUnitObject($tagName, $name)
+ {
+ $node = $this->contextNode->appendChild(
+ $this->dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ $tagName
+ )
+ );
+
+ return new Unit($node, $name);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class Tests
+{
+ private $contextNode;
+
+ private $codeMap = [
+ 0 => 'PASSED', // PHPUnit_Runner_BaseTestRunner::STATUS_PASSED
+ 1 => 'SKIPPED', // PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED
+ 2 => 'INCOMPLETE', // PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE
+ 3 => 'FAILURE', // PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE
+ 4 => 'ERROR', // PHPUnit_Runner_BaseTestRunner::STATUS_ERROR
+ 5 => 'RISKY', // PHPUnit_Runner_BaseTestRunner::STATUS_RISKY
+ 6 => 'WARNING' // PHPUnit_Runner_BaseTestRunner::STATUS_WARNING
+ ];
+
+ public function __construct(\DOMElement $context)
+ {
+ $this->contextNode = $context;
+ }
+
+ public function addTest($test, array $result)
+ {
+ $node = $this->contextNode->appendChild(
+ $this->contextNode->ownerDocument->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'test'
+ )
+ );
+
+ $node->setAttribute('name', $test);
+ $node->setAttribute('size', $result['size']);
+ $node->setAttribute('result', (int) $result['status']);
+ $node->setAttribute('status', $this->codeMap[(int) $result['status']]);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+use SebastianBergmann\CodeCoverage\Util;
+
+class Totals
+{
+ /**
+ * @var \DOMNode
+ */
+ private $container;
+
+ /**
+ * @var \DOMElement
+ */
+ private $linesNode;
+
+ /**
+ * @var \DOMElement
+ */
+ private $methodsNode;
+
+ /**
+ * @var \DOMElement
+ */
+ private $functionsNode;
+
+ /**
+ * @var \DOMElement
+ */
+ private $classesNode;
+
+ /**
+ * @var \DOMElement
+ */
+ private $traitsNode;
+
+ public function __construct(\DOMElement $container)
+ {
+ $this->container = $container;
+ $dom = $container->ownerDocument;
+
+ $this->linesNode = $dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'lines'
+ );
+
+ $this->methodsNode = $dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'methods'
+ );
+
+ $this->functionsNode = $dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'functions'
+ );
+
+ $this->classesNode = $dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'classes'
+ );
+
+ $this->traitsNode = $dom->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'traits'
+ );
+
+ $container->appendChild($this->linesNode);
+ $container->appendChild($this->methodsNode);
+ $container->appendChild($this->functionsNode);
+ $container->appendChild($this->classesNode);
+ $container->appendChild($this->traitsNode);
+ }
+
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ public function setNumLines($loc, $cloc, $ncloc, $executable, $executed)
+ {
+ $this->linesNode->setAttribute('total', $loc);
+ $this->linesNode->setAttribute('comments', $cloc);
+ $this->linesNode->setAttribute('code', $ncloc);
+ $this->linesNode->setAttribute('executable', $executable);
+ $this->linesNode->setAttribute('executed', $executed);
+ $this->linesNode->setAttribute(
+ 'percent',
+ Util::percent($executed, $executable, true)
+ );
+ }
+
+ public function setNumClasses($count, $tested)
+ {
+ $this->classesNode->setAttribute('count', $count);
+ $this->classesNode->setAttribute('tested', $tested);
+ $this->classesNode->setAttribute(
+ 'percent',
+ Util::percent($tested, $count, true)
+ );
+ }
+
+ public function setNumTraits($count, $tested)
+ {
+ $this->traitsNode->setAttribute('count', $count);
+ $this->traitsNode->setAttribute('tested', $tested);
+ $this->traitsNode->setAttribute(
+ 'percent',
+ Util::percent($tested, $count, true)
+ );
+ }
+
+ public function setNumMethods($count, $tested)
+ {
+ $this->methodsNode->setAttribute('count', $count);
+ $this->methodsNode->setAttribute('tested', $tested);
+ $this->methodsNode->setAttribute(
+ 'percent',
+ Util::percent($tested, $count, true)
+ );
+ }
+
+ public function setNumFunctions($count, $tested)
+ {
+ $this->functionsNode->setAttribute('count', $count);
+ $this->functionsNode->setAttribute('tested', $tested);
+ $this->functionsNode->setAttribute(
+ 'percent',
+ Util::percent($tested, $count, true)
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage\Report\Xml;
+
+class Unit
+{
+ /**
+ * @var \DOMElement
+ */
+ private $contextNode;
+
+ public function __construct(\DOMElement $context, $name)
+ {
+ $this->contextNode = $context;
+
+ $this->setName($name);
+ }
+
+ private function setName($name)
+ {
+ $this->contextNode->setAttribute('name', $name);
+ }
+
+ public function setLines($start, $executable, $executed)
+ {
+ $this->contextNode->setAttribute('start', $start);
+ $this->contextNode->setAttribute('executable', $executable);
+ $this->contextNode->setAttribute('executed', $executed);
+ }
+
+ public function setCrap($crap)
+ {
+ $this->contextNode->setAttribute('crap', $crap);
+ }
+
+ public function setPackage($full, $package, $sub, $category)
+ {
+ $node = $this->contextNode->getElementsByTagNameNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'package'
+ )->item(0);
+
+ if (!$node) {
+ $node = $this->contextNode->appendChild(
+ $this->contextNode->ownerDocument->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'package'
+ )
+ );
+ }
+
+ $node->setAttribute('full', $full);
+ $node->setAttribute('name', $package);
+ $node->setAttribute('sub', $sub);
+ $node->setAttribute('category', $category);
+ }
+
+ public function setNamespace($namespace)
+ {
+ $node = $this->contextNode->getElementsByTagNameNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'namespace'
+ )->item(0);
+
+ if (!$node) {
+ $node = $this->contextNode->appendChild(
+ $this->contextNode->ownerDocument->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'namespace'
+ )
+ );
+ }
+
+ $node->setAttribute('name', $namespace);
+ }
+
+ public function addMethod($name)
+ {
+ $node = $this->contextNode->appendChild(
+ $this->contextNode->ownerDocument->createElementNS(
+ 'http://schema.phpunit.de/coverage/1.0',
+ 'method'
+ )
+ );
+
+ return new Method($node, $name);
+ }
+}
+<?php
+/*
+ * This file is part of the php-code-coverage package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeCoverage;
+
+/**
+ * Utility methods.
+ */
+class Util
+{
+ /**
+ * @param float $a
+ * @param float $b
+ * @param bool $asString
+ * @param bool $fixedWidth
+ *
+ * @return float|int|string
+ */
+ public static function percent($a, $b, $asString = false, $fixedWidth = false)
+ {
+ if ($asString && $b == 0) {
+ return '';
+ }
+
+ if ($b > 0) {
+ $percent = ($a / $b) * 100;
+ } else {
+ $percent = 100;
+ }
+
+ if ($asString) {
+ if ($fixedWidth) {
+ return sprintf('%6.2F%%', $percent);
+ }
+
+ return sprintf('%01.2F%%', $percent);
+ } else {
+ return $percent;
+ }
+ }
+}
+File_Iterator
+
+Copyright (c) 2009-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the File_Iterator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Façade implementation that uses File_Iterator_Factory to create a
+ * File_Iterator that operates on an AppendIterator that contains an
+ * RecursiveDirectoryIterator for each given path. The list of unique
+ * files is returned as an array.
+ *
+ * @since Class available since Release 1.3.0
+ */
+class File_Iterator_Facade
+{
+ /**
+ * @param array|string $paths
+ * @param array|string $suffixes
+ * @param array|string $prefixes
+ * @param array $exclude
+ * @param bool $commonPath
+ * @return array
+ */
+ public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = array(), $commonPath = FALSE)
+ {
+ if (is_string($paths)) {
+ $paths = array($paths);
+ }
+
+ $factory = new File_Iterator_Factory;
+ $iterator = $factory->getFileIterator(
+ $paths, $suffixes, $prefixes, $exclude
+ );
+
+ $files = array();
+
+ foreach ($iterator as $file) {
+ $file = $file->getRealPath();
+
+ if ($file) {
+ $files[] = $file;
+ }
+ }
+
+ foreach ($paths as $path) {
+ if (is_file($path)) {
+ $files[] = realpath($path);
+ }
+ }
+
+ $files = array_unique($files);
+ sort($files);
+
+ if ($commonPath) {
+ return array(
+ 'commonPath' => $this->getCommonPath($files),
+ 'files' => $files
+ );
+ } else {
+ return $files;
+ }
+ }
+
+ /**
+ * Returns the common path of a set of files.
+ *
+ * @param array $files
+ * @return string
+ */
+ protected function getCommonPath(array $files)
+ {
+ $count = count($files);
+
+ if ($count == 0) {
+ return '';
+ }
+
+ if ($count == 1) {
+ return dirname($files[0]) . DIRECTORY_SEPARATOR;
+ }
+
+ $_files = array();
+
+ foreach ($files as $file) {
+ $_files[] = $_fileParts = explode(DIRECTORY_SEPARATOR, $file);
+
+ if (empty($_fileParts[0])) {
+ $_fileParts[0] = DIRECTORY_SEPARATOR;
+ }
+ }
+
+ $common = '';
+ $done = FALSE;
+ $j = 0;
+ $count--;
+
+ while (!$done) {
+ for ($i = 0; $i < $count; $i++) {
+ if ($_files[$i][$j] != $_files[$i+1][$j]) {
+ $done = TRUE;
+ break;
+ }
+ }
+
+ if (!$done) {
+ $common .= $_files[0][$j];
+
+ if ($j > 0) {
+ $common .= DIRECTORY_SEPARATOR;
+ }
+ }
+
+ $j++;
+ }
+
+ return DIRECTORY_SEPARATOR . $common;
+ }
+}
+<?php
+/*
+ * This file is part of the File_Iterator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Factory Method implementation that creates a File_Iterator that operates on
+ * an AppendIterator that contains an RecursiveDirectoryIterator for each given
+ * path.
+ *
+ * @since Class available since Release 1.1.0
+ */
+class File_Iterator_Factory
+{
+ /**
+ * @param array|string $paths
+ * @param array|string $suffixes
+ * @param array|string $prefixes
+ * @param array $exclude
+ * @return AppendIterator
+ */
+ public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = array())
+ {
+ if (is_string($paths)) {
+ $paths = array($paths);
+ }
+
+ $paths = $this->getPathsAfterResolvingWildcards($paths);
+ $exclude = $this->getPathsAfterResolvingWildcards($exclude);
+
+ if (is_string($prefixes)) {
+ if ($prefixes != '') {
+ $prefixes = array($prefixes);
+ } else {
+ $prefixes = array();
+ }
+ }
+
+ if (is_string($suffixes)) {
+ if ($suffixes != '') {
+ $suffixes = array($suffixes);
+ } else {
+ $suffixes = array();
+ }
+ }
+
+ $iterator = new AppendIterator;
+
+ foreach ($paths as $path) {
+ if (is_dir($path)) {
+ $iterator->append(
+ new File_Iterator(
+ new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::FOLLOW_SYMLINKS)
+ ),
+ $suffixes,
+ $prefixes,
+ $exclude,
+ $path
+ )
+ );
+ }
+ }
+
+ return $iterator;
+ }
+
+ /**
+ * @param array $paths
+ * @return array
+ */
+ protected function getPathsAfterResolvingWildcards(array $paths)
+ {
+ $_paths = array();
+
+ foreach ($paths as $path) {
+ if ($locals = glob($path, GLOB_ONLYDIR)) {
+ $_paths = array_merge($_paths, $locals);
+ } else {
+ $_paths[] = $path;
+ }
+ }
+
+ return $_paths;
+ }
+}
+<?php
+/*
+ * This file is part of the File_Iterator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * FilterIterator implementation that filters files based on prefix(es) and/or
+ * suffix(es). Hidden files and files from hidden directories are also filtered.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class File_Iterator extends FilterIterator
+{
+ const PREFIX = 0;
+ const SUFFIX = 1;
+
+ /**
+ * @var array
+ */
+ protected $suffixes = array();
+
+ /**
+ * @var array
+ */
+ protected $prefixes = array();
+
+ /**
+ * @var array
+ */
+ protected $exclude = array();
+
+ /**
+ * @var string
+ */
+ protected $basepath;
+
+ /**
+ * @param Iterator $iterator
+ * @param array $suffixes
+ * @param array $prefixes
+ * @param array $exclude
+ * @param string $basepath
+ */
+ public function __construct(Iterator $iterator, array $suffixes = array(), array $prefixes = array(), array $exclude = array(), $basepath = NULL)
+ {
+ $exclude = array_filter(array_map('realpath', $exclude));
+
+ if ($basepath !== NULL) {
+ $basepath = realpath($basepath);
+ }
+
+ if ($basepath === FALSE) {
+ $basepath = NULL;
+ } else {
+ foreach ($exclude as &$_exclude) {
+ $_exclude = str_replace($basepath, '', $_exclude);
+ }
+ }
+
+ $this->prefixes = $prefixes;
+ $this->suffixes = $suffixes;
+ $this->exclude = $exclude;
+ $this->basepath = $basepath;
+
+ parent::__construct($iterator);
+ }
+
+ /**
+ * @return bool
+ */
+ public function accept()
+ {
+ $current = $this->getInnerIterator()->current();
+ $filename = $current->getFilename();
+ $realpath = $current->getRealPath();
+
+ if ($this->basepath !== NULL) {
+ $realpath = str_replace($this->basepath, '', $realpath);
+ }
+
+ // Filter files in hidden directories.
+ if (preg_match('=/\.[^/]*/=', $realpath)) {
+ return FALSE;
+ }
+
+ return $this->acceptPath($realpath) &&
+ $this->acceptPrefix($filename) &&
+ $this->acceptSuffix($filename);
+ }
+
+ /**
+ * @param string $path
+ * @return bool
+ * @since Method available since Release 1.1.0
+ */
+ protected function acceptPath($path)
+ {
+ foreach ($this->exclude as $exclude) {
+ if (strpos($path, $exclude) === 0) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * @param string $filename
+ * @return bool
+ * @since Method available since Release 1.1.0
+ */
+ protected function acceptPrefix($filename)
+ {
+ return $this->acceptSubString($filename, $this->prefixes, self::PREFIX);
+ }
+
+ /**
+ * @param string $filename
+ * @return bool
+ * @since Method available since Release 1.1.0
+ */
+ protected function acceptSuffix($filename)
+ {
+ return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX);
+ }
+
+ /**
+ * @param string $filename
+ * @param array $subString
+ * @param int $type
+ * @return bool
+ * @since Method available since Release 1.1.0
+ */
+ protected function acceptSubString($filename, array $subStrings, $type)
+ {
+ if (empty($subStrings)) {
+ return TRUE;
+ }
+
+ $matched = FALSE;
+
+ foreach ($subStrings as $string) {
+ if (($type == self::PREFIX && strpos($filename, $string) === 0) ||
+ ($type == self::SUFFIX &&
+ substr($filename, -1 * strlen($string)) == $string)) {
+ $matched = TRUE;
+ break;
+ }
+ }
+
+ return $matched;
+ }
+}
+Text_Template
+
+Copyright (c) 2009-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the Text_Template package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A simple template engine.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class Text_Template
+{
+ /**
+ * @var string
+ */
+ protected $template = '';
+
+ /**
+ * @var string
+ */
+ protected $openDelimiter = '{';
+
+ /**
+ * @var string
+ */
+ protected $closeDelimiter = '}';
+
+ /**
+ * @var array
+ */
+ protected $values = array();
+
+ /**
+ * Constructor.
+ *
+ * @param string $file
+ * @throws InvalidArgumentException
+ */
+ public function __construct($file = '', $openDelimiter = '{', $closeDelimiter = '}')
+ {
+ $this->setFile($file);
+ $this->openDelimiter = $openDelimiter;
+ $this->closeDelimiter = $closeDelimiter;
+ }
+
+ /**
+ * Sets the template file.
+ *
+ * @param string $file
+ * @throws InvalidArgumentException
+ */
+ public function setFile($file)
+ {
+ $distFile = $file . '.dist';
+
+ if (file_exists($file)) {
+ $this->template = file_get_contents($file);
+ }
+
+ else if (file_exists($distFile)) {
+ $this->template = file_get_contents($distFile);
+ }
+
+ else {
+ throw new InvalidArgumentException(
+ 'Template file could not be loaded.'
+ );
+ }
+ }
+
+ /**
+ * Sets one or more template variables.
+ *
+ * @param array $values
+ * @param bool $merge
+ */
+ public function setVar(array $values, $merge = TRUE)
+ {
+ if (!$merge || empty($this->values)) {
+ $this->values = $values;
+ } else {
+ $this->values = array_merge($this->values, $values);
+ }
+ }
+
+ /**
+ * Renders the template and returns the result.
+ *
+ * @return string
+ */
+ public function render()
+ {
+ $keys = array();
+
+ foreach ($this->values as $key => $value) {
+ $keys[] = $this->openDelimiter . $key . $this->closeDelimiter;
+ }
+
+ return str_replace($keys, $this->values, $this->template);
+ }
+
+ /**
+ * Renders the template and writes the result to a file.
+ *
+ * @param string $target
+ */
+ public function renderTo($target)
+ {
+ $fp = @fopen($target, 'wt');
+
+ if ($fp) {
+ fwrite($fp, $this->render());
+ fclose($fp);
+ } else {
+ $error = error_get_last();
+
+ throw new RuntimeException(
+ sprintf(
+ 'Could not write to %s: %s',
+ $target,
+ substr(
+ $error['message'],
+ strpos($error['message'], ':') + 2
+ )
+ )
+ );
+ }
+ }
+}
+
+PHP_Timer
+
+Copyright (c) 2010-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the PHP_Timer package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Utility class for timing.
+ */
+class PHP_Timer
+{
+ /**
+ * @var array
+ */
+ private static $times = array(
+ 'hour' => 3600000,
+ 'minute' => 60000,
+ 'second' => 1000
+ );
+
+ /**
+ * @var array
+ */
+ private static $startTimes = array();
+
+ /**
+ * @var float
+ */
+ public static $requestTime;
+
+ /**
+ * Starts the timer.
+ */
+ public static function start()
+ {
+ array_push(self::$startTimes, microtime(true));
+ }
+
+ /**
+ * Stops the timer and returns the elapsed time.
+ *
+ * @return float
+ */
+ public static function stop()
+ {
+ return microtime(true) - array_pop(self::$startTimes);
+ }
+
+ /**
+ * Formats the elapsed time as a string.
+ *
+ * @param float $time
+ * @return string
+ */
+ public static function secondsToTimeString($time)
+ {
+ $ms = round($time * 1000);
+
+ foreach (self::$times as $unit => $value) {
+ if ($ms >= $value) {
+ $time = floor($ms / $value * 100.0) / 100.0;
+
+ return $time . ' ' . ($time == 1 ? $unit : $unit . 's');
+ }
+ }
+
+ return $ms . ' ms';
+ }
+
+ /**
+ * Formats the elapsed time since the start of the request as a string.
+ *
+ * @return string
+ */
+ public static function timeSinceStartOfRequest()
+ {
+ return self::secondsToTimeString(microtime(true) - self::$requestTime);
+ }
+
+ /**
+ * Returns the resources (time, memory) of the request as a string.
+ *
+ * @return string
+ */
+ public static function resourceUsage()
+ {
+ return sprintf(
+ 'Time: %s, Memory: %4.2fMB',
+ self::timeSinceStartOfRequest(),
+ memory_get_peak_usage(true) / 1048576
+ );
+ }
+}
+
+if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
+ PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
+} elseif (isset($_SERVER['REQUEST_TIME'])) {
+ PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME'];
+} else {
+ PHP_Timer::$requestTime = microtime(true);
+}
+PHP_TokenStream
+
+Copyright (c) 2009-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the PHP_TokenStream package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A PHP token.
+ *
+ * @author Sebastian Bergmann <sebastian@phpunit.de>
+ * @copyright Sebastian Bergmann <sebastian@phpunit.de>
+ * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
+ * @link http://github.com/sebastianbergmann/php-token-stream/tree
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHP_Token
+{
+ /**
+ * @var string
+ */
+ protected $text;
+
+ /**
+ * @var integer
+ */
+ protected $line;
+
+ /**
+ * @var PHP_Token_Stream
+ */
+ protected $tokenStream;
+
+ /**
+ * @var integer
+ */
+ protected $id;
+
+ /**
+ * Constructor.
+ *
+ * @param string $text
+ * @param integer $line
+ * @param PHP_Token_Stream $tokenStream
+ * @param integer $id
+ */
+ public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id)
+ {
+ $this->text = $text;
+ $this->line = $line;
+ $this->tokenStream = $tokenStream;
+ $this->id = $id;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->text;
+ }
+
+ /**
+ * @return integer
+ */
+ public function getLine()
+ {
+ return $this->line;
+ }
+}
+
+abstract class PHP_TokenWithScope extends PHP_Token
+{
+ /**
+ * @var integer
+ */
+ protected $endTokenId;
+
+ /**
+ * Get the docblock for this token
+ *
+ * This method will fetch the docblock belonging to the current token. The
+ * docblock must be placed on the line directly above the token to be
+ * recognized.
+ *
+ * @return string|null Returns the docblock as a string if found
+ */
+ public function getDocblock()
+ {
+ $tokens = $this->tokenStream->tokens();
+ $currentLineNumber = $tokens[$this->id]->getLine();
+ $prevLineNumber = $currentLineNumber - 1;
+
+ for ($i = $this->id - 1; $i; $i--) {
+ if (!isset($tokens[$i])) {
+ return;
+ }
+
+ if ($tokens[$i] instanceof PHP_Token_FUNCTION ||
+ $tokens[$i] instanceof PHP_Token_CLASS ||
+ $tokens[$i] instanceof PHP_Token_TRAIT) {
+ // Some other trait, class or function, no docblock can be
+ // used for the current token
+ break;
+ }
+
+ $line = $tokens[$i]->getLine();
+
+ if ($line == $currentLineNumber ||
+ ($line == $prevLineNumber &&
+ $tokens[$i] instanceof PHP_Token_WHITESPACE)) {
+ continue;
+ }
+
+ if ($line < $currentLineNumber &&
+ !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) {
+ break;
+ }
+
+ return (string)$tokens[$i];
+ }
+ }
+
+ /**
+ * @return integer
+ */
+ public function getEndTokenId()
+ {
+ $block = 0;
+ $i = $this->id;
+ $tokens = $this->tokenStream->tokens();
+
+ while ($this->endTokenId === null && isset($tokens[$i])) {
+ if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY ||
+ $tokens[$i] instanceof PHP_Token_CURLY_OPEN) {
+ $block++;
+ } elseif ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) {
+ $block--;
+
+ if ($block === 0) {
+ $this->endTokenId = $i;
+ }
+ } elseif (($this instanceof PHP_Token_FUNCTION ||
+ $this instanceof PHP_Token_NAMESPACE) &&
+ $tokens[$i] instanceof PHP_Token_SEMICOLON) {
+ if ($block === 0) {
+ $this->endTokenId = $i;
+ }
+ }
+
+ $i++;
+ }
+
+ if ($this->endTokenId === null) {
+ $this->endTokenId = $this->id;
+ }
+
+ return $this->endTokenId;
+ }
+
+ /**
+ * @return integer
+ */
+ public function getEndLine()
+ {
+ return $this->tokenStream[$this->getEndTokenId()]->getLine();
+ }
+}
+
+abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope
+{
+ /**
+ * @return string
+ */
+ public function getVisibility()
+ {
+ $tokens = $this->tokenStream->tokens();
+
+ for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) {
+ if (isset($tokens[$i]) &&
+ ($tokens[$i] instanceof PHP_Token_PRIVATE ||
+ $tokens[$i] instanceof PHP_Token_PROTECTED ||
+ $tokens[$i] instanceof PHP_Token_PUBLIC)) {
+ return strtolower(
+ str_replace('PHP_Token_', '', get_class($tokens[$i]))
+ );
+ }
+ if (isset($tokens[$i]) &&
+ !($tokens[$i] instanceof PHP_Token_STATIC ||
+ $tokens[$i] instanceof PHP_Token_FINAL ||
+ $tokens[$i] instanceof PHP_Token_ABSTRACT)) {
+ // no keywords; stop visibility search
+ break;
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getKeywords()
+ {
+ $keywords = array();
+ $tokens = $this->tokenStream->tokens();
+
+ for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) {
+ if (isset($tokens[$i]) &&
+ ($tokens[$i] instanceof PHP_Token_PRIVATE ||
+ $tokens[$i] instanceof PHP_Token_PROTECTED ||
+ $tokens[$i] instanceof PHP_Token_PUBLIC)) {
+ continue;
+ }
+
+ if (isset($tokens[$i]) &&
+ ($tokens[$i] instanceof PHP_Token_STATIC ||
+ $tokens[$i] instanceof PHP_Token_FINAL ||
+ $tokens[$i] instanceof PHP_Token_ABSTRACT)) {
+ $keywords[] = strtolower(
+ str_replace('PHP_Token_', '', get_class($tokens[$i]))
+ );
+ }
+ }
+
+ return implode(',', $keywords);
+ }
+}
+
+abstract class PHP_Token_Includes extends PHP_Token
+{
+ /**
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * @var string
+ */
+ protected $type;
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ if ($this->name === null) {
+ $this->process();
+ }
+
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType()
+ {
+ if ($this->type === null) {
+ $this->process();
+ }
+
+ return $this->type;
+ }
+
+ private function process()
+ {
+ $tokens = $this->tokenStream->tokens();
+
+ if ($tokens[$this->id+2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) {
+ $this->name = trim($tokens[$this->id+2], "'\"");
+ $this->type = strtolower(
+ str_replace('PHP_Token_', '', get_class($tokens[$this->id]))
+ );
+ }
+ }
+}
+
+
+class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility
+{
+ /**
+ * @var array
+ */
+ protected $arguments;
+
+ /**
+ * @var integer
+ */
+ protected $ccn;
+
+ /**
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * @var string
+ */
+ protected $signature;
+
+ /**
+ * @return array
+ */
+ public function getArguments()
+ {
+ if ($this->arguments !== null) {
+ return $this->arguments;
+ }
+
+ $this->arguments = array();
+ $tokens = $this->tokenStream->tokens();
+ $typeDeclaration = null;
+
+ // Search for first token inside brackets
+ $i = $this->id + 2;
+
+ while (!$tokens[$i-1] instanceof PHP_Token_OPEN_BRACKET) {
+ $i++;
+ }
+
+ while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) {
+ if ($tokens[$i] instanceof PHP_Token_STRING) {
+ $typeDeclaration = (string)$tokens[$i];
+ } elseif ($tokens[$i] instanceof PHP_Token_VARIABLE) {
+ $this->arguments[(string)$tokens[$i]] = $typeDeclaration;
+ $typeDeclaration = null;
+ }
+
+ $i++;
+ }
+
+ return $this->arguments;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ if ($this->name !== null) {
+ return $this->name;
+ }
+
+ $tokens = $this->tokenStream->tokens();
+
+ for ($i = $this->id + 1; $i < count($tokens); $i++) {
+ if ($tokens[$i] instanceof PHP_Token_STRING) {
+ $this->name = (string)$tokens[$i];
+ break;
+ } elseif ($tokens[$i] instanceof PHP_Token_AMPERSAND &&
+ $tokens[$i+1] instanceof PHP_Token_STRING) {
+ $this->name = (string)$tokens[$i+1];
+ break;
+ } elseif ($tokens[$i] instanceof PHP_Token_OPEN_BRACKET) {
+ $this->name = 'anonymous function';
+ break;
+ }
+ }
+
+ if ($this->name != 'anonymous function') {
+ for ($i = $this->id; $i; --$i) {
+ if ($tokens[$i] instanceof PHP_Token_NAMESPACE) {
+ $this->name = $tokens[$i]->getName() . '\\' . $this->name;
+ break;
+ }
+
+ if ($tokens[$i] instanceof PHP_Token_INTERFACE) {
+ break;
+ }
+ }
+ }
+
+ return $this->name;
+ }
+
+ /**
+ * @return integer
+ */
+ public function getCCN()
+ {
+ if ($this->ccn !== null) {
+ return $this->ccn;
+ }
+
+ $this->ccn = 1;
+ $end = $this->getEndTokenId();
+ $tokens = $this->tokenStream->tokens();
+
+ for ($i = $this->id; $i <= $end; $i++) {
+ switch (get_class($tokens[$i])) {
+ case 'PHP_Token_IF':
+ case 'PHP_Token_ELSEIF':
+ case 'PHP_Token_FOR':
+ case 'PHP_Token_FOREACH':
+ case 'PHP_Token_WHILE':
+ case 'PHP_Token_CASE':
+ case 'PHP_Token_CATCH':
+ case 'PHP_Token_BOOLEAN_AND':
+ case 'PHP_Token_LOGICAL_AND':
+ case 'PHP_Token_BOOLEAN_OR':
+ case 'PHP_Token_LOGICAL_OR':
+ case 'PHP_Token_QUESTION_MARK':
+ $this->ccn++;
+ break;
+ }
+ }
+
+ return $this->ccn;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSignature()
+ {
+ if ($this->signature !== null) {
+ return $this->signature;
+ }
+
+ if ($this->getName() == 'anonymous function') {
+ $this->signature = 'anonymous function';
+ $i = $this->id + 1;
+ } else {
+ $this->signature = '';
+ $i = $this->id + 2;
+ }
+
+ $tokens = $this->tokenStream->tokens();
+
+ while (isset($tokens[$i]) &&
+ !$tokens[$i] instanceof PHP_Token_OPEN_CURLY &&
+ !$tokens[$i] instanceof PHP_Token_SEMICOLON) {
+ $this->signature .= $tokens[$i++];
+ }
+
+ $this->signature = trim($this->signature);
+
+ return $this->signature;
+ }
+}
+
+class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility
+{
+ /**
+ * @var array
+ */
+ protected $interfaces;
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return (string)$this->tokenStream[$this->id + 2];
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasParent()
+ {
+ return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS;
+ }
+
+ /**
+ * @return array
+ */
+ public function getPackage()
+ {
+ $className = $this->getName();
+ $docComment = $this->getDocblock();
+
+ $result = array(
+ 'namespace' => '',
+ 'fullPackage' => '',
+ 'category' => '',
+ 'package' => '',
+ 'subpackage' => ''
+ );
+
+ for ($i = $this->id; $i; --$i) {
+ if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) {
+ $result['namespace'] = $this->tokenStream[$i]->getName();
+ break;
+ }
+ }
+
+ if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) {
+ $result['category'] = $matches[1];
+ }
+
+ if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) {
+ $result['package'] = $matches[1];
+ $result['fullPackage'] = $matches[1];
+ }
+
+ if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) {
+ $result['subpackage'] = $matches[1];
+ $result['fullPackage'] .= '.' . $matches[1];
+ }
+
+ if (empty($result['fullPackage'])) {
+ $result['fullPackage'] = $this->arrayToName(
+ explode('_', str_replace('\\', '_', $className)),
+ '.'
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array $parts
+ * @param string $join
+ * @return string
+ */
+ protected function arrayToName(array $parts, $join = '\\')
+ {
+ $result = '';
+
+ if (count($parts) > 1) {
+ array_pop($parts);
+
+ $result = join($join, $parts);
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return boolean|string
+ */
+ public function getParent()
+ {
+ if (!$this->hasParent()) {
+ return false;
+ }
+
+ $i = $this->id + 6;
+ $tokens = $this->tokenStream->tokens();
+ $className = (string)$tokens[$i];
+
+ while (isset($tokens[$i+1]) &&
+ !$tokens[$i+1] instanceof PHP_Token_WHITESPACE) {
+ $className .= (string)$tokens[++$i];
+ }
+
+ return $className;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasInterfaces()
+ {
+ return (isset($this->tokenStream[$this->id + 4]) &&
+ $this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) ||
+ (isset($this->tokenStream[$this->id + 8]) &&
+ $this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS);
+ }
+
+ /**
+ * @return array|boolean
+ */
+ public function getInterfaces()
+ {
+ if ($this->interfaces !== null) {
+ return $this->interfaces;
+ }
+
+ if (!$this->hasInterfaces()) {
+ return ($this->interfaces = false);
+ }
+
+ if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) {
+ $i = $this->id + 3;
+ } else {
+ $i = $this->id + 7;
+ }
+
+ $tokens = $this->tokenStream->tokens();
+
+ while (!$tokens[$i+1] instanceof PHP_Token_OPEN_CURLY) {
+ $i++;
+
+ if ($tokens[$i] instanceof PHP_Token_STRING) {
+ $this->interfaces[] = (string)$tokens[$i];
+ }
+ }
+
+ return $this->interfaces;
+ }
+}
+
+class PHP_Token_ABSTRACT extends PHP_Token {}
+class PHP_Token_AMPERSAND extends PHP_Token {}
+class PHP_Token_AND_EQUAL extends PHP_Token {}
+class PHP_Token_ARRAY extends PHP_Token {}
+class PHP_Token_ARRAY_CAST extends PHP_Token {}
+class PHP_Token_AS extends PHP_Token {}
+class PHP_Token_AT extends PHP_Token {}
+class PHP_Token_BACKTICK extends PHP_Token {}
+class PHP_Token_BAD_CHARACTER extends PHP_Token {}
+class PHP_Token_BOOLEAN_AND extends PHP_Token {}
+class PHP_Token_BOOLEAN_OR extends PHP_Token {}
+class PHP_Token_BOOL_CAST extends PHP_Token {}
+class PHP_Token_BREAK extends PHP_Token {}
+class PHP_Token_CARET extends PHP_Token {}
+class PHP_Token_CASE extends PHP_Token {}
+class PHP_Token_CATCH extends PHP_Token {}
+class PHP_Token_CHARACTER extends PHP_Token {}
+
+class PHP_Token_CLASS extends PHP_Token_INTERFACE
+{
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ $next = $this->tokenStream[$this->id + 1];
+
+ if ($next instanceof PHP_Token_WHITESPACE) {
+ $next = $this->tokenStream[$this->id + 2];
+ }
+
+ if ($next instanceof PHP_Token_STRING) {
+ return (string) $next;
+ }
+
+ if ($next instanceof PHP_Token_OPEN_CURLY ||
+ $next instanceof PHP_Token_EXTENDS ||
+ $next instanceof PHP_Token_IMPLEMENTS) {
+ return 'anonymous class';
+ }
+ }
+}
+
+class PHP_Token_CLASS_C extends PHP_Token {}
+class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token {}
+class PHP_Token_CLONE extends PHP_Token {}
+class PHP_Token_CLOSE_BRACKET extends PHP_Token {}
+class PHP_Token_CLOSE_CURLY extends PHP_Token {}
+class PHP_Token_CLOSE_SQUARE extends PHP_Token {}
+class PHP_Token_CLOSE_TAG extends PHP_Token {}
+class PHP_Token_COLON extends PHP_Token {}
+class PHP_Token_COMMA extends PHP_Token {}
+class PHP_Token_COMMENT extends PHP_Token {}
+class PHP_Token_CONCAT_EQUAL extends PHP_Token {}
+class PHP_Token_CONST extends PHP_Token {}
+class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token {}
+class PHP_Token_CONTINUE extends PHP_Token {}
+class PHP_Token_CURLY_OPEN extends PHP_Token {}
+class PHP_Token_DEC extends PHP_Token {}
+class PHP_Token_DECLARE extends PHP_Token {}
+class PHP_Token_DEFAULT extends PHP_Token {}
+class PHP_Token_DIV extends PHP_Token {}
+class PHP_Token_DIV_EQUAL extends PHP_Token {}
+class PHP_Token_DNUMBER extends PHP_Token {}
+class PHP_Token_DO extends PHP_Token {}
+class PHP_Token_DOC_COMMENT extends PHP_Token {}
+class PHP_Token_DOLLAR extends PHP_Token {}
+class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token {}
+class PHP_Token_DOT extends PHP_Token {}
+class PHP_Token_DOUBLE_ARROW extends PHP_Token {}
+class PHP_Token_DOUBLE_CAST extends PHP_Token {}
+class PHP_Token_DOUBLE_COLON extends PHP_Token {}
+class PHP_Token_DOUBLE_QUOTES extends PHP_Token {}
+class PHP_Token_ECHO extends PHP_Token {}
+class PHP_Token_ELSE extends PHP_Token {}
+class PHP_Token_ELSEIF extends PHP_Token {}
+class PHP_Token_EMPTY extends PHP_Token {}
+class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token {}
+class PHP_Token_ENDDECLARE extends PHP_Token {}
+class PHP_Token_ENDFOR extends PHP_Token {}
+class PHP_Token_ENDFOREACH extends PHP_Token {}
+class PHP_Token_ENDIF extends PHP_Token {}
+class PHP_Token_ENDSWITCH extends PHP_Token {}
+class PHP_Token_ENDWHILE extends PHP_Token {}
+class PHP_Token_END_HEREDOC extends PHP_Token {}
+class PHP_Token_EQUAL extends PHP_Token {}
+class PHP_Token_EVAL extends PHP_Token {}
+class PHP_Token_EXCLAMATION_MARK extends PHP_Token {}
+class PHP_Token_EXIT extends PHP_Token {}
+class PHP_Token_EXTENDS extends PHP_Token {}
+class PHP_Token_FILE extends PHP_Token {}
+class PHP_Token_FINAL extends PHP_Token {}
+class PHP_Token_FOR extends PHP_Token {}
+class PHP_Token_FOREACH extends PHP_Token {}
+class PHP_Token_FUNC_C extends PHP_Token {}
+class PHP_Token_GLOBAL extends PHP_Token {}
+class PHP_Token_GT extends PHP_Token {}
+class PHP_Token_IF extends PHP_Token {}
+class PHP_Token_IMPLEMENTS extends PHP_Token {}
+class PHP_Token_INC extends PHP_Token {}
+class PHP_Token_INCLUDE extends PHP_Token_Includes {}
+class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes {}
+class PHP_Token_INLINE_HTML extends PHP_Token {}
+class PHP_Token_INSTANCEOF extends PHP_Token {}
+class PHP_Token_INT_CAST extends PHP_Token {}
+class PHP_Token_ISSET extends PHP_Token {}
+class PHP_Token_IS_EQUAL extends PHP_Token {}
+class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token {}
+class PHP_Token_IS_IDENTICAL extends PHP_Token {}
+class PHP_Token_IS_NOT_EQUAL extends PHP_Token {}
+class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token {}
+class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token {}
+class PHP_Token_LINE extends PHP_Token {}
+class PHP_Token_LIST extends PHP_Token {}
+class PHP_Token_LNUMBER extends PHP_Token {}
+class PHP_Token_LOGICAL_AND extends PHP_Token {}
+class PHP_Token_LOGICAL_OR extends PHP_Token {}
+class PHP_Token_LOGICAL_XOR extends PHP_Token {}
+class PHP_Token_LT extends PHP_Token {}
+class PHP_Token_METHOD_C extends PHP_Token {}
+class PHP_Token_MINUS extends PHP_Token {}
+class PHP_Token_MINUS_EQUAL extends PHP_Token {}
+class PHP_Token_MOD_EQUAL extends PHP_Token {}
+class PHP_Token_MULT extends PHP_Token {}
+class PHP_Token_MUL_EQUAL extends PHP_Token {}
+class PHP_Token_NEW extends PHP_Token {}
+class PHP_Token_NUM_STRING extends PHP_Token {}
+class PHP_Token_OBJECT_CAST extends PHP_Token {}
+class PHP_Token_OBJECT_OPERATOR extends PHP_Token {}
+class PHP_Token_OPEN_BRACKET extends PHP_Token {}
+class PHP_Token_OPEN_CURLY extends PHP_Token {}
+class PHP_Token_OPEN_SQUARE extends PHP_Token {}
+class PHP_Token_OPEN_TAG extends PHP_Token {}
+class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token {}
+class PHP_Token_OR_EQUAL extends PHP_Token {}
+class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token {}
+class PHP_Token_PERCENT extends PHP_Token {}
+class PHP_Token_PIPE extends PHP_Token {}
+class PHP_Token_PLUS extends PHP_Token {}
+class PHP_Token_PLUS_EQUAL extends PHP_Token {}
+class PHP_Token_PRINT extends PHP_Token {}
+class PHP_Token_PRIVATE extends PHP_Token {}
+class PHP_Token_PROTECTED extends PHP_Token {}
+class PHP_Token_PUBLIC extends PHP_Token {}
+class PHP_Token_QUESTION_MARK extends PHP_Token {}
+class PHP_Token_REQUIRE extends PHP_Token_Includes {}
+class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes {}
+class PHP_Token_RETURN extends PHP_Token {}
+class PHP_Token_SEMICOLON extends PHP_Token {}
+class PHP_Token_SL extends PHP_Token {}
+class PHP_Token_SL_EQUAL extends PHP_Token {}
+class PHP_Token_SR extends PHP_Token {}
+class PHP_Token_SR_EQUAL extends PHP_Token {}
+class PHP_Token_START_HEREDOC extends PHP_Token {}
+class PHP_Token_STATIC extends PHP_Token {}
+class PHP_Token_STRING extends PHP_Token {}
+class PHP_Token_STRING_CAST extends PHP_Token {}
+class PHP_Token_STRING_VARNAME extends PHP_Token {}
+class PHP_Token_SWITCH extends PHP_Token {}
+class PHP_Token_THROW extends PHP_Token {}
+class PHP_Token_TILDE extends PHP_Token {}
+class PHP_Token_TRY extends PHP_Token {}
+class PHP_Token_UNSET extends PHP_Token {}
+class PHP_Token_UNSET_CAST extends PHP_Token {}
+class PHP_Token_USE extends PHP_Token {}
+class PHP_Token_USE_FUNCTION extends PHP_Token {}
+class PHP_Token_VAR extends PHP_Token {}
+class PHP_Token_VARIABLE extends PHP_Token {}
+class PHP_Token_WHILE extends PHP_Token {}
+class PHP_Token_WHITESPACE extends PHP_Token {}
+class PHP_Token_XOR_EQUAL extends PHP_Token {}
+
+// Tokens introduced in PHP 5.1
+class PHP_Token_HALT_COMPILER extends PHP_Token {}
+
+// Tokens introduced in PHP 5.3
+class PHP_Token_DIR extends PHP_Token {}
+class PHP_Token_GOTO extends PHP_Token {}
+
+class PHP_Token_NAMESPACE extends PHP_TokenWithScope
+{
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ $tokens = $this->tokenStream->tokens();
+ $namespace = (string)$tokens[$this->id+2];
+
+ for ($i = $this->id + 3;; $i += 2) {
+ if (isset($tokens[$i]) &&
+ $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) {
+ $namespace .= '\\' . $tokens[$i+1];
+ } else {
+ break;
+ }
+ }
+
+ return $namespace;
+ }
+}
+
+class PHP_Token_NS_C extends PHP_Token {}
+class PHP_Token_NS_SEPARATOR extends PHP_Token {}
+
+// Tokens introduced in PHP 5.4
+class PHP_Token_CALLABLE extends PHP_Token {}
+class PHP_Token_INSTEADOF extends PHP_Token {}
+class PHP_Token_TRAIT extends PHP_Token_INTERFACE {}
+class PHP_Token_TRAIT_C extends PHP_Token {}
+
+// Tokens introduced in PHP 5.5
+class PHP_Token_FINALLY extends PHP_Token {}
+class PHP_Token_YIELD extends PHP_Token {}
+
+// Tokens introduced in PHP 5.6
+class PHP_Token_ELLIPSIS extends PHP_Token {}
+class PHP_Token_POW extends PHP_Token {}
+class PHP_Token_POW_EQUAL extends PHP_Token {}
+
+// Tokens introduced in PHP 7.0
+class PHP_Token_COALESCE extends PHP_Token {}
+class PHP_Token_SPACESHIP extends PHP_Token {}
+class PHP_Token_YIELD_FROM extends PHP_Token {}
+
+// Tokens introduced in HackLang / HHVM
+class PHP_Token_ASYNC extends PHP_Token {}
+class PHP_Token_AWAIT extends PHP_Token {}
+class PHP_Token_COMPILER_HALT_OFFSET extends PHP_Token {}
+class PHP_Token_ENUM extends PHP_Token {}
+class PHP_Token_EQUALS extends PHP_Token {}
+class PHP_Token_IN extends PHP_Token {}
+class PHP_Token_JOIN extends PHP_Token {}
+class PHP_Token_LAMBDA_ARROW extends PHP_Token {}
+class PHP_Token_LAMBDA_CP extends PHP_Token {}
+class PHP_Token_LAMBDA_OP extends PHP_Token {}
+class PHP_Token_ONUMBER extends PHP_Token {}
+class PHP_Token_NULLSAFE_OBJECT_OPERATOR extends PHP_Token {}
+class PHP_Token_SHAPE extends PHP_Token {}
+class PHP_Token_SUPER extends PHP_Token {}
+class PHP_Token_TYPE extends PHP_Token {}
+class PHP_Token_TYPELIST_GT extends PHP_Token {}
+class PHP_Token_TYPELIST_LT extends PHP_Token {}
+class PHP_Token_WHERE extends PHP_Token {}
+class PHP_Token_XHP_ATTRIBUTE extends PHP_Token {}
+class PHP_Token_XHP_CATEGORY extends PHP_Token {}
+class PHP_Token_XHP_CATEGORY_LABEL extends PHP_Token {}
+class PHP_Token_XHP_CHILDREN extends PHP_Token {}
+class PHP_Token_XHP_LABEL extends PHP_Token {}
+class PHP_Token_XHP_REQUIRED extends PHP_Token {}
+class PHP_Token_XHP_TAG_GT extends PHP_Token {}
+class PHP_Token_XHP_TAG_LT extends PHP_Token {}
+class PHP_Token_XHP_TEXT extends PHP_Token {}
+<?php
+/*
+ * This file is part of the PHP_TokenStream package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A stream of PHP tokens.
+ *
+ * @author Sebastian Bergmann <sebastian@phpunit.de>
+ * @copyright Sebastian Bergmann <sebastian@phpunit.de>
+ * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
+ * @link http://github.com/sebastianbergmann/php-token-stream/tree
+ * @since Class available since Release 1.0.0
+ */
+class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator
+{
+ /**
+ * @var array
+ */
+ protected static $customTokens = array(
+ '(' => 'PHP_Token_OPEN_BRACKET',
+ ')' => 'PHP_Token_CLOSE_BRACKET',
+ '[' => 'PHP_Token_OPEN_SQUARE',
+ ']' => 'PHP_Token_CLOSE_SQUARE',
+ '{' => 'PHP_Token_OPEN_CURLY',
+ '}' => 'PHP_Token_CLOSE_CURLY',
+ ';' => 'PHP_Token_SEMICOLON',
+ '.' => 'PHP_Token_DOT',
+ ',' => 'PHP_Token_COMMA',
+ '=' => 'PHP_Token_EQUAL',
+ '<' => 'PHP_Token_LT',
+ '>' => 'PHP_Token_GT',
+ '+' => 'PHP_Token_PLUS',
+ '-' => 'PHP_Token_MINUS',
+ '*' => 'PHP_Token_MULT',
+ '/' => 'PHP_Token_DIV',
+ '?' => 'PHP_Token_QUESTION_MARK',
+ '!' => 'PHP_Token_EXCLAMATION_MARK',
+ ':' => 'PHP_Token_COLON',
+ '"' => 'PHP_Token_DOUBLE_QUOTES',
+ '@' => 'PHP_Token_AT',
+ '&' => 'PHP_Token_AMPERSAND',
+ '%' => 'PHP_Token_PERCENT',
+ '|' => 'PHP_Token_PIPE',
+ '$' => 'PHP_Token_DOLLAR',
+ '^' => 'PHP_Token_CARET',
+ '~' => 'PHP_Token_TILDE',
+ '`' => 'PHP_Token_BACKTICK'
+ );
+
+ /**
+ * @var string
+ */
+ protected $filename;
+
+ /**
+ * @var array
+ */
+ protected $tokens = array();
+
+ /**
+ * @var integer
+ */
+ protected $position = 0;
+
+ /**
+ * @var array
+ */
+ protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0);
+
+ /**
+ * @var array
+ */
+ protected $classes;
+
+ /**
+ * @var array
+ */
+ protected $functions;
+
+ /**
+ * @var array
+ */
+ protected $includes;
+
+ /**
+ * @var array
+ */
+ protected $interfaces;
+
+ /**
+ * @var array
+ */
+ protected $traits;
+
+ /**
+ * @var array
+ */
+ protected $lineToFunctionMap = array();
+
+ /**
+ * Constructor.
+ *
+ * @param string $sourceCode
+ */
+ public function __construct($sourceCode)
+ {
+ if (is_file($sourceCode)) {
+ $this->filename = $sourceCode;
+ $sourceCode = file_get_contents($sourceCode);
+ }
+
+ $this->scan($sourceCode);
+ }
+
+ /**
+ * Destructor.
+ */
+ public function __destruct()
+ {
+ $this->tokens = array();
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ $buffer = '';
+
+ foreach ($this as $token) {
+ $buffer .= $token;
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @return string
+ * @since Method available since Release 1.1.0
+ */
+ public function getFilename()
+ {
+ return $this->filename;
+ }
+
+ /**
+ * Scans the source for sequences of characters and converts them into a
+ * stream of tokens.
+ *
+ * @param string $sourceCode
+ */
+ protected function scan($sourceCode)
+ {
+ $id = 0;
+ $line = 1;
+ $tokens = token_get_all($sourceCode);
+ $numTokens = count($tokens);
+
+ $lastNonWhitespaceTokenWasDoubleColon = false;
+
+ for ($i = 0; $i < $numTokens; ++$i) {
+ $token = $tokens[$i];
+ $skip = 0;
+
+ if (is_array($token)) {
+ $name = substr(token_name($token[0]), 2);
+ $text = $token[1];
+
+ if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') {
+ $name = 'CLASS_NAME_CONSTANT';
+ } elseif ($name == 'USE' && isset($tokens[$i+2][0]) && $tokens[$i+2][0] == T_FUNCTION) {
+ $name = 'USE_FUNCTION';
+ $skip = 2;
+ }
+
+ $tokenClass = 'PHP_Token_' . $name;
+ } else {
+ $text = $token;
+ $tokenClass = self::$customTokens[$token];
+ }
+
+ $this->tokens[] = new $tokenClass($text, $line, $this, $id++);
+ $lines = substr_count($text, "\n");
+ $line += $lines;
+
+ if ($tokenClass == 'PHP_Token_HALT_COMPILER') {
+ break;
+ } elseif ($tokenClass == 'PHP_Token_COMMENT' ||
+ $tokenClass == 'PHP_Token_DOC_COMMENT') {
+ $this->linesOfCode['cloc'] += $lines + 1;
+ }
+
+ if ($name == 'DOUBLE_COLON') {
+ $lastNonWhitespaceTokenWasDoubleColon = true;
+ } elseif ($name != 'WHITESPACE') {
+ $lastNonWhitespaceTokenWasDoubleColon = false;
+ }
+
+ $i += $skip;
+ }
+
+ $this->linesOfCode['loc'] = substr_count($sourceCode, "\n");
+ $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] -
+ $this->linesOfCode['cloc'];
+ }
+
+ /**
+ * @return integer
+ */
+ public function count()
+ {
+ return count($this->tokens);
+ }
+
+ /**
+ * @return PHP_Token[]
+ */
+ public function tokens()
+ {
+ return $this->tokens;
+ }
+
+ /**
+ * @return array
+ */
+ public function getClasses()
+ {
+ if ($this->classes !== null) {
+ return $this->classes;
+ }
+
+ $this->parse();
+
+ return $this->classes;
+ }
+
+ /**
+ * @return array
+ */
+ public function getFunctions()
+ {
+ if ($this->functions !== null) {
+ return $this->functions;
+ }
+
+ $this->parse();
+
+ return $this->functions;
+ }
+
+ /**
+ * @return array
+ */
+ public function getInterfaces()
+ {
+ if ($this->interfaces !== null) {
+ return $this->interfaces;
+ }
+
+ $this->parse();
+
+ return $this->interfaces;
+ }
+
+ /**
+ * @return array
+ * @since Method available since Release 1.1.0
+ */
+ public function getTraits()
+ {
+ if ($this->traits !== null) {
+ return $this->traits;
+ }
+
+ $this->parse();
+
+ return $this->traits;
+ }
+
+ /**
+ * Gets the names of all files that have been included
+ * using include(), include_once(), require() or require_once().
+ *
+ * Parameter $categorize set to TRUE causing this function to return a
+ * multi-dimensional array with categories in the keys of the first dimension
+ * and constants and their values in the second dimension.
+ *
+ * Parameter $category allow to filter following specific inclusion type
+ *
+ * @param bool $categorize OPTIONAL
+ * @param string $category OPTIONAL Either 'require_once', 'require',
+ * 'include_once', 'include'.
+ * @return array
+ * @since Method available since Release 1.1.0
+ */
+ public function getIncludes($categorize = false, $category = null)
+ {
+ if ($this->includes === null) {
+ $this->includes = array(
+ 'require_once' => array(),
+ 'require' => array(),
+ 'include_once' => array(),
+ 'include' => array()
+ );
+
+ foreach ($this->tokens as $token) {
+ switch (get_class($token)) {
+ case 'PHP_Token_REQUIRE_ONCE':
+ case 'PHP_Token_REQUIRE':
+ case 'PHP_Token_INCLUDE_ONCE':
+ case 'PHP_Token_INCLUDE':
+ $this->includes[$token->getType()][] = $token->getName();
+ break;
+ }
+ }
+ }
+
+ if (isset($this->includes[$category])) {
+ $includes = $this->includes[$category];
+ } elseif ($categorize === false) {
+ $includes = array_merge(
+ $this->includes['require_once'],
+ $this->includes['require'],
+ $this->includes['include_once'],
+ $this->includes['include']
+ );
+ } else {
+ $includes = $this->includes;
+ }
+
+ return $includes;
+ }
+
+ /**
+ * Returns the name of the function or method a line belongs to.
+ *
+ * @return string or null if the line is not in a function or method
+ * @since Method available since Release 1.2.0
+ */
+ public function getFunctionForLine($line)
+ {
+ $this->parse();
+
+ if (isset($this->lineToFunctionMap[$line])) {
+ return $this->lineToFunctionMap[$line];
+ }
+ }
+
+ protected function parse()
+ {
+ $this->interfaces = array();
+ $this->classes = array();
+ $this->traits = array();
+ $this->functions = array();
+ $class = array();
+ $classEndLine = array();
+ $trait = false;
+ $traitEndLine = false;
+ $interface = false;
+ $interfaceEndLine = false;
+
+ foreach ($this->tokens as $token) {
+ switch (get_class($token)) {
+ case 'PHP_Token_HALT_COMPILER':
+ return;
+
+ case 'PHP_Token_INTERFACE':
+ $interface = $token->getName();
+ $interfaceEndLine = $token->getEndLine();
+
+ $this->interfaces[$interface] = array(
+ 'methods' => array(),
+ 'parent' => $token->getParent(),
+ 'keywords' => $token->getKeywords(),
+ 'docblock' => $token->getDocblock(),
+ 'startLine' => $token->getLine(),
+ 'endLine' => $interfaceEndLine,
+ 'package' => $token->getPackage(),
+ 'file' => $this->filename
+ );
+ break;
+
+ case 'PHP_Token_CLASS':
+ case 'PHP_Token_TRAIT':
+ $tmp = array(
+ 'methods' => array(),
+ 'parent' => $token->getParent(),
+ 'interfaces'=> $token->getInterfaces(),
+ 'keywords' => $token->getKeywords(),
+ 'docblock' => $token->getDocblock(),
+ 'startLine' => $token->getLine(),
+ 'endLine' => $token->getEndLine(),
+ 'package' => $token->getPackage(),
+ 'file' => $this->filename
+ );
+
+ if ($token instanceof PHP_Token_CLASS) {
+ $class[] = $token->getName();
+ $classEndLine[] = $token->getEndLine();
+
+ if ($class[count($class)-1] != 'anonymous class') {
+ $this->classes[$class[count($class)-1]] = $tmp;
+ }
+ } else {
+ $trait = $token->getName();
+ $traitEndLine = $token->getEndLine();
+ $this->traits[$trait] = $tmp;
+ }
+ break;
+
+ case 'PHP_Token_FUNCTION':
+ $name = $token->getName();
+ $tmp = array(
+ 'docblock' => $token->getDocblock(),
+ 'keywords' => $token->getKeywords(),
+ 'visibility'=> $token->getVisibility(),
+ 'signature' => $token->getSignature(),
+ 'startLine' => $token->getLine(),
+ 'endLine' => $token->getEndLine(),
+ 'ccn' => $token->getCCN(),
+ 'file' => $this->filename
+ );
+
+ if (empty($class) &&
+ $trait === false &&
+ $interface === false) {
+ $this->functions[$name] = $tmp;
+
+ $this->addFunctionToMap(
+ $name,
+ $tmp['startLine'],
+ $tmp['endLine']
+ );
+ } elseif (!empty($class) && $class[count($class)-1] != 'anonymous class') {
+ $this->classes[$class[count($class)-1]]['methods'][$name] = $tmp;
+
+ $this->addFunctionToMap(
+ $class[count($class)-1] . '::' . $name,
+ $tmp['startLine'],
+ $tmp['endLine']
+ );
+ } elseif ($trait !== false) {
+ $this->traits[$trait]['methods'][$name] = $tmp;
+
+ $this->addFunctionToMap(
+ $trait . '::' . $name,
+ $tmp['startLine'],
+ $tmp['endLine']
+ );
+ } else {
+ $this->interfaces[$interface]['methods'][$name] = $tmp;
+ }
+ break;
+
+ case 'PHP_Token_CLOSE_CURLY':
+ if (!empty($classEndLine) &&
+ $classEndLine[count($classEndLine)-1] == $token->getLine()) {
+ array_pop($classEndLine);
+ array_pop($class);
+ } elseif ($traitEndLine !== false &&
+ $traitEndLine == $token->getLine()) {
+ $trait = false;
+ $traitEndLine = false;
+ } elseif ($interfaceEndLine !== false &&
+ $interfaceEndLine == $token->getLine()) {
+ $interface = false;
+ $interfaceEndLine = false;
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getLinesOfCode()
+ {
+ return $this->linesOfCode;
+ }
+
+ /**
+ */
+ public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function valid()
+ {
+ return isset($this->tokens[$this->position]);
+ }
+
+ /**
+ * @return integer
+ */
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * @return PHP_Token
+ */
+ public function current()
+ {
+ return $this->tokens[$this->position];
+ }
+
+ /**
+ */
+ public function next()
+ {
+ $this->position++;
+ }
+
+ /**
+ * @param integer $offset
+ * @return boolean
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->tokens[$offset]);
+ }
+
+ /**
+ * @param integer $offset
+ * @return mixed
+ * @throws OutOfBoundsException
+ */
+ public function offsetGet($offset)
+ {
+ if (!$this->offsetExists($offset)) {
+ throw new OutOfBoundsException(
+ sprintf(
+ 'No token at position "%s"',
+ $offset
+ )
+ );
+ }
+
+ return $this->tokens[$offset];
+ }
+
+ /**
+ * @param integer $offset
+ * @param mixed $value
+ */
+ public function offsetSet($offset, $value)
+ {
+ $this->tokens[$offset] = $value;
+ }
+
+ /**
+ * @param integer $offset
+ * @throws OutOfBoundsException
+ */
+ public function offsetUnset($offset)
+ {
+ if (!$this->offsetExists($offset)) {
+ throw new OutOfBoundsException(
+ sprintf(
+ 'No token at position "%s"',
+ $offset
+ )
+ );
+ }
+
+ unset($this->tokens[$offset]);
+ }
+
+ /**
+ * Seek to an absolute position.
+ *
+ * @param integer $position
+ * @throws OutOfBoundsException
+ */
+ public function seek($position)
+ {
+ $this->position = $position;
+
+ if (!$this->valid()) {
+ throw new OutOfBoundsException(
+ sprintf(
+ 'No token at position "%s"',
+ $this->position
+ )
+ );
+ }
+ }
+
+ /**
+ * @param string $name
+ * @param integer $startLine
+ * @param integer $endLine
+ */
+ private function addFunctionToMap($name, $startLine, $endLine)
+ {
+ for ($line = $startLine; $line <= $endLine; $line++) {
+ $this->lineToFunctionMap[$line] = $name;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHP_TokenStream package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A caching factory for token stream objects.
+ *
+ * @author Sebastian Bergmann <sebastian@phpunit.de>
+ * @copyright Sebastian Bergmann <sebastian@phpunit.de>
+ * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
+ * @link http://github.com/sebastianbergmann/php-token-stream/tree
+ * @since Class available since Release 1.0.0
+ */
+class PHP_Token_Stream_CachingFactory
+{
+ /**
+ * @var array
+ */
+ protected static $cache = array();
+
+ /**
+ * @param string $filename
+ * @return PHP_Token_Stream
+ */
+ public static function get($filename)
+ {
+ if (!isset(self::$cache[$filename])) {
+ self::$cache[$filename] = new PHP_Token_Stream($filename);
+ }
+
+ return self::$cache[$filename];
+ }
+
+ /**
+ * @param string $filename
+ */
+ public static function clear($filename = null)
+ {
+ if (is_string($filename)) {
+ unset(self::$cache[$filename]);
+ } else {
+ self::$cache = array();
+ }
+ }
+}
+PHPUnit_MockObject
+
+Copyright (c) 2002-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Builder interface for unique identifiers.
+ *
+ * Defines the interface for recording unique identifiers. The identifiers
+ * can be used to define the invocation order of expectations. The expectation
+ * is recorded using id() and then defined in order using
+ * PHPUnit_Framework_MockObject_Builder_Match::after().
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Builder_Identity
+{
+ /**
+ * Sets the identification of the expectation to $id.
+ *
+ * @note The identifier is unique per mock object.
+ *
+ * @param string $id Unique identification of expectation.
+ */
+ public function id($id);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Builder for mocked or stubbed invocations.
+ *
+ * Provides methods for building expectations without having to resort to
+ * instantiating the various matchers manually. These methods also form a
+ * more natural way of reading the expectation. This class should be together
+ * with the test case PHPUnit_Framework_MockObject_TestCase.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Builder_InvocationMocker implements PHPUnit_Framework_MockObject_Builder_MethodNameMatch
+{
+ /**
+ * @var PHPUnit_Framework_MockObject_Stub_MatcherCollection
+ */
+ protected $collection;
+
+ /**
+ * @var PHPUnit_Framework_MockObject_Matcher
+ */
+ protected $matcher;
+
+ /**
+ * @var string[]
+ */
+ private $configurableMethods = [];
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Stub_MatcherCollection $collection
+ * @param PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher
+ * @param array $configurableMethods
+ */
+ public function __construct(PHPUnit_Framework_MockObject_Stub_MatcherCollection $collection, PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher, array $configurableMethods)
+ {
+ $this->collection = $collection;
+ $this->matcher = new PHPUnit_Framework_MockObject_Matcher(
+ $invocationMatcher
+ );
+
+ $this->collection->addMatcher($this->matcher);
+
+ $this->configurableMethods = $configurableMethods;
+ }
+
+ /**
+ * @return PHPUnit_Framework_MockObject_Matcher
+ */
+ public function getMatcher()
+ {
+ return $this->matcher;
+ }
+
+ /**
+ * @param mixed $id
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function id($id)
+ {
+ $this->collection->registerId($id, $this);
+
+ return $this;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Stub $stub
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function will(PHPUnit_Framework_MockObject_Stub $stub)
+ {
+ $this->matcher->stub = $stub;
+
+ return $this;
+ }
+
+ /**
+ * @param mixed $value
+ * @param mixed $nextValues, ...
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willReturn($value, ...$nextValues)
+ {
+ $stub = count($nextValues) === 0 ?
+ new PHPUnit_Framework_MockObject_Stub_Return($value) :
+ new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls(
+ array_merge([$value], $nextValues)
+ );
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @param mixed $reference
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willReturnReference(&$reference)
+ {
+ $stub = new PHPUnit_Framework_MockObject_Stub_ReturnReference($reference);
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @param array $valueMap
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willReturnMap(array $valueMap)
+ {
+ $stub = new PHPUnit_Framework_MockObject_Stub_ReturnValueMap(
+ $valueMap
+ );
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @param mixed $argumentIndex
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willReturnArgument($argumentIndex)
+ {
+ $stub = new PHPUnit_Framework_MockObject_Stub_ReturnArgument(
+ $argumentIndex
+ );
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @param callable $callback
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willReturnCallback($callback)
+ {
+ $stub = new PHPUnit_Framework_MockObject_Stub_ReturnCallback(
+ $callback
+ );
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willReturnSelf()
+ {
+ $stub = new PHPUnit_Framework_MockObject_Stub_ReturnSelf;
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @param mixed $values, ...
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willReturnOnConsecutiveCalls(...$values)
+ {
+ $stub = new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($values);
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @param Exception $exception
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function willThrowException(Exception $exception)
+ {
+ $stub = new PHPUnit_Framework_MockObject_Stub_Exception($exception);
+
+ return $this->will($stub);
+ }
+
+ /**
+ * @param mixed $id
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function after($id)
+ {
+ $this->matcher->afterMatchBuilderId = $id;
+
+ return $this;
+ }
+
+ /**
+ * Validate that a parameters matcher can be defined, throw exceptions otherwise.
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ */
+ private function canDefineParameters()
+ {
+ if ($this->matcher->methodNameMatcher === null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'Method name matcher is not defined, cannot define parameter ' .
+ 'matcher without one'
+ );
+ }
+
+ if ($this->matcher->parametersMatcher !== null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'Parameter matcher is already defined, cannot redefine'
+ );
+ }
+ }
+
+ /**
+ * @param array ...$arguments
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function with(...$arguments)
+ {
+ $this->canDefineParameters();
+
+ $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_Parameters($arguments);
+
+ return $this;
+ }
+
+ /**
+ * @param array ...$arguments
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function withConsecutive(...$arguments)
+ {
+ $this->canDefineParameters();
+
+ $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters($arguments);
+
+ return $this;
+ }
+
+ /**
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function withAnyParameters()
+ {
+ $this->canDefineParameters();
+
+ $this->matcher->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_AnyParameters;
+
+ return $this;
+ }
+
+ /**
+ * @param PHPUnit_Framework_Constraint|string $constraint
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function method($constraint)
+ {
+ if ($this->matcher->methodNameMatcher !== null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'Method name matcher is already defined, cannot redefine'
+ );
+ }
+
+ if (is_string($constraint) && !in_array(strtolower($constraint), $this->configurableMethods)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static',
+ $constraint
+ )
+ );
+ }
+
+ $this->matcher->methodNameMatcher = new PHPUnit_Framework_MockObject_Matcher_MethodName($constraint);
+
+ return $this;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Builder interface for invocation order matches.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Builder_Match extends PHPUnit_Framework_MockObject_Builder_Stub
+{
+ /**
+ * Defines the expectation which must occur before the current is valid.
+ *
+ * @param string $id The identification of the expectation that should
+ * occur before this one.
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_Stub
+ */
+ public function after($id);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Builder interface for matcher of method names.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Builder_MethodNameMatch extends PHPUnit_Framework_MockObject_Builder_ParametersMatch
+{
+ /**
+ * Adds a new method name match and returns the parameter match object for
+ * further matching possibilities.
+ *
+ * @param PHPUnit_Framework_Constraint $name Constraint for matching method, if a string is passed it will use the PHPUnit_Framework_Constraint_IsEqual
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_ParametersMatch
+ */
+ public function method($name);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for builders which can register builders with a given identification.
+ *
+ * This interface relates to PHPUnit_Framework_MockObject_Builder_Identity.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Builder_Namespace
+{
+ /**
+ * Looks up the match builder with identification $id and returns it.
+ *
+ * @param string $id The identification of the match builder
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_Match
+ */
+ public function lookupId($id);
+
+ /**
+ * Registers the match builder $builder with the identification $id. The
+ * builder can later be looked up using lookupId() to figure out if it
+ * has been invoked.
+ *
+ * @param string $id The identification of the match builder
+ * @param PHPUnit_Framework_MockObject_Builder_Match $builder The builder which is being registered
+ */
+ public function registerId($id, PHPUnit_Framework_MockObject_Builder_Match $builder);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Builder interface for parameter matchers.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Builder_ParametersMatch extends PHPUnit_Framework_MockObject_Builder_Match
+{
+ /**
+ * Sets the parameters to match for, each parameter to this funtion will
+ * be part of match. To perform specific matches or constraints create a
+ * new PHPUnit_Framework_Constraint and use it for the parameter.
+ * If the parameter value is not a constraint it will use the
+ * PHPUnit_Framework_Constraint_IsEqual for the value.
+ *
+ * Some examples:
+ * <code>
+ * // match first parameter with value 2
+ * $b->with(2);
+ * // match first parameter with value 'smock' and second identical to 42
+ * $b->with('smock', new PHPUnit_Framework_Constraint_IsEqual(42));
+ * </code>
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_ParametersMatch
+ */
+ public function with(...$arguments);
+
+ /**
+ * Sets a matcher which allows any kind of parameters.
+ *
+ * Some examples:
+ * <code>
+ * // match any number of parameters
+ * $b->withAnyParameters();
+ * </code>
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_AnyParameters
+ */
+ public function withAnyParameters();
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Builder interface for stubs which are actions replacing an invocation.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Builder_Stub extends PHPUnit_Framework_MockObject_Builder_Identity
+{
+ /**
+ * Stubs the matching method with the stub object $stub. Any invocations of
+ * the matched method will now be handled by the stub instead.
+ *
+ * @param PHPUnit_Framework_MockObject_Stub $stub
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_Identity
+ */
+ public function will(PHPUnit_Framework_MockObject_Stub $stub);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * @since Class available since Release 2.0.6
+ */
+class PHPUnit_Framework_MockObject_BadMethodCallException extends BadMethodCallException implements PHPUnit_Framework_MockObject_Exception
+{
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for exceptions used by PHPUnit_MockObject.
+ *
+ * @since Interface available since Release 2.0.6
+ */
+interface PHPUnit_Framework_MockObject_Exception
+{
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * @since Class available since Release 2.0.6
+ */
+class PHPUnit_Framework_MockObject_RuntimeException extends RuntimeException implements PHPUnit_Framework_MockObject_Exception
+{
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Doctrine\Instantiator\Instantiator;
+use Doctrine\Instantiator\Exception\InvalidArgumentException as InstantiatorInvalidArgumentException;
+use Doctrine\Instantiator\Exception\UnexpectedValueException as InstantiatorUnexpectedValueException;
+
+/**
+ * Mock Object Code Generator
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Generator
+{
+ /**
+ * @var array
+ */
+ private static $cache = [];
+
+ /**
+ * @var Text_Template[]
+ */
+ private static $templates = [];
+
+ /**
+ * @var array
+ */
+ private $legacyBlacklistedMethodNames = [
+ '__CLASS__' => true,
+ '__DIR__' => true,
+ '__FILE__' => true,
+ '__FUNCTION__' => true,
+ '__LINE__' => true,
+ '__METHOD__' => true,
+ '__NAMESPACE__' => true,
+ '__TRAIT__' => true,
+ '__clone' => true,
+ '__halt_compiler' => true,
+ 'abstract' => true,
+ 'and' => true,
+ 'array' => true,
+ 'as' => true,
+ 'break' => true,
+ 'callable' => true,
+ 'case' => true,
+ 'catch' => true,
+ 'class' => true,
+ 'clone' => true,
+ 'const' => true,
+ 'continue' => true,
+ 'declare' => true,
+ 'default' => true,
+ 'die' => true,
+ 'do' => true,
+ 'echo' => true,
+ 'else' => true,
+ 'elseif' => true,
+ 'empty' => true,
+ 'enddeclare' => true,
+ 'endfor' => true,
+ 'endforeach' => true,
+ 'endif' => true,
+ 'endswitch' => true,
+ 'endwhile' => true,
+ 'eval' => true,
+ 'exit' => true,
+ 'expects' => true,
+ 'extends' => true,
+ 'final' => true,
+ 'for' => true,
+ 'foreach' => true,
+ 'function' => true,
+ 'global' => true,
+ 'goto' => true,
+ 'if' => true,
+ 'implements' => true,
+ 'include' => true,
+ 'include_once' => true,
+ 'instanceof' => true,
+ 'insteadof' => true,
+ 'interface' => true,
+ 'isset' => true,
+ 'list' => true,
+ 'namespace' => true,
+ 'new' => true,
+ 'or' => true,
+ 'print' => true,
+ 'private' => true,
+ 'protected' => true,
+ 'public' => true,
+ 'require' => true,
+ 'require_once' => true,
+ 'return' => true,
+ 'static' => true,
+ 'switch' => true,
+ 'throw' => true,
+ 'trait' => true,
+ 'try' => true,
+ 'unset' => true,
+ 'use' => true,
+ 'var' => true,
+ 'while' => true,
+ 'xor' => true
+ ];
+
+ /**
+ * @var array
+ */
+ private $blacklistedMethodNames = [
+ '__CLASS__' => true,
+ '__DIR__' => true,
+ '__FILE__' => true,
+ '__FUNCTION__' => true,
+ '__LINE__' => true,
+ '__METHOD__' => true,
+ '__NAMESPACE__' => true,
+ '__TRAIT__' => true,
+ '__clone' => true,
+ '__halt_compiler' => true,
+ ];
+
+ /**
+ * Returns a mock object for the specified class.
+ *
+ * @param array|string $type
+ * @param array $methods
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ * @param object $proxyTarget
+ * @param bool $allowMockingUnknownTypes
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws InvalidArgumentException
+ * @throws PHPUnit_Framework_Exception
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ *
+ * @since Method available since Release 1.0.0
+ */
+ public function getMock($type, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false, $proxyTarget = null, $allowMockingUnknownTypes = true)
+ {
+ if (!is_array($type) && !is_string($type)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'array or string');
+ }
+
+ if (!is_string($mockClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'string');
+ }
+
+ if (!is_array($methods) && !is_null($methods)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'array', $methods);
+ }
+
+ if ($type === 'Traversable' || $type === '\\Traversable') {
+ $type = 'Iterator';
+ }
+
+ if (is_array($type)) {
+ $type = array_unique(
+ array_map(
+ function ($type) {
+ if ($type === 'Traversable' ||
+ $type === '\\Traversable' ||
+ $type === '\\Iterator') {
+ return 'Iterator';
+ }
+
+ return $type;
+ },
+ $type
+ )
+ );
+ }
+
+ if (!$allowMockingUnknownTypes) {
+ if (is_array($type)) {
+ foreach ($type as $_type) {
+ if (!class_exists($_type, $callAutoload) &&
+ !interface_exists($_type, $callAutoload)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Cannot stub or mock class or interface "%s" which does not exist',
+ $_type
+ )
+ );
+ }
+ }
+ } else {
+ if (!class_exists($type, $callAutoload) &&
+ !interface_exists($type, $callAutoload)
+ ) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Cannot stub or mock class or interface "%s" which does not exist',
+ $type
+ )
+ );
+ }
+ }
+ }
+
+ if (null !== $methods) {
+ foreach ($methods as $method) {
+ if (!preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Cannot stub or mock method with invalid name "%s"',
+ $method
+ )
+ );
+ }
+ }
+
+ if ($methods != array_unique($methods)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")',
+ implode(', ', $methods),
+ implode(', ', array_unique(array_diff_assoc($methods, array_unique($methods))))
+ )
+ );
+ }
+ }
+
+ if ($mockClassName != '' && class_exists($mockClassName, false)) {
+ $reflect = new ReflectionClass($mockClassName);
+
+ if (!$reflect->implementsInterface('PHPUnit_Framework_MockObject_MockObject')) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Class "%s" already exists.',
+ $mockClassName
+ )
+ );
+ }
+ }
+
+ if ($callOriginalConstructor === false && $callOriginalMethods === true) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'Proxying to original methods requires invoking the original constructor'
+ );
+ }
+
+ $mock = $this->generate(
+ $type,
+ $methods,
+ $mockClassName,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments,
+ $callOriginalMethods
+ );
+
+ return $this->getObject(
+ $mock['code'],
+ $mock['mockClassName'],
+ $type,
+ $callOriginalConstructor,
+ $callAutoload,
+ $arguments,
+ $callOriginalMethods,
+ $proxyTarget
+ );
+ }
+
+ /**
+ * @param string $code
+ * @param string $className
+ * @param array|string $type
+ * @param bool $callOriginalConstructor
+ * @param bool $callAutoload
+ * @param array $arguments
+ * @param bool $callOriginalMethods
+ * @param object $proxyTarget
+ *
+ * @return object
+ */
+ private function getObject($code, $className, $type = '', $callOriginalConstructor = false, $callAutoload = false, array $arguments = [], $callOriginalMethods = false, $proxyTarget = null)
+ {
+ $this->evalClass($code, $className);
+
+ if ($callOriginalConstructor &&
+ is_string($type) &&
+ !interface_exists($type, $callAutoload)) {
+ if (count($arguments) == 0) {
+ $object = new $className;
+ } else {
+ $class = new ReflectionClass($className);
+ $object = $class->newInstanceArgs($arguments);
+ }
+ } else {
+ try {
+ $instantiator = new Instantiator;
+ $object = $instantiator->instantiate($className);
+ } catch (InstantiatorUnexpectedValueException $exception) {
+ if ($exception->getPrevious()) {
+ $exception = $exception->getPrevious();
+ }
+
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ $exception->getMessage()
+ );
+ } catch (InstantiatorInvalidArgumentException $exception) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ $exception->getMessage()
+ );
+ }
+ }
+
+ if ($callOriginalMethods) {
+ if (!is_object($proxyTarget)) {
+ if (count($arguments) == 0) {
+ $proxyTarget = new $type;
+ } else {
+ $class = new ReflectionClass($type);
+ $proxyTarget = $class->newInstanceArgs($arguments);
+ }
+ }
+
+ $object->__phpunit_setOriginalObject($proxyTarget);
+ }
+
+ return $object;
+ }
+
+ /**
+ * @param string $code
+ * @param string $className
+ */
+ private function evalClass($code, $className)
+ {
+ if (!class_exists($className, false)) {
+ eval($code);
+ }
+ }
+
+ /**
+ * Returns a mock object for the specified abstract class with all abstract
+ * methods of the class mocked. Concrete methods to mock can be specified with
+ * the last parameter
+ *
+ * @param string $originalClassName
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param array $mockedMethods
+ * @param bool $cloneArguments
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @since Method available since Release 1.0.0
+ */
+ public function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true)
+ {
+ if (!is_string($originalClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($mockClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
+ }
+
+ if (class_exists($originalClassName, $callAutoload) ||
+ interface_exists($originalClassName, $callAutoload)) {
+ $reflector = new ReflectionClass($originalClassName);
+ $methods = $mockedMethods;
+
+ foreach ($reflector->getMethods() as $method) {
+ if ($method->isAbstract() && !in_array($method->getName(), $methods)) {
+ $methods[] = $method->getName();
+ }
+ }
+
+ if (empty($methods)) {
+ $methods = null;
+ }
+
+ return $this->getMock(
+ $originalClassName,
+ $methods,
+ $arguments,
+ $mockClassName,
+ $callOriginalConstructor,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments
+ );
+ } else {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf('Class "%s" does not exist.', $originalClassName)
+ );
+ }
+ }
+
+ /**
+ * Returns a mock object for the specified trait with all abstract methods
+ * of the trait mocked. Concrete methods to mock can be specified with the
+ * `$mockedMethods` parameter.
+ *
+ * @param string $traitName
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param array $mockedMethods
+ * @param bool $cloneArguments
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @since Method available since Release 1.2.3
+ */
+ public function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true)
+ {
+ if (!is_string($traitName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($mockClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
+ }
+
+ if (!trait_exists($traitName, $callAutoload)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Trait "%s" does not exist.',
+ $traitName
+ )
+ );
+ }
+
+ $className = $this->generateClassName(
+ $traitName,
+ '',
+ 'Trait_'
+ );
+
+ $templateDir = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
+ $classTemplate = $this->getTemplate($templateDir . 'trait_class.tpl');
+
+ $classTemplate->setVar(
+ [
+ 'prologue' => 'abstract ',
+ 'class_name' => $className['className'],
+ 'trait_name' => $traitName
+ ]
+ );
+
+ $this->evalClass(
+ $classTemplate->render(),
+ $className['className']
+ );
+
+ return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments);
+ }
+
+ /**
+ * Returns an object for the specified trait.
+ *
+ * @param string $traitName
+ * @param array $arguments
+ * @param string $traitClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ *
+ * @return object
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @since Method available since Release 1.1.0
+ */
+ public function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)
+ {
+ if (!is_string($traitName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($traitClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
+ }
+
+ if (!trait_exists($traitName, $callAutoload)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Trait "%s" does not exist.',
+ $traitName
+ )
+ );
+ }
+
+ $className = $this->generateClassName(
+ $traitName,
+ $traitClassName,
+ 'Trait_'
+ );
+
+ $templateDir = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
+ $classTemplate = $this->getTemplate($templateDir . 'trait_class.tpl');
+
+ $classTemplate->setVar(
+ [
+ 'prologue' => '',
+ 'class_name' => $className['className'],
+ 'trait_name' => $traitName
+ ]
+ );
+
+ return $this->getObject(
+ $classTemplate->render(),
+ $className['className']
+ );
+ }
+
+ /**
+ * @param array|string $type
+ * @param array $methods
+ * @param string $mockClassName
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ *
+ * @return array
+ */
+ public function generate($type, array $methods = null, $mockClassName = '', $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false)
+ {
+ if (is_array($type)) {
+ sort($type);
+ }
+
+ if ($mockClassName == '') {
+ $key = md5(
+ is_array($type) ? implode('_', $type) : $type .
+ serialize($methods) .
+ serialize($callOriginalClone) .
+ serialize($cloneArguments) .
+ serialize($callOriginalMethods)
+ );
+
+ if (isset(self::$cache[$key])) {
+ return self::$cache[$key];
+ }
+ }
+
+ $mock = $this->generateMock(
+ $type,
+ $methods,
+ $mockClassName,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments,
+ $callOriginalMethods
+ );
+
+ if (isset($key)) {
+ self::$cache[$key] = $mock;
+ }
+
+ return $mock;
+ }
+
+ /**
+ * @param string $wsdlFile
+ * @param string $className
+ * @param array $methods
+ * @param array $options
+ *
+ * @return string
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ */
+ public function generateClassFromWsdl($wsdlFile, $className, array $methods = [], array $options = [])
+ {
+ if (!extension_loaded('soap')) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'The SOAP extension is required to generate a mock object from WSDL.'
+ );
+ }
+
+ $options = array_merge($options, ['cache_wsdl' => WSDL_CACHE_NONE]);
+ $client = new SoapClient($wsdlFile, $options);
+ $_methods = array_unique($client->__getFunctions());
+ unset($client);
+
+ sort($_methods);
+
+ $templateDir = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
+ $methodTemplate = $this->getTemplate($templateDir . 'wsdl_method.tpl');
+ $methodsBuffer = '';
+
+ foreach ($_methods as $method) {
+ $nameStart = strpos($method, ' ') + 1;
+ $nameEnd = strpos($method, '(');
+ $name = substr($method, $nameStart, $nameEnd - $nameStart);
+
+ if (empty($methods) || in_array($name, $methods)) {
+ $args = explode(
+ ',',
+ substr(
+ $method,
+ $nameEnd + 1,
+ strpos($method, ')') - $nameEnd - 1
+ )
+ );
+ $numArgs = count($args);
+
+ for ($i = 0; $i < $numArgs; $i++) {
+ $args[$i] = substr($args[$i], strpos($args[$i], '$'));
+ }
+
+ $methodTemplate->setVar(
+ [
+ 'method_name' => $name,
+ 'arguments' => implode(', ', $args)
+ ]
+ );
+
+ $methodsBuffer .= $methodTemplate->render();
+ }
+ }
+
+ $optionsBuffer = 'array(';
+
+ foreach ($options as $key => $value) {
+ $optionsBuffer .= $key . ' => ' . $value;
+ }
+
+ $optionsBuffer .= ')';
+
+ $classTemplate = $this->getTemplate($templateDir . 'wsdl_class.tpl');
+ $namespace = '';
+
+ if (strpos($className, '\\') !== false) {
+ $parts = explode('\\', $className);
+ $className = array_pop($parts);
+ $namespace = 'namespace ' . implode('\\', $parts) . ';' . "\n\n";
+ }
+
+ $classTemplate->setVar(
+ [
+ 'namespace' => $namespace,
+ 'class_name' => $className,
+ 'wsdl' => $wsdlFile,
+ 'options' => $optionsBuffer,
+ 'methods' => $methodsBuffer
+ ]
+ );
+
+ return $classTemplate->render();
+ }
+
+ /**
+ * @param array|string $type
+ * @param array|null $methods
+ * @param string $mockClassName
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ *
+ * @return array
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ */
+ private function generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods)
+ {
+ $methodReflections = [];
+ $templateDir = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
+ $classTemplate = $this->getTemplate($templateDir . 'mocked_class.tpl');
+
+ $additionalInterfaces = [];
+ $cloneTemplate = '';
+ $isClass = false;
+ $isInterface = false;
+ $isMultipleInterfaces = false;
+
+ if (is_array($type)) {
+ foreach ($type as $_type) {
+ if (!interface_exists($_type, $callAutoload)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Interface "%s" does not exist.',
+ $_type
+ )
+ );
+ }
+
+ $isMultipleInterfaces = true;
+
+ $additionalInterfaces[] = $_type;
+ $typeClass = new ReflectionClass($this->generateClassName(
+ $_type,
+ $mockClassName,
+ 'Mock_'
+ )['fullClassName']
+ );
+
+ foreach ($this->getClassMethods($_type) as $method) {
+ if (in_array($method, $methods)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Duplicate method "%s" not allowed.',
+ $method
+ )
+ );
+ }
+
+ $methodReflections[$method] = $typeClass->getMethod($method);
+ $methods[] = $method;
+ }
+ }
+ }
+
+ $mockClassName = $this->generateClassName(
+ $type,
+ $mockClassName,
+ 'Mock_'
+ );
+
+ if (class_exists($mockClassName['fullClassName'], $callAutoload)) {
+ $isClass = true;
+ } elseif (interface_exists($mockClassName['fullClassName'], $callAutoload)) {
+ $isInterface = true;
+ }
+
+ if (!$isClass && !$isInterface) {
+ $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";
+
+ if (!empty($mockClassName['namespaceName'])) {
+ $prologue = 'namespace ' . $mockClassName['namespaceName'] .
+ " {\n\n" . $prologue . "}\n\n" .
+ "namespace {\n\n";
+
+ $epilogue = "\n\n}";
+ }
+
+ $cloneTemplate = $this->getTemplate($templateDir . 'mocked_clone.tpl');
+ } else {
+ $class = new ReflectionClass($mockClassName['fullClassName']);
+
+ if ($class->isFinal()) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Class "%s" is declared "final" and cannot be mocked.',
+ $mockClassName['fullClassName']
+ )
+ );
+ }
+
+ if ($class->hasMethod('__clone')) {
+ $cloneMethod = $class->getMethod('__clone');
+
+ if (!$cloneMethod->isFinal()) {
+ if ($callOriginalClone && !$isInterface) {
+ $cloneTemplate = $this->getTemplate($templateDir . 'unmocked_clone.tpl');
+ } else {
+ $cloneTemplate = $this->getTemplate($templateDir . 'mocked_clone.tpl');
+ }
+ }
+ } else {
+ $cloneTemplate = $this->getTemplate($templateDir . 'mocked_clone.tpl');
+ }
+ }
+
+ if (is_object($cloneTemplate)) {
+ $cloneTemplate = $cloneTemplate->render();
+ }
+
+ if (is_array($methods) && empty($methods) &&
+ ($isClass || $isInterface)) {
+ $methods = $this->getClassMethods($mockClassName['fullClassName']);
+ }
+
+ if (!is_array($methods)) {
+ $methods = [];
+ }
+
+ $mockedMethods = '';
+ $configurable = [];
+
+ foreach ($methods as $methodName) {
+ if ($methodName != '__construct' && $methodName != '__clone') {
+ $configurable[] = strtolower($methodName);
+ }
+ }
+
+ if (isset($class)) {
+ // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103
+ if ($isInterface && $class->implementsInterface('Traversable') &&
+ !$class->implementsInterface('Iterator') &&
+ !$class->implementsInterface('IteratorAggregate')) {
+ $additionalInterfaces[] = 'Iterator';
+ $methods = array_merge($methods, $this->getClassMethods('Iterator'));
+ }
+
+ foreach ($methods as $methodName) {
+ try {
+ $method = $class->getMethod($methodName);
+
+ if ($this->canMockMethod($method)) {
+ $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting(
+ $templateDir,
+ $method,
+ $cloneArguments,
+ $callOriginalMethods
+ );
+ }
+ } catch (ReflectionException $e) {
+ $mockedMethods .= $this->generateMockedMethodDefinition(
+ $templateDir,
+ $mockClassName['fullClassName'],
+ $methodName,
+ $cloneArguments
+ );
+ }
+ }
+ } elseif ($isMultipleInterfaces) {
+ foreach ($methods as $methodName) {
+ if ($this->canMockMethod($methodReflections[$methodName])) {
+ $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting(
+ $templateDir,
+ $methodReflections[$methodName],
+ $cloneArguments,
+ $callOriginalMethods
+ );
+ }
+ }
+ } else {
+ foreach ($methods as $methodName) {
+ $mockedMethods .= $this->generateMockedMethodDefinition(
+ $templateDir,
+ $mockClassName['fullClassName'],
+ $methodName,
+ $cloneArguments
+ );
+ }
+ }
+
+ $method = '';
+
+ if (!in_array('method', $methods) && (!isset($class) || !$class->hasMethod('method'))) {
+ $methodTemplate = $this->getTemplate($templateDir . 'mocked_class_method.tpl');
+
+ $method = $methodTemplate->render();
+ }
+
+ $classTemplate->setVar(
+ [
+ 'prologue' => isset($prologue) ? $prologue : '',
+ 'epilogue' => isset($epilogue) ? $epilogue : '',
+ 'class_declaration' => $this->generateMockClassDeclaration(
+ $mockClassName,
+ $isInterface,
+ $additionalInterfaces
+ ),
+ 'clone' => $cloneTemplate,
+ 'mock_class_name' => $mockClassName['className'],
+ 'mocked_methods' => $mockedMethods,
+ 'method' => $method,
+ 'configurable' => '[' . implode(', ', array_map(function ($m) { return '\'' . $m . '\'';}, $configurable)) . ']'
+ ]
+ );
+
+ return [
+ 'code' => $classTemplate->render(),
+ 'mockClassName' => $mockClassName['className']
+ ];
+ }
+
+ /**
+ * @param array|string $type
+ * @param string $className
+ * @param string $prefix
+ *
+ * @return array
+ */
+ private function generateClassName($type, $className, $prefix)
+ {
+ if (is_array($type)) {
+ $type = implode('_', $type);
+ }
+
+ if ($type[0] == '\\') {
+ $type = substr($type, 1);
+ }
+
+ $classNameParts = explode('\\', $type);
+
+ if (count($classNameParts) > 1) {
+ $type = array_pop($classNameParts);
+ $namespaceName = implode('\\', $classNameParts);
+ $fullClassName = $namespaceName . '\\' . $type;
+ } else {
+ $namespaceName = '';
+ $fullClassName = $type;
+ }
+
+ if ($className == '') {
+ do {
+ $className = $prefix . $type . '_' .
+ substr(md5(microtime()), 0, 8);
+ } while (class_exists($className, false));
+ }
+
+ return [
+ 'className' => $className,
+ 'originalClassName' => $type,
+ 'fullClassName' => $fullClassName,
+ 'namespaceName' => $namespaceName
+ ];
+ }
+
+ /**
+ * @param array $mockClassName
+ * @param bool $isInterface
+ * @param array $additionalInterfaces
+ *
+ * @return array
+ */
+ private function generateMockClassDeclaration(array $mockClassName, $isInterface, array $additionalInterfaces = [])
+ {
+ $buffer = 'class ';
+
+ $additionalInterfaces[] = 'PHPUnit_Framework_MockObject_MockObject';
+ $interfaces = implode(', ', $additionalInterfaces);
+
+ if ($isInterface) {
+ $buffer .= sprintf(
+ '%s implements %s',
+ $mockClassName['className'],
+ $interfaces
+ );
+
+ if (!in_array($mockClassName['originalClassName'], $additionalInterfaces)) {
+ $buffer .= ', ';
+
+ if (!empty($mockClassName['namespaceName'])) {
+ $buffer .= $mockClassName['namespaceName'] . '\\';
+ }
+
+ $buffer .= $mockClassName['originalClassName'];
+ }
+ } else {
+ $buffer .= sprintf(
+ '%s extends %s%s implements %s',
+ $mockClassName['className'],
+ !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
+ $mockClassName['originalClassName'],
+ $interfaces
+ );
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @param string $templateDir
+ * @param ReflectionMethod $method
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ *
+ * @return string
+ */
+ private function generateMockedMethodDefinitionFromExisting($templateDir, ReflectionMethod $method, $cloneArguments, $callOriginalMethods)
+ {
+ if ($method->isPrivate()) {
+ $modifier = 'private';
+ } elseif ($method->isProtected()) {
+ $modifier = 'protected';
+ } else {
+ $modifier = 'public';
+ }
+
+ if ($method->isStatic()) {
+ $modifier .= ' static';
+ }
+
+ if ($method->returnsReference()) {
+ $reference = '&';
+ } else {
+ $reference = '';
+ }
+
+ if ($this->hasReturnType($method)) {
+ $returnType = (string) $method->getReturnType();
+ } else {
+ $returnType = '';
+ }
+
+ if (preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $method->getDocComment(), $deprecation)) {
+ $deprecation = trim(preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
+ } else {
+ $deprecation = false;
+ }
+
+ return $this->generateMockedMethodDefinition(
+ $templateDir,
+ $method->getDeclaringClass()->getName(),
+ $method->getName(),
+ $cloneArguments,
+ $modifier,
+ $this->getMethodParameters($method),
+ $this->getMethodParameters($method, true),
+ $returnType,
+ $reference,
+ $callOriginalMethods,
+ $method->isStatic(),
+ $deprecation,
+ $this->allowsReturnNull($method)
+ );
+ }
+
+ /**
+ * @param string $templateDir
+ * @param string $className
+ * @param string $methodName
+ * @param bool $cloneArguments
+ * @param string $modifier
+ * @param string $arguments_decl
+ * @param string $arguments_call
+ * @param string $return_type
+ * @param string $reference
+ * @param bool $callOriginalMethods
+ * @param bool $static
+ * @param string|false $deprecation
+ * @param bool $allowsReturnNull
+ *
+ * @return string
+ */
+ private function generateMockedMethodDefinition($templateDir, $className, $methodName, $cloneArguments = true, $modifier = 'public', $arguments_decl = '', $arguments_call = '', $return_type = '', $reference = '', $callOriginalMethods = false, $static = false, $deprecation = false, $allowsReturnNull = false)
+ {
+ if ($static) {
+ $templateFile = 'mocked_static_method.tpl';
+ } else {
+ if ($return_type === 'void') {
+ $templateFile = sprintf(
+ '%s_method_void.tpl',
+ $callOriginalMethods ? 'proxied' : 'mocked'
+ );
+ } else {
+ $templateFile = sprintf(
+ '%s_method.tpl',
+ $callOriginalMethods ? 'proxied' : 'mocked'
+ );
+ }
+ }
+
+ // Mocked interfaces returning 'self' must explicitly declare the
+ // interface name as the return type. See
+ // https://bugs.php.net/bug.php?id=70722
+ if ($return_type === 'self') {
+ $return_type = $className;
+ }
+
+ if (false !== $deprecation) {
+ $deprecation = "The $className::$methodName method is deprecated ($deprecation).";
+ $deprecationTemplate = $this->getTemplate($templateDir . 'deprecation.tpl');
+
+ $deprecationTemplate->setVar(
+ [
+ 'deprecation' => var_export($deprecation, true),
+ ]
+ );
+
+ $deprecation = $deprecationTemplate->render();
+ }
+
+ $template = $this->getTemplate($templateDir . $templateFile);
+
+ $template->setVar(
+ [
+ 'arguments_decl' => $arguments_decl,
+ 'arguments_call' => $arguments_call,
+ 'return_delim' => $return_type ? ': ' : '',
+ 'return_type' => $allowsReturnNull ? '?' . $return_type : $return_type,
+ 'arguments_count' => !empty($arguments_call) ? count(explode(',', $arguments_call)) : 0,
+ 'class_name' => $className,
+ 'method_name' => $methodName,
+ 'modifier' => $modifier,
+ 'reference' => $reference,
+ 'clone_arguments' => $cloneArguments ? 'true' : 'false',
+ 'deprecation' => $deprecation
+ ]
+ );
+
+ return $template->render();
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ private function canMockMethod(ReflectionMethod $method)
+ {
+ if ($method->isConstructor() ||
+ $method->isFinal() ||
+ $method->isPrivate() ||
+ $this->isMethodNameBlacklisted($method->getName())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns whether i method name is blacklisted
+ *
+ * Since PHP 7 the only names that are still reserved for method names are the ones that start with an underscore
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ private function isMethodNameBlacklisted($name)
+ {
+ if (PHP_MAJOR_VERSION < 7 && isset($this->legacyBlacklistedMethodNames[$name])) {
+ return true;
+ }
+
+ if (PHP_MAJOR_VERSION >= 7 && isset($this->blacklistedMethodNames[$name])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the parameters of a function or method.
+ *
+ * @param ReflectionMethod $method
+ * @param bool $forCall
+ *
+ * @return string
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ *
+ * @since Method available since Release 2.0.0
+ */
+ private function getMethodParameters(ReflectionMethod $method, $forCall = false)
+ {
+ $parameters = [];
+
+ foreach ($method->getParameters() as $i => $parameter) {
+ $name = '$' . $parameter->getName();
+
+ /* Note: PHP extensions may use empty names for reference arguments
+ * or "..." for methods taking a variable number of arguments.
+ */
+ if ($name === '$' || $name === '$...') {
+ $name = '$arg' . $i;
+ }
+
+ if ($this->isVariadic($parameter)) {
+ if ($forCall) {
+ continue;
+ } else {
+ $name = '...' . $name;
+ }
+ }
+
+ $nullable = '';
+ $default = '';
+ $reference = '';
+ $typeDeclaration = '';
+
+ if (!$forCall) {
+ if ($this->hasType($parameter) && (string) $parameter->getType() !== 'self') {
+ if (version_compare(PHP_VERSION, '7.1', '>=') && $parameter->allowsNull() && !$parameter->isVariadic()) {
+ $nullable = '?';
+ }
+
+ $typeDeclaration = (string) $parameter->getType() . ' ';
+ } elseif ($parameter->isArray()) {
+ $typeDeclaration = 'array ';
+ } elseif ($parameter->isCallable()) {
+ $typeDeclaration = 'callable ';
+ } else {
+ try {
+ $class = $parameter->getClass();
+ } catch (ReflectionException $e) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Cannot mock %s::%s() because a class or ' .
+ 'interface used in the signature is not loaded',
+ $method->getDeclaringClass()->getName(),
+ $method->getName()
+ ),
+ 0,
+ $e
+ );
+ }
+
+ if ($class !== null) {
+ $typeDeclaration = $class->getName() . ' ';
+ }
+ }
+
+ if (!$this->isVariadic($parameter)) {
+ if ($parameter->isDefaultValueAvailable()) {
+ $value = $parameter->getDefaultValue();
+ $default = ' = ' . var_export($value, true);
+ } elseif ($parameter->isOptional()) {
+ $default = ' = null';
+ }
+ }
+ }
+
+ if ($parameter->isPassedByReference()) {
+ $reference = '&';
+ }
+
+ $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default;
+ }
+
+ return implode(', ', $parameters);
+ }
+
+ /**
+ * @param ReflectionParameter $parameter
+ *
+ * @return bool
+ *
+ * @since Method available since Release 2.2.1
+ */
+ private function isVariadic(ReflectionParameter $parameter)
+ {
+ return method_exists(ReflectionParameter::class, 'isVariadic') && $parameter->isVariadic();
+ }
+
+ /**
+ * @param ReflectionParameter $parameter
+ *
+ * @return bool
+ *
+ * @since Method available since Release 2.3.4
+ */
+ private function hasType(ReflectionParameter $parameter)
+ {
+ return method_exists(ReflectionParameter::class, 'hasType') && $parameter->hasType();
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ private function hasReturnType(ReflectionMethod $method)
+ {
+ return method_exists(ReflectionMethod::class, 'hasReturnType') && $method->hasReturnType();
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ private function allowsReturnNull(ReflectionMethod $method)
+ {
+ return method_exists(ReflectionMethod::class, 'getReturnType')
+ && method_exists(ReflectionType::class, 'allowsNull')
+ && $method->hasReturnType()
+ && $method->getReturnType()->allowsNull();
+ }
+
+ /**
+ * @param string $className
+ *
+ * @return array
+ *
+ * @since Method available since Release 2.3.2
+ */
+ public function getClassMethods($className)
+ {
+ $class = new ReflectionClass($className);
+ $methods = [];
+
+ foreach ($class->getMethods() as $method) {
+ if ($method->isPublic() || $method->isAbstract()) {
+ $methods[] = $method->getName();
+ }
+ }
+
+ return $methods;
+ }
+
+ /**
+ * @param string $filename
+ *
+ * @return Text_Template
+ *
+ * @since Method available since Release 3.2.4
+ */
+ private function getTemplate($filename)
+ {
+ if (!isset(self::$templates[$filename])) {
+ self::$templates[$filename] = new Text_Template($filename);
+ }
+
+ return self::$templates[$filename];
+ }
+}
+
+ @trigger_error({deprecation}, E_USER_DEPRECATED);
+{prologue}{class_declaration}
+{
+ private $__phpunit_invocationMocker;
+ private $__phpunit_originalObject;
+ private $__phpunit_configurable = {configurable};
+
+{clone}{mocked_methods}
+ public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
+ {
+ return $this->__phpunit_getInvocationMocker()->expects($matcher);
+ }
+{method}
+ public function __phpunit_setOriginalObject($originalObject)
+ {
+ $this->__phpunit_originalObject = $originalObject;
+ }
+
+ public function __phpunit_getInvocationMocker()
+ {
+ if ($this->__phpunit_invocationMocker === null) {
+ $this->__phpunit_invocationMocker = new PHPUnit_Framework_MockObject_InvocationMocker($this->__phpunit_configurable);
+ }
+
+ return $this->__phpunit_invocationMocker;
+ }
+
+ public function __phpunit_hasMatchers()
+ {
+ return $this->__phpunit_getInvocationMocker()->hasMatchers();
+ }
+
+ public function __phpunit_verify($unsetInvocationMocker = true)
+ {
+ $this->__phpunit_getInvocationMocker()->verify();
+
+ if ($unsetInvocationMocker) {
+ $this->__phpunit_invocationMocker = null;
+ }
+ }
+}{epilogue}
+
+ public function method()
+ {
+ $any = new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
+ $expects = $this->expects($any);
+ return call_user_func_array(array($expects, 'method'), func_get_args());
+ }
+ public function __clone()
+ {
+ $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker();
+ }
+
+ {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
+ {{deprecation}
+ $arguments = array({arguments_call});
+ $count = func_num_args();
+
+ if ($count > {arguments_count}) {
+ $_arguments = func_get_args();
+
+ for ($i = {arguments_count}; $i < $count; $i++) {
+ $arguments[] = $_arguments[$i];
+ }
+ }
+
+ $result = $this->__phpunit_getInvocationMocker()->invoke(
+ new PHPUnit_Framework_MockObject_Invocation_Object(
+ '{class_name}', '{method_name}', $arguments, '{return_type}', $this, {clone_arguments}
+ )
+ );
+
+ return $result;
+ }
+
+ {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
+ {{deprecation}
+ $arguments = array({arguments_call});
+ $count = func_num_args();
+
+ if ($count > {arguments_count}) {
+ $_arguments = func_get_args();
+
+ for ($i = {arguments_count}; $i < $count; $i++) {
+ $arguments[] = $_arguments[$i];
+ }
+ }
+
+ $this->__phpunit_getInvocationMocker()->invoke(
+ new PHPUnit_Framework_MockObject_Invocation_Object(
+ '{class_name}', '{method_name}', $arguments, '{return_type}', $this, {clone_arguments}
+ )
+ );
+ }
+
+ {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
+ {
+ throw new PHPUnit_Framework_MockObject_BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object');
+ }
+
+ {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
+ {
+ $arguments = array({arguments_call});
+ $count = func_num_args();
+
+ if ($count > {arguments_count}) {
+ $_arguments = func_get_args();
+
+ for ($i = {arguments_count}; $i < $count; $i++) {
+ $arguments[] = $_arguments[$i];
+ }
+ }
+
+ $this->__phpunit_getInvocationMocker()->invoke(
+ new PHPUnit_Framework_MockObject_Invocation_Object(
+ '{class_name}', '{method_name}', $arguments, '{return_type}', $this, {clone_arguments}
+ )
+ );
+
+ return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $arguments);
+ }
+
+ {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
+ {
+ $arguments = array({arguments_call});
+ $count = func_num_args();
+
+ if ($count > {arguments_count}) {
+ $_arguments = func_get_args();
+
+ for ($i = {arguments_count}; $i < $count; $i++) {
+ $arguments[] = $_arguments[$i];
+ }
+ }
+
+ $this->__phpunit_getInvocationMocker()->invoke(
+ new PHPUnit_Framework_MockObject_Invocation_Object(
+ '{class_name}', '{method_name}', $arguments, '{return_type}', $this, {clone_arguments}
+ )
+ );
+
+ call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $arguments);
+ }
+{prologue}class {class_name}
+{
+ use {trait_name};
+}
+ public function __clone()
+ {
+ $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker();
+ parent::__clone();
+ }
+{namespace}class {class_name} extends \SoapClient
+{
+ public function __construct($wsdl, array $options)
+ {
+ parent::__construct('{wsdl}', $options);
+ }
+{methods}}
+
+ public function {method_name}({arguments})
+ {
+ }
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for invocations.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Invocation
+{
+ /**
+ * @return mixed Mocked return value.
+ */
+ public function generateReturnValue();
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents a non-static invocation.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Invocation_Object extends PHPUnit_Framework_MockObject_Invocation_Static
+{
+ /**
+ * @var object
+ */
+ public $object;
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ * @param array $parameters
+ * @param string $returnType
+ * @param object $object
+ * @param bool $cloneObjects
+ */
+ public function __construct($className, $methodName, array $parameters, $returnType, $object, $cloneObjects = false)
+ {
+ parent::__construct($className, $methodName, $parameters, $returnType, $cloneObjects);
+
+ $this->object = $object;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Exporter\Exporter;
+
+/**
+ * Represents a static invocation.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Invocation_Static implements PHPUnit_Framework_MockObject_Invocation, PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * @var array
+ */
+ protected static $uncloneableExtensions = [
+ 'mysqli' => true,
+ 'SQLite' => true,
+ 'sqlite3' => true,
+ 'tidy' => true,
+ 'xmlwriter' => true,
+ 'xsl' => true
+ ];
+
+ /**
+ * @var array
+ */
+ protected static $uncloneableClasses = [
+ 'Closure',
+ 'COMPersistHelper',
+ 'IteratorIterator',
+ 'RecursiveIteratorIterator',
+ 'SplFileObject',
+ 'PDORow',
+ 'ZipArchive'
+ ];
+
+ /**
+ * @var string
+ */
+ public $className;
+
+ /**
+ * @var string
+ */
+ public $methodName;
+
+ /**
+ * @var array
+ */
+ public $parameters;
+
+ /**
+ * @var string
+ */
+ public $returnType;
+
+ /**
+ * @var bool
+ */
+ public $returnTypeNullable = false;
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ * @param array $parameters
+ * @param string $returnType
+ * @param bool $cloneObjects
+ */
+ public function __construct($className, $methodName, array $parameters, $returnType, $cloneObjects = false)
+ {
+ $this->className = $className;
+ $this->methodName = $methodName;
+ $this->parameters = $parameters;
+
+ if (strpos($returnType, '?') === 0) {
+ $returnType = substr($returnType, 1);
+ $this->returnTypeNullable = true;
+ }
+
+ $this->returnType = $returnType;
+
+ if (!$cloneObjects) {
+ return;
+ }
+
+ foreach ($this->parameters as $key => $value) {
+ if (is_object($value)) {
+ $this->parameters[$key] = $this->cloneObject($value);
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ $exporter = new Exporter;
+
+ return sprintf(
+ '%s::%s(%s)%s',
+ $this->className,
+ $this->methodName,
+ implode(
+ ', ',
+ array_map(
+ [$exporter, 'shortenedExport'],
+ $this->parameters
+ )
+ ),
+ $this->returnType ? sprintf(': %s', $this->returnType) : ''
+ );
+ }
+
+ /**
+ * @return mixed Mocked return value.
+ */
+ public function generateReturnValue()
+ {
+ switch ($this->returnType) {
+ case '': return;
+ case 'string': return $this->returnTypeNullable ? null : '';
+ case 'float': return $this->returnTypeNullable ? null : 0.0;
+ case 'int': return $this->returnTypeNullable ? null : 0;
+ case 'bool': return $this->returnTypeNullable ? null : false;
+ case 'array': return $this->returnTypeNullable ? null : [];
+ case 'void': return;
+
+ case 'callable':
+ case 'Closure':
+ return function () {};
+
+ case 'Traversable':
+ case 'Generator':
+ $generator = function () { yield; };
+
+ return $generator();
+
+ default:
+ if ($this->returnTypeNullable) {
+ return null;
+ }
+
+ $generator = new PHPUnit_Framework_MockObject_Generator;
+
+ return $generator->getMock($this->returnType, [], [], '', false);
+ }
+ }
+
+ /**
+ * @param object $original
+ *
+ * @return object
+ */
+ protected function cloneObject($original)
+ {
+ $cloneable = null;
+ $object = new ReflectionObject($original);
+
+ // Check the blacklist before asking PHP reflection to work around
+ // https://bugs.php.net/bug.php?id=53967
+ if ($object->isInternal() &&
+ isset(self::$uncloneableExtensions[$object->getExtensionName()])) {
+ $cloneable = false;
+ }
+
+ if ($cloneable === null) {
+ foreach (self::$uncloneableClasses as $class) {
+ if ($original instanceof $class) {
+ $cloneable = false;
+ break;
+ }
+ }
+ }
+
+ if ($cloneable === null && method_exists($object, 'isCloneable')) {
+ $cloneable = $object->isCloneable();
+ }
+
+ if ($cloneable === null && $object->hasMethod('__clone')) {
+ $method = $object->getMethod('__clone');
+ $cloneable = $method->isPublic();
+ }
+
+ if ($cloneable === null) {
+ $cloneable = true;
+ }
+
+ if ($cloneable) {
+ try {
+ return clone $original;
+ } catch (Exception $e) {
+ return $original;
+ }
+ } else {
+ return $original;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Mocker for invocations which are sent from
+ * PHPUnit_Framework_MockObject_MockObject objects.
+ *
+ * Keeps track of all expectations and stubs as well as registering
+ * identifications for builders.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_InvocationMocker implements PHPUnit_Framework_MockObject_Stub_MatcherCollection, PHPUnit_Framework_MockObject_Invokable, PHPUnit_Framework_MockObject_Builder_Namespace
+{
+ /**
+ * @var PHPUnit_Framework_MockObject_Matcher_Invocation[]
+ */
+ protected $matchers = [];
+
+ /**
+ * @var PHPUnit_Framework_MockObject_Builder_Match[]
+ */
+ protected $builderMap = [];
+
+ /**
+ * @var string[]
+ */
+ private $configurableMethods = [];
+
+ /**
+ * @param array $configurableMethods
+ */
+ public function __construct(array $configurableMethods)
+ {
+ $this->configurableMethods = $configurableMethods;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
+ */
+ public function addMatcher(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
+ {
+ $this->matchers[] = $matcher;
+ }
+
+ /**
+ * @since Method available since Release 1.1.0
+ */
+ public function hasMatchers()
+ {
+ foreach ($this->matchers as $matcher) {
+ if ($matcher->hasMatchers()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param mixed $id
+ *
+ * @return bool|null
+ */
+ public function lookupId($id)
+ {
+ if (isset($this->builderMap[$id])) {
+ return $this->builderMap[$id];
+ }
+
+ return;
+ }
+
+ /**
+ * @param mixed $id
+ * @param PHPUnit_Framework_MockObject_Builder_Match $builder
+ *
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ */
+ public function registerId($id, PHPUnit_Framework_MockObject_Builder_Match $builder)
+ {
+ if (isset($this->builderMap[$id])) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'Match builder with id <' . $id . '> is already registered.'
+ );
+ }
+
+ $this->builderMap[$id] = $builder;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
+ {
+ return new PHPUnit_Framework_MockObject_Builder_InvocationMocker(
+ $this,
+ $matcher,
+ $this->configurableMethods
+ );
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return mixed
+ *
+ * @throws Exception
+ */
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ $exception = null;
+ $hasReturnValue = false;
+ $returnValue = null;
+
+ foreach ($this->matchers as $match) {
+ try {
+ if ($match->matches($invocation)) {
+ $value = $match->invoked($invocation);
+
+ if (!$hasReturnValue) {
+ $returnValue = $value;
+ $hasReturnValue = true;
+ }
+ }
+ } catch (Exception $e) {
+ $exception = $e;
+ }
+ }
+
+ if ($exception !== null) {
+ throw $exception;
+ }
+
+ if ($hasReturnValue) {
+ return $returnValue;
+ } elseif (strtolower($invocation->methodName) == '__tostring') {
+ return '';
+ }
+
+ return $invocation->generateReturnValue();
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ foreach ($this->matchers as $matcher) {
+ if (!$matcher->matches($invocation)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function verify()
+ {
+ foreach ($this->matchers as $matcher) {
+ $matcher->verify();
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for classes which can be invoked.
+ *
+ * The invocation will be taken from a mock object and passed to an object
+ * of this class.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Invokable extends PHPUnit_Framework_MockObject_Verifiable
+{
+ /**
+ * Invokes the invocation object $invocation so that it can be checked for
+ * expectations or matched against stubs.
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation The invocation object passed from mock object
+ *
+ * @return object
+ */
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation);
+
+ /**
+ * Checks if the invocation matches.
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation The invocation object passed from mock object
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Main matcher which defines a full expectation using method, parameter and
+ * invocation matchers.
+ * This matcher encapsulates all the other matchers and allows the builder to
+ * set the specific matchers when the appropriate methods are called (once(),
+ * where() etc.).
+ *
+ * All properties are public so that they can easily be accessed by the builder.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher implements PHPUnit_Framework_MockObject_Matcher_Invocation
+{
+ /**
+ * @var PHPUnit_Framework_MockObject_Matcher_Invocation
+ */
+ public $invocationMatcher;
+
+ /**
+ * @var mixed
+ */
+ public $afterMatchBuilderId = null;
+
+ /**
+ * @var bool
+ */
+ public $afterMatchBuilderIsInvoked = false;
+
+ /**
+ * @var PHPUnit_Framework_MockObject_Matcher_MethodName
+ */
+ public $methodNameMatcher = null;
+
+ /**
+ * @var PHPUnit_Framework_MockObject_Matcher_Parameters
+ */
+ public $parametersMatcher = null;
+
+ /**
+ * @var PHPUnit_Framework_MockObject_Stub
+ */
+ public $stub = null;
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher
+ */
+ public function __construct(PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher)
+ {
+ $this->invocationMatcher = $invocationMatcher;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ $list = [];
+
+ if ($this->invocationMatcher !== null) {
+ $list[] = $this->invocationMatcher->toString();
+ }
+
+ if ($this->methodNameMatcher !== null) {
+ $list[] = 'where ' . $this->methodNameMatcher->toString();
+ }
+
+ if ($this->parametersMatcher !== null) {
+ $list[] = 'and ' . $this->parametersMatcher->toString();
+ }
+
+ if ($this->afterMatchBuilderId !== null) {
+ $list[] = 'after ' . $this->afterMatchBuilderId;
+ }
+
+ if ($this->stub !== null) {
+ $list[] = 'will ' . $this->stub->toString();
+ }
+
+ return implode(' ', $list);
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return mixed
+ */
+ public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ if ($this->invocationMatcher === null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'No invocation matcher is set'
+ );
+ }
+
+ if ($this->methodNameMatcher === null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
+ }
+
+ if ($this->afterMatchBuilderId !== null) {
+ $builder = $invocation->object
+ ->__phpunit_getInvocationMocker()
+ ->lookupId($this->afterMatchBuilderId);
+
+ if (!$builder) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'No builder found for match builder identification <%s>',
+ $this->afterMatchBuilderId
+ )
+ );
+ }
+
+ $matcher = $builder->getMatcher();
+
+ if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) {
+ $this->afterMatchBuilderIsInvoked = true;
+ }
+ }
+
+ $this->invocationMatcher->invoked($invocation);
+
+ try {
+ if ($this->parametersMatcher !== null &&
+ !$this->parametersMatcher->matches($invocation)) {
+ $this->parametersMatcher->verify();
+ }
+ } catch (PHPUnit_Framework_ExpectationFailedException $e) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ sprintf(
+ "Expectation failed for %s when %s\n%s",
+ $this->methodNameMatcher->toString(),
+ $this->invocationMatcher->toString(),
+ $e->getMessage()
+ ),
+ $e->getComparisonFailure()
+ );
+ }
+
+ if ($this->stub) {
+ return $this->stub->invoke($invocation);
+ }
+
+ return $invocation->generateReturnValue();
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ if ($this->afterMatchBuilderId !== null) {
+ $builder = $invocation->object
+ ->__phpunit_getInvocationMocker()
+ ->lookupId($this->afterMatchBuilderId);
+
+ if (!$builder) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'No builder found for match builder identification <%s>',
+ $this->afterMatchBuilderId
+ )
+ );
+ }
+
+ $matcher = $builder->getMatcher();
+
+ if (!$matcher) {
+ return false;
+ }
+
+ if (!$matcher->invocationMatcher->hasBeenInvoked()) {
+ return false;
+ }
+ }
+
+ if ($this->invocationMatcher === null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'No invocation matcher is set'
+ );
+ }
+
+ if ($this->methodNameMatcher === null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
+ }
+
+ if (!$this->invocationMatcher->matches($invocation)) {
+ return false;
+ }
+
+ try {
+ if (!$this->methodNameMatcher->matches($invocation)) {
+ return false;
+ }
+ } catch (PHPUnit_Framework_ExpectationFailedException $e) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ sprintf(
+ "Expectation failed for %s when %s\n%s",
+ $this->methodNameMatcher->toString(),
+ $this->invocationMatcher->toString(),
+ $e->getMessage()
+ ),
+ $e->getComparisonFailure()
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify()
+ {
+ if ($this->invocationMatcher === null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'No invocation matcher is set'
+ );
+ }
+
+ if ($this->methodNameMatcher === null) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
+ }
+
+ try {
+ $this->invocationMatcher->verify();
+
+ if ($this->parametersMatcher === null) {
+ $this->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_AnyParameters;
+ }
+
+ $invocationIsAny = $this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
+ $invocationIsNever = $this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_InvokedCount && $this->invocationMatcher->isNever();
+
+ if (!$invocationIsAny && !$invocationIsNever) {
+ $this->parametersMatcher->verify();
+ }
+ } catch (PHPUnit_Framework_ExpectationFailedException $e) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ sprintf(
+ "Expectation failed for %s when %s.\n%s",
+ $this->methodNameMatcher->toString(),
+ $this->invocationMatcher->toString(),
+ PHPUnit_Framework_TestFailure::exceptionToString($e)
+ )
+ );
+ }
+ }
+
+ /**
+ * @since Method available since Release 1.2.4
+ */
+ public function hasMatchers()
+ {
+ if ($this->invocationMatcher !== null &&
+ !$this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount) {
+ return true;
+ }
+
+ return false;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which checks if a method has been invoked zero or more
+ * times. This matcher will always match.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
+{
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'invoked zero or more times';
+ }
+
+ /**
+ */
+ public function verify()
+ {
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which allows any parameters to a method.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_AnyParameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
+{
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'with any parameters';
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ return true;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which looks for sets of specific parameters in the invocations.
+ *
+ * Checks the parameters of the incoming invocations, the parameter list is
+ * checked against the defined constraints in $parameters. If the constraint
+ * is met it will return true in matches().
+ *
+ * It takes a list of match groups and and increases a call index after each invocation.
+ * So the first invocation uses the first group of constraints, the second the next and so on.
+ */
+class PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
+{
+ /**
+ * @var array
+ */
+ private $parameterGroups = [];
+
+ /**
+ * @var array
+ */
+ private $invocations = [];
+
+ /**
+ * @param array $parameterGroups
+ */
+ public function __construct(array $parameterGroups)
+ {
+ foreach ($parameterGroups as $index => $parameters) {
+ foreach ($parameters as $parameter) {
+ if (!$parameter instanceof PHPUnit_Framework_Constraint) {
+ $parameter = new PHPUnit_Framework_Constraint_IsEqual($parameter);
+ }
+
+ $this->parameterGroups[$index][] = $parameter;
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ $text = 'with consecutive parameters';
+
+ return $text;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ $this->invocations[] = $invocation;
+ $callIndex = count($this->invocations) - 1;
+
+ $this->verifyInvocation($invocation, $callIndex);
+
+ return false;
+ }
+
+ public function verify()
+ {
+ foreach ($this->invocations as $callIndex => $invocation) {
+ $this->verifyInvocation($invocation, $callIndex);
+ }
+ }
+
+ /**
+ * Verify a single invocation
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ * @param int $callIndex
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ private function verifyInvocation(PHPUnit_Framework_MockObject_Invocation $invocation, $callIndex)
+ {
+ if (isset($this->parameterGroups[$callIndex])) {
+ $parameters = $this->parameterGroups[$callIndex];
+ } else {
+ // no parameter assertion for this call index
+ return;
+ }
+
+ if ($invocation === null) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ 'Mocked method does not exist.'
+ );
+ }
+
+ if (count($invocation->parameters) < count($parameters)) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ sprintf(
+ 'Parameter count for invocation %s is too low.',
+ $invocation->toString()
+ )
+ );
+ }
+
+ foreach ($parameters as $i => $parameter) {
+ $parameter->evaluate(
+ $invocation->parameters[$i],
+ sprintf(
+ 'Parameter %s for invocation #%d %s does not match expected ' .
+ 'value.',
+ $i,
+ $callIndex,
+ $invocation->toString()
+ )
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for classes which matches an invocation based on its
+ * method name, argument, order or call count.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Matcher_Invocation extends PHPUnit_Framework_SelfDescribing, PHPUnit_Framework_MockObject_Verifiable
+{
+ /**
+ * Registers the invocation $invocation in the object as being invoked.
+ * This will only occur after matches() returns true which means the
+ * current invocation is the correct one.
+ *
+ * The matcher can store information from the invocation which can later
+ * be checked in verify(), or it can check the values directly and throw
+ * and exception if an expectation is not met.
+ *
+ * If the matcher is a stub it will also have a return value.
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
+ *
+ * @return mixed
+ */
+ public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation);
+
+ /**
+ * Checks if the invocation $invocation matches the current rules. If it does
+ * the matcher will get the invoked() method called which should check if an
+ * expectation is met.
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which checks if a method was invoked at a certain index.
+ *
+ * If the expected index number does not match the current invocation index it
+ * will not match which means it skips all method and parameter matching. Only
+ * once the index is reached will the method and parameter start matching and
+ * verifying.
+ *
+ * If the index is never reached it will throw an exception in index.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex implements PHPUnit_Framework_MockObject_Matcher_Invocation
+{
+ /**
+ * @var int
+ */
+ protected $sequenceIndex;
+
+ /**
+ * @var int
+ */
+ protected $currentIndex = -1;
+
+ /**
+ * @param int $sequenceIndex
+ */
+ public function __construct($sequenceIndex)
+ {
+ $this->sequenceIndex = $sequenceIndex;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'invoked at sequence index ' . $this->sequenceIndex;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ $this->currentIndex++;
+
+ return $this->currentIndex == $this->sequenceIndex;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ */
+ public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ }
+
+ /**
+ * Verifies that the current expectation is valid. If everything is OK the
+ * code should just return, if not it must throw an exception.
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify()
+ {
+ if ($this->currentIndex < $this->sequenceIndex) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ sprintf(
+ 'The expected invocation at index %s was never reached.',
+ $this->sequenceIndex
+ )
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which checks if a method has been invoked at least
+ * N times.
+ *
+ * @since Class available since Release 2.2.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
+{
+ /**
+ * @var int
+ */
+ private $requiredInvocations;
+
+ /**
+ * @param int $requiredInvocations
+ */
+ public function __construct($requiredInvocations)
+ {
+ $this->requiredInvocations = $requiredInvocations;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'invoked at least ' . $this->requiredInvocations . ' times';
+ }
+
+ /**
+ * Verifies that the current expectation is valid. If everything is OK the
+ * code should just return, if not it must throw an exception.
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify()
+ {
+ $count = $this->getInvocationCount();
+
+ if ($count < $this->requiredInvocations) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ 'Expected invocation at least ' . $this->requiredInvocations .
+ ' times but it occurred ' . $count . ' time(s).'
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which checks if a method has been invoked at least one
+ * time.
+ *
+ * If the number of invocations is 0 it will throw an exception in verify.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
+{
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'invoked at least once';
+ }
+
+ /**
+ * Verifies that the current expectation is valid. If everything is OK the
+ * code should just return, if not it must throw an exception.
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify()
+ {
+ $count = $this->getInvocationCount();
+
+ if ($count < 1) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ 'Expected invocation at least once but it never occurred.'
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which checks if a method has been invoked at least
+ * N times.
+ *
+ * @since Class available since Release 2.2.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
+{
+ /**
+ * @var int
+ */
+ private $allowedInvocations;
+
+ /**
+ * @param int $allowedInvocations
+ */
+ public function __construct($allowedInvocations)
+ {
+ $this->allowedInvocations = $allowedInvocations;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'invoked at most ' . $this->allowedInvocations . ' times';
+ }
+
+ /**
+ * Verifies that the current expectation is valid. If everything is OK the
+ * code should just return, if not it must throw an exception.
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify()
+ {
+ $count = $this->getInvocationCount();
+
+ if ($count > $this->allowedInvocations) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ 'Expected invocation at most ' . $this->allowedInvocations .
+ ' times but it occurred ' . $count . ' time(s).'
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which checks if a method has been invoked a certain amount
+ * of times.
+ * If the number of invocations exceeds the value it will immediately throw an
+ * exception,
+ * If the number is less it will later be checked in verify() and also throw an
+ * exception.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_InvokedCount extends PHPUnit_Framework_MockObject_Matcher_InvokedRecorder
+{
+ /**
+ * @var int
+ */
+ protected $expectedCount;
+
+ /**
+ * @param int $expectedCount
+ */
+ public function __construct($expectedCount)
+ {
+ $this->expectedCount = $expectedCount;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isNever()
+ {
+ return $this->expectedCount == 0;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'invoked ' . $this->expectedCount . ' time(s)';
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ parent::invoked($invocation);
+
+ $count = $this->getInvocationCount();
+
+ if ($count > $this->expectedCount) {
+ $message = $invocation->toString() . ' ';
+
+ switch ($this->expectedCount) {
+ case 0: {
+ $message .= 'was not expected to be called.';
+ }
+ break;
+
+ case 1: {
+ $message .= 'was not expected to be called more than once.';
+ }
+ break;
+
+ default: {
+ $message .= sprintf(
+ 'was not expected to be called more than %d times.',
+ $this->expectedCount
+ );
+ }
+ }
+
+ throw new PHPUnit_Framework_ExpectationFailedException($message);
+ }
+ }
+
+ /**
+ * Verifies that the current expectation is valid. If everything is OK the
+ * code should just return, if not it must throw an exception.
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify()
+ {
+ $count = $this->getInvocationCount();
+
+ if ($count !== $this->expectedCount) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ sprintf(
+ 'Method was expected to be called %d times, ' .
+ 'actually called %d times.',
+ $this->expectedCount,
+ $count
+ )
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Records invocations and provides convenience methods for checking them later
+ * on.
+ * This abstract class can be implemented by matchers which needs to check the
+ * number of times an invocation has occurred.
+ *
+ * @since Class available since Release 1.0.0
+ * @abstract
+ */
+abstract class PHPUnit_Framework_MockObject_Matcher_InvokedRecorder implements PHPUnit_Framework_MockObject_Matcher_Invocation
+{
+ /**
+ * @var PHPUnit_Framework_MockObject_Invocation[]
+ */
+ protected $invocations = [];
+
+ /**
+ * @return int
+ */
+ public function getInvocationCount()
+ {
+ return count($this->invocations);
+ }
+
+ /**
+ * @return PHPUnit_Framework_MockObject_Invocation[]
+ */
+ public function getInvocations()
+ {
+ return $this->invocations;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasBeenInvoked()
+ {
+ return count($this->invocations) > 0;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ */
+ public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ $this->invocations[] = $invocation;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ return true;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which looks for a specific method name in the invocations.
+ *
+ * Checks the method name all incoming invocations, the name is checked against
+ * the defined constraint $constraint. If the constraint is met it will return
+ * true in matches().
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_MethodName extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
+{
+ /**
+ * @var PHPUnit_Framework_Constraint
+ */
+ protected $constraint;
+
+ /**
+ * @param PHPUnit_Framework_Constraint|string
+ *
+ * @throws PHPUnit_Framework_Constraint
+ */
+ public function __construct($constraint)
+ {
+ if (!$constraint instanceof PHPUnit_Framework_Constraint) {
+ if (!is_string($constraint)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_IsEqual(
+ $constraint,
+ 0,
+ 10,
+ false,
+ true
+ );
+ }
+
+ $this->constraint = $constraint;
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'method name ' . $this->constraint->toString();
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ return $this->constraint->evaluate($invocation->methodName, '', true);
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which looks for specific parameters in the invocations.
+ *
+ * Checks the parameters of all incoming invocations, the parameter list is
+ * checked against the defined constraints in $parameters. If the constraint
+ * is met it will return true in matches().
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Matcher_Parameters extends PHPUnit_Framework_MockObject_Matcher_StatelessInvocation
+{
+ /**
+ * @var PHPUnit_Framework_Constraint[]
+ */
+ protected $parameters = [];
+
+ /**
+ * @var PHPUnit_Framework_MockObject_Invocation
+ */
+ protected $invocation;
+
+ /**
+ * @var PHPUnit_Framework_ExpectationFailedException
+ */
+ private $parameterVerificationResult;
+
+ /**
+ * @param array $parameters
+ */
+ public function __construct(array $parameters)
+ {
+ foreach ($parameters as $parameter) {
+ if (!($parameter instanceof PHPUnit_Framework_Constraint)) {
+ $parameter = new PHPUnit_Framework_Constraint_IsEqual(
+ $parameter
+ );
+ }
+
+ $this->parameters[] = $parameter;
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ $text = 'with parameter';
+
+ foreach ($this->parameters as $index => $parameter) {
+ if ($index > 0) {
+ $text .= ' and';
+ }
+
+ $text .= ' ' . $index . ' ' . $parameter->toString();
+ }
+
+ return $text;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation
+ *
+ * @return bool
+ */
+ public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ $this->invocation = $invocation;
+ $this->parameterVerificationResult = null;
+
+ try {
+ $this->parameterVerificationResult = $this->verify();
+
+ return $this->parameterVerificationResult;
+ } catch (PHPUnit_Framework_ExpectationFailedException $e) {
+ $this->parameterVerificationResult = $e;
+
+ throw $this->parameterVerificationResult;
+ }
+ }
+
+ /**
+ * Checks if the invocation $invocation matches the current rules. If it
+ * does the matcher will get the invoked() method called which should check
+ * if an expectation is met.
+ *
+ * @return bool
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify()
+ {
+ if (isset($this->parameterVerificationResult)) {
+ return $this->guardAgainstDuplicateEvaluationOfParameterConstraints();
+ }
+
+ if ($this->invocation === null) {
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ 'Mocked method does not exist.'
+ );
+ }
+
+ if (count($this->invocation->parameters) < count($this->parameters)) {
+ $message = 'Parameter count for invocation %s is too low.';
+
+ // The user called `->with($this->anything())`, but may have meant
+ // `->withAnyParameters()`.
+ //
+ // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199
+ if (count($this->parameters) === 1 &&
+ get_class($this->parameters[0]) === 'PHPUnit_Framework_Constraint_IsAnything') {
+ $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead.";
+ }
+
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ sprintf($message, $this->invocation->toString())
+ );
+ }
+
+ foreach ($this->parameters as $i => $parameter) {
+ $parameter->evaluate(
+ $this->invocation->parameters[$i],
+ sprintf(
+ 'Parameter %s for invocation %s does not match expected ' .
+ 'value.',
+ $i,
+ $this->invocation->toString()
+ )
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * @return bool
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ private function guardAgainstDuplicateEvaluationOfParameterConstraints()
+ {
+ if ($this->parameterVerificationResult instanceof Exception) {
+ throw $this->parameterVerificationResult;
+ }
+
+ return (bool) $this->parameterVerificationResult;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Invocation matcher which does not care about previous state from earlier
+ * invocations.
+ *
+ * This abstract class can be implemented by matchers which does not care about
+ * state but only the current run-time value of the invocation itself.
+ *
+ * @since Class available since Release 1.0.0
+ * @abstract
+ */
+abstract class PHPUnit_Framework_MockObject_Matcher_StatelessInvocation implements PHPUnit_Framework_MockObject_Matcher_Invocation
+{
+ /**
+ * Registers the invocation $invocation in the object as being invoked.
+ * This will only occur after matches() returns true which means the
+ * current invocation is the correct one.
+ *
+ * The matcher can store information from the invocation which can later
+ * be checked in verify(), or it can check the values directly and throw
+ * and exception if an expectation is not met.
+ *
+ * If the matcher is a stub it will also have a return value.
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
+ *
+ * @return mixed
+ */
+ public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ }
+
+ /**
+ * Checks if the invocation $invocation matches the current rules. If it does
+ * the matcher will get the invoked() method called which should check if an
+ * expectation is met.
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation Object containing information on a mocked or stubbed method which was invoked
+ *
+ * @return bool
+ */
+ public function verify()
+ {
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Implementation of the Builder pattern for Mock objects.
+ *
+ * @since File available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_MockBuilder
+{
+ /**
+ * @var PHPUnit_Framework_TestCase
+ */
+ private $testCase;
+
+ /**
+ * @var string
+ */
+ private $type;
+
+ /**
+ * @var array
+ */
+ private $methods = [];
+
+ /**
+ * @var array
+ */
+ private $methodsExcept = [];
+
+ /**
+ * @var string
+ */
+ private $mockClassName = '';
+
+ /**
+ * @var array
+ */
+ private $constructorArgs = [];
+
+ /**
+ * @var bool
+ */
+ private $originalConstructor = true;
+
+ /**
+ * @var bool
+ */
+ private $originalClone = true;
+
+ /**
+ * @var bool
+ */
+ private $autoload = true;
+
+ /**
+ * @var bool
+ */
+ private $cloneArguments = false;
+
+ /**
+ * @var bool
+ */
+ private $callOriginalMethods = false;
+
+ /**
+ * @var object
+ */
+ private $proxyTarget = null;
+
+ /**
+ * @var bool
+ */
+ private $allowMockingUnknownTypes = true;
+
+ /**
+ * @var PHPUnit_Framework_MockObject_Generator
+ */
+ private $generator;
+
+ /**
+ * @param PHPUnit_Framework_TestCase $testCase
+ * @param array|string $type
+ */
+ public function __construct(PHPUnit_Framework_TestCase $testCase, $type)
+ {
+ $this->testCase = $testCase;
+ $this->type = $type;
+ $this->generator = new PHPUnit_Framework_MockObject_Generator;
+ }
+
+ /**
+ * Creates a mock object using a fluent interface.
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ */
+ public function getMock()
+ {
+ $object = $this->generator->getMock(
+ $this->type,
+ $this->methods,
+ $this->constructorArgs,
+ $this->mockClassName,
+ $this->originalConstructor,
+ $this->originalClone,
+ $this->autoload,
+ $this->cloneArguments,
+ $this->callOriginalMethods,
+ $this->proxyTarget,
+ $this->allowMockingUnknownTypes
+ );
+
+ $this->testCase->registerMockObject($object);
+
+ return $object;
+ }
+
+ /**
+ * Creates a mock object for an abstract class using a fluent interface.
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ */
+ public function getMockForAbstractClass()
+ {
+ $object = $this->generator->getMockForAbstractClass(
+ $this->type,
+ $this->constructorArgs,
+ $this->mockClassName,
+ $this->originalConstructor,
+ $this->originalClone,
+ $this->autoload,
+ $this->methods,
+ $this->cloneArguments
+ );
+
+ $this->testCase->registerMockObject($object);
+
+ return $object;
+ }
+
+ /**
+ * Creates a mock object for a trait using a fluent interface.
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ */
+ public function getMockForTrait()
+ {
+ $object = $this->generator->getMockForTrait(
+ $this->type,
+ $this->constructorArgs,
+ $this->mockClassName,
+ $this->originalConstructor,
+ $this->originalClone,
+ $this->autoload,
+ $this->methods,
+ $this->cloneArguments
+ );
+
+ $this->testCase->registerMockObject($object);
+
+ return $object;
+ }
+
+ /**
+ * Specifies the subset of methods to mock. Default is to mock all of them.
+ *
+ * @param array|null $methods
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function setMethods(array $methods = null)
+ {
+ $this->methods = $methods;
+
+ return $this;
+ }
+
+ /**
+ * Specifies the subset of methods to not mock. Default is to mock all of them.
+ *
+ * @param array $methods
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function setMethodsExcept(array $methods = [])
+ {
+ $this->methodsExcept = $methods;
+
+ $this->setMethods(
+ array_diff(
+ $this->generator->getClassMethods($this->type),
+ $this->methodsExcept
+ )
+ );
+
+ return $this;
+ }
+
+ /**
+ * Specifies the arguments for the constructor.
+ *
+ * @param array $args
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function setConstructorArgs(array $args)
+ {
+ $this->constructorArgs = $args;
+
+ return $this;
+ }
+
+ /**
+ * Specifies the name for the mock class.
+ *
+ * @param string $name
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function setMockClassName($name)
+ {
+ $this->mockClassName = $name;
+
+ return $this;
+ }
+
+ /**
+ * Disables the invocation of the original constructor.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function disableOriginalConstructor()
+ {
+ $this->originalConstructor = false;
+
+ return $this;
+ }
+
+ /**
+ * Enables the invocation of the original constructor.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 1.2.0
+ */
+ public function enableOriginalConstructor()
+ {
+ $this->originalConstructor = true;
+
+ return $this;
+ }
+
+ /**
+ * Disables the invocation of the original clone constructor.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function disableOriginalClone()
+ {
+ $this->originalClone = false;
+
+ return $this;
+ }
+
+ /**
+ * Enables the invocation of the original clone constructor.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 1.2.0
+ */
+ public function enableOriginalClone()
+ {
+ $this->originalClone = true;
+
+ return $this;
+ }
+
+ /**
+ * Disables the use of class autoloading while creating the mock object.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function disableAutoload()
+ {
+ $this->autoload = false;
+
+ return $this;
+ }
+
+ /**
+ * Enables the use of class autoloading while creating the mock object.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 1.2.0
+ */
+ public function enableAutoload()
+ {
+ $this->autoload = true;
+
+ return $this;
+ }
+
+ /**
+ * Disables the cloning of arguments passed to mocked methods.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 1.2.0
+ */
+ public function disableArgumentCloning()
+ {
+ $this->cloneArguments = false;
+
+ return $this;
+ }
+
+ /**
+ * Enables the cloning of arguments passed to mocked methods.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 1.2.0
+ */
+ public function enableArgumentCloning()
+ {
+ $this->cloneArguments = true;
+
+ return $this;
+ }
+
+ /**
+ * Enables the invocation of the original methods.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 2.0.0
+ */
+ public function enableProxyingToOriginalMethods()
+ {
+ $this->callOriginalMethods = true;
+
+ return $this;
+ }
+
+ /**
+ * Disables the invocation of the original methods.
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 2.0.0
+ */
+ public function disableProxyingToOriginalMethods()
+ {
+ $this->callOriginalMethods = false;
+ $this->proxyTarget = null;
+
+ return $this;
+ }
+
+ /**
+ * Sets the proxy target.
+ *
+ * @param object $object
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 2.0.0
+ */
+ public function setProxyTarget($object)
+ {
+ $this->proxyTarget = $object;
+
+ return $this;
+ }
+
+ /**
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 3.2.0
+ */
+ public function allowMockingUnknownTypes()
+ {
+ $this->allowMockingUnknownTypes = true;
+
+ return $this;
+ }
+
+ /**
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ *
+ * @since Method available since Release 3.2.0
+ */
+ public function disallowMockingUnknownTypes()
+ {
+ $this->allowMockingUnknownTypes = false;
+
+ return $this;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for all mock objects which are generated by
+ * PHPUnit_Framework_MockObject_MockBuilder.
+ *
+ * @method PHPUnit_Framework_MockObject_Builder_InvocationMocker method($constraint)
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_MockObject /*extends PHPUnit_Framework_MockObject_Verifiable*/
+{
+ /**
+ * Registers a new expectation in the mock object and returns the match
+ * object which can be infused with further details.
+ *
+ * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher
+ *
+ * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+ */
+ public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher);
+
+ /**
+ * @return PHPUnit_Framework_MockObject_InvocationMocker
+ *
+ * @since Method available since Release 2.0.0
+ */
+ public function __phpunit_setOriginalObject($originalObject);
+
+ /**
+ * @return PHPUnit_Framework_MockObject_InvocationMocker
+ */
+ public function __phpunit_getInvocationMocker();
+
+ /**
+ * Verifies that the current expectation is valid. If everything is OK the
+ * code should just return, if not it must throw an exception.
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function __phpunit_verify();
+
+ /**
+ * @return bool
+ */
+ public function __phpunit_hasMatchers();
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An object that stubs the process of a normal method for a mock object.
+ *
+ * The stub object will replace the code for the stubbed method and return a
+ * specific value instead of the original value.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Stub extends PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * Fakes the processing of the invocation $invocation by returning a
+ * specific value.
+ *
+ * @param PHPUnit_Framework_MockObject_Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers
+ *
+ * @return mixed
+ */
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Exporter\Exporter;
+
+/**
+ * Stubs a method by returning a user-defined stack of values.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls implements PHPUnit_Framework_MockObject_Stub
+{
+ protected $stack;
+ protected $value;
+
+ public function __construct($stack)
+ {
+ $this->stack = $stack;
+ }
+
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ $this->value = array_shift($this->stack);
+
+ if ($this->value instanceof PHPUnit_Framework_MockObject_Stub) {
+ $this->value = $this->value->invoke($invocation);
+ }
+
+ return $this->value;
+ }
+
+ public function toString()
+ {
+ $exporter = new Exporter;
+
+ return sprintf(
+ 'return user-specified value %s',
+ $exporter->export($this->value)
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Exporter\Exporter;
+
+/**
+ * Stubs a method by raising a user-defined exception.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Stub_Exception implements PHPUnit_Framework_MockObject_Stub
+{
+ protected $exception;
+
+ public function __construct($exception)
+ {
+ // TODO Replace check with type declaration when support for PHP 5 is dropped
+ if (!$exception instanceof Throwable && !$exception instanceof Exception) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'Exception must be an instance of Throwable (PHP 7) or Exception (PHP 5)'
+ );
+ }
+
+ $this->exception = $exception;
+ }
+
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ throw $this->exception;
+ }
+
+ public function toString()
+ {
+ $exporter = new Exporter;
+
+ return sprintf(
+ 'raise user-specified exception %s',
+ $exporter->export($this->exception)
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stubs a method by returning a user-defined value.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Stub_MatcherCollection
+{
+ /**
+ * Adds a new matcher to the collection which can be used as an expectation
+ * or a stub.
+ *
+ * @param PHPUnit_Framework_MockObject_Matcher_Invocation $matcher Matcher for invocations to mock objects
+ */
+ public function addMatcher(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher);
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Exporter\Exporter;
+
+/**
+ * Stubs a method by returning a user-defined value.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Stub_Return implements PHPUnit_Framework_MockObject_Stub
+{
+ protected $value;
+
+ public function __construct($value)
+ {
+ $this->value = $value;
+ }
+
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ return $this->value;
+ }
+
+ public function toString()
+ {
+ $exporter = new Exporter;
+
+ return sprintf(
+ 'return user-specified value %s',
+ $exporter->export($this->value)
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stubs a method by returning an argument that was passed to the mocked method.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Stub_ReturnArgument extends PHPUnit_Framework_MockObject_Stub_Return
+{
+ protected $argumentIndex;
+
+ public function __construct($argumentIndex)
+ {
+ $this->argumentIndex = $argumentIndex;
+ }
+
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ if (isset($invocation->parameters[$this->argumentIndex])) {
+ return $invocation->parameters[$this->argumentIndex];
+ } else {
+ return;
+ }
+ }
+
+ public function toString()
+ {
+ return sprintf('return argument #%d', $this->argumentIndex);
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Stub_ReturnCallback implements PHPUnit_Framework_MockObject_Stub
+{
+ protected $callback;
+
+ public function __construct($callback)
+ {
+ $this->callback = $callback;
+ }
+
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ return call_user_func_array($this->callback, $invocation->parameters);
+ }
+
+ public function toString()
+ {
+ if (is_array($this->callback)) {
+ if (is_object($this->callback[0])) {
+ $class = get_class($this->callback[0]);
+ $type = '->';
+ } else {
+ $class = $this->callback[0];
+ $type = '::';
+ }
+
+ return sprintf(
+ 'return result of user defined callback %s%s%s() with the ' .
+ 'passed arguments',
+ $class,
+ $type,
+ $this->callback[1]
+ );
+ } else {
+ return 'return result of user defined callback ' . $this->callback .
+ ' with the passed arguments';
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stubs a method by returning a user-defined reference to a value.
+ *
+ * @since Class available since Release 3.0.7
+ */
+class PHPUnit_Framework_MockObject_Stub_ReturnReference extends PHPUnit_Framework_MockObject_Stub_Return
+{
+ public function __construct(&$value)
+ {
+ $this->value = &$value;
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stubs a method by returning the current object.
+ *
+ * @since Class available since Release 1.1.0
+ */
+class PHPUnit_Framework_MockObject_Stub_ReturnSelf implements PHPUnit_Framework_MockObject_Stub
+{
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ if (!$invocation instanceof PHPUnit_Framework_MockObject_Invocation_Object) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'The current object can only be returned when mocking an ' .
+ 'object, not a static class.'
+ );
+ }
+
+ return $invocation->object;
+ }
+
+ public function toString()
+ {
+ return 'return the current object';
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Stubs a method by returning a value from a map.
+ *
+ * @since Class available since Release 1.1.0
+ */
+class PHPUnit_Framework_MockObject_Stub_ReturnValueMap implements PHPUnit_Framework_MockObject_Stub
+{
+ protected $valueMap;
+
+ public function __construct(array $valueMap)
+ {
+ $this->valueMap = $valueMap;
+ }
+
+ public function invoke(PHPUnit_Framework_MockObject_Invocation $invocation)
+ {
+ $parameterCount = count($invocation->parameters);
+
+ foreach ($this->valueMap as $map) {
+ if (!is_array($map) || $parameterCount != count($map) - 1) {
+ continue;
+ }
+
+ $return = array_pop($map);
+ if ($invocation->parameters === $map) {
+ return $return;
+ }
+ }
+
+ return;
+ }
+
+ public function toString()
+ {
+ return 'return value from a map';
+ }
+}
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for classes which must verify a given expectation.
+ *
+ * @since Interface available since Release 1.0.0
+ */
+interface PHPUnit_Framework_MockObject_Verifiable
+{
+ /**
+ * Verifies that the current expectation is valid. If everything is OK the
+ * code should just return, if not it must throw an exception.
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function verify();
+}
+code-unit-reverse-lookup
+
+Copyright (c) 2016-2017, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of code-unit-reverse-lookup.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\CodeUnitReverseLookup;
+
+/**
+ * @since Class available since Release 1.0.0
+ */
+class Wizard
+{
+ /**
+ * @var array
+ */
+ private $lookupTable = [];
+
+ /**
+ * @var array
+ */
+ private $processedClasses = [];
+
+ /**
+ * @var array
+ */
+ private $processedFunctions = [];
+
+ /**
+ * @param string $filename
+ * @param int $lineNumber
+ *
+ * @return string
+ */
+ public function lookup($filename, $lineNumber)
+ {
+ if (!isset($this->lookupTable[$filename][$lineNumber])) {
+ $this->updateLookupTable();
+ }
+
+ if (isset($this->lookupTable[$filename][$lineNumber])) {
+ return $this->lookupTable[$filename][$lineNumber];
+ } else {
+ return $filename . ':' . $lineNumber;
+ }
+ }
+
+ private function updateLookupTable()
+ {
+ $this->processClassesAndTraits();
+ $this->processFunctions();
+ }
+
+ private function processClassesAndTraits()
+ {
+ foreach (array_merge(get_declared_classes(), get_declared_traits()) as $classOrTrait) {
+ if (isset($this->processedClasses[$classOrTrait])) {
+ continue;
+ }
+
+ $reflector = new \ReflectionClass($classOrTrait);
+
+ foreach ($reflector->getMethods() as $method) {
+ $this->processFunctionOrMethod($method);
+ }
+
+ $this->processedClasses[$classOrTrait] = true;
+ }
+ }
+
+ private function processFunctions()
+ {
+ foreach (get_defined_functions()['user'] as $function) {
+ if (isset($this->processedFunctions[$function])) {
+ continue;
+ }
+
+ $this->processFunctionOrMethod(new \ReflectionFunction($function));
+
+ $this->processedFunctions[$function] = true;
+ }
+ }
+
+ /**
+ * @param \ReflectionFunctionAbstract $functionOrMethod
+ */
+ private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod)
+ {
+ if ($functionOrMethod->isInternal()) {
+ return;
+ }
+
+ $name = $functionOrMethod->getName();
+
+ if ($functionOrMethod instanceof \ReflectionMethod) {
+ $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name;
+ }
+
+ if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) {
+ $this->lookupTable[$functionOrMethod->getFileName()] = [];
+ }
+
+ foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) {
+ $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name;
+ }
+ }
+}
+Comparator
+
+Copyright (c) 2002-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares arrays for equality.
+ */
+class ArrayComparator extends Comparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return is_array($expected) && is_array($actual);
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ * @param array $processed List of already processed elements (used to prevent infinite recursion)
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
+ {
+ if ($canonicalize) {
+ sort($expected);
+ sort($actual);
+ }
+
+ $remaining = $actual;
+ $expString = $actString = "Array (\n";
+ $equal = true;
+
+ foreach ($expected as $key => $value) {
+ unset($remaining[$key]);
+
+ if (!array_key_exists($key, $actual)) {
+ $expString .= sprintf(
+ " %s => %s\n",
+ $this->exporter->export($key),
+ $this->exporter->shortenedExport($value)
+ );
+
+ $equal = false;
+
+ continue;
+ }
+
+ try {
+ $comparator = $this->factory->getComparatorFor($value, $actual[$key]);
+ $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed);
+
+ $expString .= sprintf(
+ " %s => %s\n",
+ $this->exporter->export($key),
+ $this->exporter->shortenedExport($value)
+ );
+ $actString .= sprintf(
+ " %s => %s\n",
+ $this->exporter->export($key),
+ $this->exporter->shortenedExport($actual[$key])
+ );
+ } catch (ComparisonFailure $e) {
+ $expString .= sprintf(
+ " %s => %s\n",
+ $this->exporter->export($key),
+ $e->getExpectedAsString()
+ ? $this->indent($e->getExpectedAsString())
+ : $this->exporter->shortenedExport($e->getExpected())
+ );
+
+ $actString .= sprintf(
+ " %s => %s\n",
+ $this->exporter->export($key),
+ $e->getActualAsString()
+ ? $this->indent($e->getActualAsString())
+ : $this->exporter->shortenedExport($e->getActual())
+ );
+
+ $equal = false;
+ }
+ }
+
+ foreach ($remaining as $key => $value) {
+ $actString .= sprintf(
+ " %s => %s\n",
+ $this->exporter->export($key),
+ $this->exporter->shortenedExport($value)
+ );
+
+ $equal = false;
+ }
+
+ $expString .= ')';
+ $actString .= ')';
+
+ if (!$equal) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $expString,
+ $actString,
+ false,
+ 'Failed asserting that two arrays are equal.'
+ );
+ }
+ }
+
+ protected function indent($lines)
+ {
+ return trim(str_replace("\n", "\n ", $lines));
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+use SebastianBergmann\Exporter\Exporter;
+
+/**
+ * Abstract base class for comparators which compare values for equality.
+ */
+abstract class Comparator
+{
+ /**
+ * @var Factory
+ */
+ protected $factory;
+
+ /**
+ * @var Exporter
+ */
+ protected $exporter;
+
+ public function __construct()
+ {
+ $this->exporter = new Exporter;
+ }
+
+ /**
+ * @param Factory $factory
+ */
+ public function setFactory(Factory $factory)
+ {
+ $this->factory = $factory;
+ }
+
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ abstract public function accepts($expected, $actual);
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ *
+ * @throws ComparisonFailure
+ */
+ abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false);
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+use SebastianBergmann\Diff\Differ;
+
+/**
+ * Thrown when an assertion for string equality failed.
+ */
+class ComparisonFailure extends \RuntimeException
+{
+ /**
+ * Expected value of the retrieval which does not match $actual.
+ * @var mixed
+ */
+ protected $expected;
+
+ /**
+ * Actually retrieved value which does not match $expected.
+ * @var mixed
+ */
+ protected $actual;
+
+ /**
+ * The string representation of the expected value
+ * @var string
+ */
+ protected $expectedAsString;
+
+ /**
+ * The string representation of the actual value
+ * @var string
+ */
+ protected $actualAsString;
+
+ /**
+ * @var bool
+ */
+ protected $identical;
+
+ /**
+ * Optional message which is placed in front of the first line
+ * returned by toString().
+ * @var string
+ */
+ protected $message;
+
+ /**
+ * Initialises with the expected value and the actual value.
+ *
+ * @param mixed $expected Expected value retrieved.
+ * @param mixed $actual Actual value retrieved.
+ * @param string $expectedAsString
+ * @param string $actualAsString
+ * @param bool $identical
+ * @param string $message A string which is prefixed on all returned lines
+ * in the difference output.
+ */
+ public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '')
+ {
+ $this->expected = $expected;
+ $this->actual = $actual;
+ $this->expectedAsString = $expectedAsString;
+ $this->actualAsString = $actualAsString;
+ $this->message = $message;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getActual()
+ {
+ return $this->actual;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getExpected()
+ {
+ return $this->expected;
+ }
+
+ /**
+ * @return string
+ */
+ public function getActualAsString()
+ {
+ return $this->actualAsString;
+ }
+
+ /**
+ * @return string
+ */
+ public function getExpectedAsString()
+ {
+ return $this->expectedAsString;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDiff()
+ {
+ if (!$this->actualAsString && !$this->expectedAsString) {
+ return '';
+ }
+
+ $differ = new Differ("\n--- Expected\n+++ Actual\n");
+
+ return $differ->diff($this->expectedAsString, $this->actualAsString);
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->message . $this->getDiff();
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+use DOMDocument;
+use DOMNode;
+
+/**
+ * Compares DOMNode instances for equality.
+ */
+class DOMNodeComparator extends ObjectComparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return $expected instanceof DOMNode && $actual instanceof DOMNode;
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ * @param array $processed List of already processed elements (used to prevent infinite recursion)
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
+ {
+ $expectedAsString = $this->nodeToText($expected, true, $ignoreCase);
+ $actualAsString = $this->nodeToText($actual, true, $ignoreCase);
+
+ if ($expectedAsString !== $actualAsString) {
+ if ($expected instanceof DOMDocument) {
+ $type = 'documents';
+ } else {
+ $type = 'nodes';
+ }
+
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $expectedAsString,
+ $actualAsString,
+ false,
+ sprintf("Failed asserting that two DOM %s are equal.\n", $type)
+ );
+ }
+ }
+
+ /**
+ * Returns the normalized, whitespace-cleaned, and indented textual
+ * representation of a DOMNode.
+ *
+ * @param DOMNode $node
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ * @return string
+ */
+ private function nodeToText(DOMNode $node, $canonicalize, $ignoreCase)
+ {
+ if ($canonicalize) {
+ $document = new DOMDocument;
+ $document->loadXML($node->C14N());
+
+ $node = $document;
+ }
+
+ if ($node instanceof DOMDocument) {
+ $document = $node;
+ } else {
+ $document = $node->ownerDocument;
+ }
+
+ $document->formatOutput = true;
+ $document->normalizeDocument();
+
+ if ($node instanceof DOMDocument) {
+ $text = $node->saveXML();
+ } else {
+ $text = $document->saveXML($node);
+ }
+
+ if ($ignoreCase) {
+ $text = strtolower($text);
+ }
+
+ return $text;
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares DateTimeInterface instances for equality.
+ */
+class DateTimeComparator extends ObjectComparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) &&
+ ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface);
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ * @param array $processed List of already processed elements (used to prevent infinite recursion)
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
+ {
+ $delta = new \DateInterval(sprintf('PT%sS', abs($delta)));
+
+ $expectedLower = clone $expected;
+ $expectedUpper = clone $expected;
+
+ if ($actual < $expectedLower->sub($delta) ||
+ $actual > $expectedUpper->add($delta)) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->dateTimeToString($expected),
+ $this->dateTimeToString($actual),
+ false,
+ 'Failed asserting that two DateTime objects are equal.'
+ );
+ }
+ }
+
+ /**
+ * Returns an ISO 8601 formatted string representation of a datetime or
+ * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly
+ * initialized.
+ *
+ * @param \DateTimeInterface $datetime
+ * @return string
+ */
+ private function dateTimeToString($datetime)
+ {
+ $string = $datetime->format('Y-m-d\TH:i:s.uO');
+
+ return $string ? $string : 'Invalid DateTimeInterface object';
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares doubles for equality.
+ */
+class DoubleComparator extends NumericComparator
+{
+ /**
+ * Smallest value available in PHP.
+ *
+ * @var float
+ */
+ const EPSILON = 0.0000000001;
+
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return (is_double($expected) || is_double($actual)) && is_numeric($expected) && is_numeric($actual);
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ if ($delta == 0) {
+ $delta = self::EPSILON;
+ }
+
+ parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase);
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares Exception instances for equality.
+ */
+class ExceptionComparator extends ObjectComparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return $expected instanceof \Exception && $actual instanceof \Exception;
+ }
+
+ /**
+ * Converts an object to an array containing all of its private, protected
+ * and public properties.
+ *
+ * @param object $object
+ * @return array
+ */
+ protected function toArray($object)
+ {
+ $array = parent::toArray($object);
+
+ unset(
+ $array['file'],
+ $array['line'],
+ $array['trace'],
+ $array['string'],
+ $array['xdebug_message']
+ );
+
+ return $array;
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Factory for comparators which compare values for equality.
+ */
+class Factory
+{
+ /**
+ * @var Comparator[]
+ */
+ private $comparators = array();
+
+ /**
+ * @var Factory
+ */
+ private static $instance;
+
+ /**
+ * Constructs a new factory.
+ */
+ public function __construct()
+ {
+ $this->register(new TypeComparator);
+ $this->register(new ScalarComparator);
+ $this->register(new NumericComparator);
+ $this->register(new DoubleComparator);
+ $this->register(new ArrayComparator);
+ $this->register(new ResourceComparator);
+ $this->register(new ObjectComparator);
+ $this->register(new ExceptionComparator);
+ $this->register(new SplObjectStorageComparator);
+ $this->register(new DOMNodeComparator);
+ $this->register(new MockObjectComparator);
+ $this->register(new DateTimeComparator);
+ }
+
+ /**
+ * @return Factory
+ */
+ public static function getInstance()
+ {
+ if (self::$instance === null) {
+ self::$instance = new self;
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Returns the correct comparator for comparing two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return Comparator
+ */
+ public function getComparatorFor($expected, $actual)
+ {
+ foreach ($this->comparators as $comparator) {
+ if ($comparator->accepts($expected, $actual)) {
+ return $comparator;
+ }
+ }
+ }
+
+ /**
+ * Registers a new comparator.
+ *
+ * This comparator will be returned by getInstance() if its accept() method
+ * returns TRUE for the compared values. It has higher priority than the
+ * existing comparators, meaning that its accept() method will be tested
+ * before those of the other comparators.
+ *
+ * @param Comparator $comparator The registered comparator
+ */
+ public function register(Comparator $comparator)
+ {
+ array_unshift($this->comparators, $comparator);
+
+ $comparator->setFactory($this);
+ }
+
+ /**
+ * Unregisters a comparator.
+ *
+ * This comparator will no longer be returned by getInstance().
+ *
+ * @param Comparator $comparator The unregistered comparator
+ */
+ public function unregister(Comparator $comparator)
+ {
+ foreach ($this->comparators as $key => $_comparator) {
+ if ($comparator === $_comparator) {
+ unset($this->comparators[$key]);
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares PHPUnit_Framework_MockObject_MockObject instances for equality.
+ */
+class MockObjectComparator extends ObjectComparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return $expected instanceof \PHPUnit_Framework_MockObject_MockObject && $actual instanceof \PHPUnit_Framework_MockObject_MockObject;
+ }
+
+ /**
+ * Converts an object to an array containing all of its private, protected
+ * and public properties.
+ *
+ * @param object $object
+ * @return array
+ */
+ protected function toArray($object)
+ {
+ $array = parent::toArray($object);
+
+ unset($array['__phpunit_invocationMocker']);
+
+ return $array;
+ }
+}<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares numerical values for equality.
+ */
+class NumericComparator extends ScalarComparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ // all numerical values, but not if one of them is a double
+ // or both of them are strings
+ return is_numeric($expected) && is_numeric($actual) &&
+ !(is_double($expected) || is_double($actual)) &&
+ !(is_string($expected) && is_string($actual));
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ if (is_infinite($actual) && is_infinite($expected)) {
+ return;
+ }
+
+ if ((is_infinite($actual) xor is_infinite($expected)) ||
+ (is_nan($actual) or is_nan($expected)) ||
+ abs($actual - $expected) > $delta) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ '',
+ '',
+ false,
+ sprintf(
+ 'Failed asserting that %s matches expected %s.',
+ $this->exporter->export($actual),
+ $this->exporter->export($expected)
+ )
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares objects for equality.
+ */
+class ObjectComparator extends ArrayComparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return is_object($expected) && is_object($actual);
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ * @param array $processed List of already processed elements (used to prevent infinite recursion)
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
+ {
+ if (get_class($actual) !== get_class($expected)) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->exporter->export($expected),
+ $this->exporter->export($actual),
+ false,
+ sprintf(
+ '%s is not instance of expected class "%s".',
+ $this->exporter->export($actual),
+ get_class($expected)
+ )
+ );
+ }
+
+ // don't compare twice to allow for cyclic dependencies
+ if (in_array(array($actual, $expected), $processed, true) ||
+ in_array(array($expected, $actual), $processed, true)) {
+ return;
+ }
+
+ $processed[] = array($actual, $expected);
+
+ // don't compare objects if they are identical
+ // this helps to avoid the error "maximum function nesting level reached"
+ // CAUTION: this conditional clause is not tested
+ if ($actual !== $expected) {
+ try {
+ parent::assertEquals(
+ $this->toArray($expected),
+ $this->toArray($actual),
+ $delta,
+ $canonicalize,
+ $ignoreCase,
+ $processed
+ );
+ } catch (ComparisonFailure $e) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ // replace "Array" with "MyClass object"
+ substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5),
+ substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5),
+ false,
+ 'Failed asserting that two objects are equal.'
+ );
+ }
+ }
+ }
+
+ /**
+ * Converts an object to an array containing all of its private, protected
+ * and public properties.
+ *
+ * @param object $object
+ * @return array
+ */
+ protected function toArray($object)
+ {
+ return $this->exporter->toArray($object);
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares resources for equality.
+ */
+class ResourceComparator extends Comparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return is_resource($expected) && is_resource($actual);
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ if ($actual != $expected) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->exporter->export($expected),
+ $this->exporter->export($actual)
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares scalar or NULL values for equality.
+ */
+class ScalarComparator extends Comparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ * @since Method available since Release 3.6.0
+ */
+ public function accepts($expected, $actual)
+ {
+ return ((is_scalar($expected) xor null === $expected) &&
+ (is_scalar($actual) xor null === $actual))
+ // allow comparison between strings and objects featuring __toString()
+ || (is_string($expected) && is_object($actual) && method_exists($actual, '__toString'))
+ || (is_object($expected) && method_exists($expected, '__toString') && is_string($actual));
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ $expectedToCompare = $expected;
+ $actualToCompare = $actual;
+
+ // always compare as strings to avoid strange behaviour
+ // otherwise 0 == 'Foobar'
+ if (is_string($expected) || is_string($actual)) {
+ $expectedToCompare = (string) $expectedToCompare;
+ $actualToCompare = (string) $actualToCompare;
+
+ if ($ignoreCase) {
+ $expectedToCompare = strtolower($expectedToCompare);
+ $actualToCompare = strtolower($actualToCompare);
+ }
+ }
+
+ if ($expectedToCompare != $actualToCompare) {
+ if (is_string($expected) && is_string($actual)) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->exporter->export($expected),
+ $this->exporter->export($actual),
+ false,
+ 'Failed asserting that two strings are equal.'
+ );
+ }
+
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ // no diff is required
+ '',
+ '',
+ false,
+ sprintf(
+ 'Failed asserting that %s matches expected %s.',
+ $this->exporter->export($actual),
+ $this->exporter->export($expected)
+ )
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares \SplObjectStorage instances for equality.
+ */
+class SplObjectStorageComparator extends Comparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage;
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ foreach ($actual as $object) {
+ if (!$expected->contains($object)) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->exporter->export($expected),
+ $this->exporter->export($actual),
+ false,
+ 'Failed asserting that two objects are equal.'
+ );
+ }
+ }
+
+ foreach ($expected as $object) {
+ if (!$actual->contains($object)) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->exporter->export($expected),
+ $this->exporter->export($actual),
+ false,
+ 'Failed asserting that two objects are equal.'
+ );
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the Comparator package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Comparator;
+
+/**
+ * Compares values for type equality.
+ */
+class TypeComparator extends Comparator
+{
+ /**
+ * Returns whether the comparator can compare two values.
+ *
+ * @param mixed $expected The first value to compare
+ * @param mixed $actual The second value to compare
+ * @return bool
+ */
+ public function accepts($expected, $actual)
+ {
+ return true;
+ }
+
+ /**
+ * Asserts that two values are equal.
+ *
+ * @param mixed $expected First value to compare
+ * @param mixed $actual Second value to compare
+ * @param float $delta Allowed numerical distance between two values to consider them equal
+ * @param bool $canonicalize Arrays are sorted before comparison when set to true
+ * @param bool $ignoreCase Case is ignored when set to true
+ *
+ * @throws ComparisonFailure
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ if (gettype($expected) != gettype($actual)) {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ // we don't need a diff
+ '',
+ '',
+ false,
+ sprintf(
+ '%s does not match expected type "%s".',
+ $this->exporter->shortenedExport($actual),
+ gettype($expected)
+ )
+ );
+ }
+ }
+}
+sebastian/diff
+
+Copyright (c) 2002-2017, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff;
+
+class Chunk
+{
+ /**
+ * @var int
+ */
+ private $start;
+
+ /**
+ * @var int
+ */
+ private $startRange;
+
+ /**
+ * @var int
+ */
+ private $end;
+
+ /**
+ * @var int
+ */
+ private $endRange;
+
+ /**
+ * @var array
+ */
+ private $lines;
+
+ /**
+ * @param int $start
+ * @param int $startRange
+ * @param int $end
+ * @param int $endRange
+ * @param array $lines
+ */
+ public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = array())
+ {
+ $this->start = (int) $start;
+ $this->startRange = (int) $startRange;
+ $this->end = (int) $end;
+ $this->endRange = (int) $endRange;
+ $this->lines = $lines;
+ }
+
+ /**
+ * @return int
+ */
+ public function getStart()
+ {
+ return $this->start;
+ }
+
+ /**
+ * @return int
+ */
+ public function getStartRange()
+ {
+ return $this->startRange;
+ }
+
+ /**
+ * @return int
+ */
+ public function getEnd()
+ {
+ return $this->end;
+ }
+
+ /**
+ * @return int
+ */
+ public function getEndRange()
+ {
+ return $this->endRange;
+ }
+
+ /**
+ * @return array
+ */
+ public function getLines()
+ {
+ return $this->lines;
+ }
+
+ /**
+ * @param array $lines
+ */
+ public function setLines(array $lines)
+ {
+ $this->lines = $lines;
+ }
+}
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff;
+
+class Diff
+{
+ /**
+ * @var string
+ */
+ private $from;
+
+ /**
+ * @var string
+ */
+ private $to;
+
+ /**
+ * @var Chunk[]
+ */
+ private $chunks;
+
+ /**
+ * @param string $from
+ * @param string $to
+ * @param Chunk[] $chunks
+ */
+ public function __construct($from, $to, array $chunks = array())
+ {
+ $this->from = $from;
+ $this->to = $to;
+ $this->chunks = $chunks;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFrom()
+ {
+ return $this->from;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTo()
+ {
+ return $this->to;
+ }
+
+ /**
+ * @return Chunk[]
+ */
+ public function getChunks()
+ {
+ return $this->chunks;
+ }
+
+ /**
+ * @param Chunk[] $chunks
+ */
+ public function setChunks(array $chunks)
+ {
+ $this->chunks = $chunks;
+ }
+}
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff;
+
+use SebastianBergmann\Diff\LCS\LongestCommonSubsequence;
+use SebastianBergmann\Diff\LCS\TimeEfficientImplementation;
+use SebastianBergmann\Diff\LCS\MemoryEfficientImplementation;
+
+/**
+ * Diff implementation.
+ */
+class Differ
+{
+ /**
+ * @var string
+ */
+ private $header;
+
+ /**
+ * @var bool
+ */
+ private $showNonDiffLines;
+
+ /**
+ * @param string $header
+ * @param bool $showNonDiffLines
+ */
+ public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true)
+ {
+ $this->header = $header;
+ $this->showNonDiffLines = $showNonDiffLines;
+ }
+
+ /**
+ * Returns the diff between two arrays or strings as string.
+ *
+ * @param array|string $from
+ * @param array|string $to
+ * @param LongestCommonSubsequence $lcs
+ *
+ * @return string
+ */
+ public function diff($from, $to, LongestCommonSubsequence $lcs = null)
+ {
+ $from = $this->validateDiffInput($from);
+ $to = $this->validateDiffInput($to);
+ $diff = $this->diffToArray($from, $to, $lcs);
+ $old = $this->checkIfDiffInOld($diff);
+ $start = isset($old[0]) ? $old[0] : 0;
+ $end = \count($diff);
+
+ if ($tmp = \array_search($end, $old)) {
+ $end = $tmp;
+ }
+
+ return $this->getBuffer($diff, $old, $start, $end);
+ }
+
+ /**
+ * Casts variable to string if it is not a string or array.
+ *
+ * @param mixed $input
+ *
+ * @return string
+ */
+ private function validateDiffInput($input)
+ {
+ if (!\is_array($input) && !\is_string($input)) {
+ return (string) $input;
+ }
+
+ return $input;
+ }
+
+ /**
+ * Takes input of the diff array and returns the old array.
+ * Iterates through diff line by line,
+ *
+ * @param array $diff
+ *
+ * @return array
+ */
+ private function checkIfDiffInOld(array $diff)
+ {
+ $inOld = false;
+ $i = 0;
+ $old = array();
+
+ foreach ($diff as $line) {
+ if ($line[1] === 0 /* OLD */) {
+ if ($inOld === false) {
+ $inOld = $i;
+ }
+ } elseif ($inOld !== false) {
+ if (($i - $inOld) > 5) {
+ $old[$inOld] = $i - 1;
+ }
+
+ $inOld = false;
+ }
+
+ ++$i;
+ }
+
+ return $old;
+ }
+
+ /**
+ * Generates buffer in string format, returning the patch.
+ *
+ * @param array $diff
+ * @param array $old
+ * @param int $start
+ * @param int $end
+ *
+ * @return string
+ */
+ private function getBuffer(array $diff, array $old, $start, $end)
+ {
+ $buffer = $this->header;
+
+ if (!isset($old[$start])) {
+ $buffer = $this->getDiffBufferElementNew($diff, $buffer, $start);
+ ++$start;
+ }
+
+ for ($i = $start; $i < $end; $i++) {
+ if (isset($old[$i])) {
+ $i = $old[$i];
+ $buffer = $this->getDiffBufferElementNew($diff, $buffer, $i);
+ } else {
+ $buffer = $this->getDiffBufferElement($diff, $buffer, $i);
+ }
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Gets individual buffer element.
+ *
+ * @param array $diff
+ * @param string $buffer
+ * @param int $diffIndex
+ *
+ * @return string
+ */
+ private function getDiffBufferElement(array $diff, $buffer, $diffIndex)
+ {
+ if ($diff[$diffIndex][1] === 1 /* ADDED */) {
+ $buffer .= '+' . $diff[$diffIndex][0] . "\n";
+ } elseif ($diff[$diffIndex][1] === 2 /* REMOVED */) {
+ $buffer .= '-' . $diff[$diffIndex][0] . "\n";
+ } elseif ($this->showNonDiffLines === true) {
+ $buffer .= ' ' . $diff[$diffIndex][0] . "\n";
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Gets individual buffer element with opening.
+ *
+ * @param array $diff
+ * @param string $buffer
+ * @param int $diffIndex
+ *
+ * @return string
+ */
+ private function getDiffBufferElementNew(array $diff, $buffer, $diffIndex)
+ {
+ if ($this->showNonDiffLines === true) {
+ $buffer .= "@@ @@\n";
+ }
+
+ return $this->getDiffBufferElement($diff, $buffer, $diffIndex);
+ }
+
+ /**
+ * Returns the diff between two arrays or strings as array.
+ *
+ * Each array element contains two elements:
+ * - [0] => mixed $token
+ * - [1] => 2|1|0
+ *
+ * - 2: REMOVED: $token was removed from $from
+ * - 1: ADDED: $token was added to $from
+ * - 0: OLD: $token is not changed in $to
+ *
+ * @param array|string $from
+ * @param array|string $to
+ * @param LongestCommonSubsequence $lcs
+ *
+ * @return array
+ */
+ public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null)
+ {
+ if (\is_string($from)) {
+ $fromMatches = $this->getNewLineMatches($from);
+ $from = $this->splitStringByLines($from);
+ } elseif (\is_array($from)) {
+ $fromMatches = array();
+ } else {
+ throw new \InvalidArgumentException('"from" must be an array or string.');
+ }
+
+ if (\is_string($to)) {
+ $toMatches = $this->getNewLineMatches($to);
+ $to = $this->splitStringByLines($to);
+ } elseif (\is_array($to)) {
+ $toMatches = array();
+ } else {
+ throw new \InvalidArgumentException('"to" must be an array or string.');
+ }
+
+ list($from, $to, $start, $end) = self::getArrayDiffParted($from, $to);
+
+ if ($lcs === null) {
+ $lcs = $this->selectLcsImplementation($from, $to);
+ }
+
+ $common = $lcs->calculate(\array_values($from), \array_values($to));
+ $diff = array();
+
+ if ($this->detectUnmatchedLineEndings($fromMatches, $toMatches)) {
+ $diff[] = array(
+ '#Warning: Strings contain different line endings!',
+ 0
+ );
+ }
+
+ foreach ($start as $token) {
+ $diff[] = array($token, 0 /* OLD */);
+ }
+
+ \reset($from);
+ \reset($to);
+
+ foreach ($common as $token) {
+ while (($fromToken = \reset($from)) !== $token) {
+ $diff[] = array(\array_shift($from), 2 /* REMOVED */);
+ }
+
+ while (($toToken = \reset($to)) !== $token) {
+ $diff[] = array(\array_shift($to), 1 /* ADDED */);
+ }
+
+ $diff[] = array($token, 0 /* OLD */);
+
+ \array_shift($from);
+ \array_shift($to);
+ }
+
+ while (($token = \array_shift($from)) !== null) {
+ $diff[] = array($token, 2 /* REMOVED */);
+ }
+
+ while (($token = \array_shift($to)) !== null) {
+ $diff[] = array($token, 1 /* ADDED */);
+ }
+
+ foreach ($end as $token) {
+ $diff[] = array($token, 0 /* OLD */);
+ }
+
+ return $diff;
+ }
+
+ /**
+ * Get new strings denoting new lines from a given string.
+ *
+ * @param string $string
+ *
+ * @return array
+ */
+ private function getNewLineMatches($string)
+ {
+ \preg_match_all('(\r\n|\r|\n)', $string, $stringMatches);
+
+ return $stringMatches;
+ }
+
+ /**
+ * Checks if input is string, if so it will split it line-by-line.
+ *
+ * @param string $input
+ *
+ * @return array
+ */
+ private function splitStringByLines($input)
+ {
+ return \preg_split('(\r\n|\r|\n)', $input);
+ }
+
+ /**
+ * @param array $from
+ * @param array $to
+ *
+ * @return LongestCommonSubsequence
+ */
+ private function selectLcsImplementation(array $from, array $to)
+ {
+ // We do not want to use the time-efficient implementation if its memory
+ // footprint will probably exceed this value. Note that the footprint
+ // calculation is only an estimation for the matrix and the LCS method
+ // will typically allocate a bit more memory than this.
+ $memoryLimit = 100 * 1024 * 1024;
+
+ if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) {
+ return new MemoryEfficientImplementation;
+ }
+
+ return new TimeEfficientImplementation;
+ }
+
+ /**
+ * Calculates the estimated memory footprint for the DP-based method.
+ *
+ * @param array $from
+ * @param array $to
+ *
+ * @return int|float
+ */
+ private function calculateEstimatedFootprint(array $from, array $to)
+ {
+ $itemSize = PHP_INT_SIZE === 4 ? 76 : 144;
+
+ return $itemSize * \pow(\min(\count($from), \count($to)), 2);
+ }
+
+ /**
+ * Returns true if line ends don't match on fromMatches and toMatches.
+ *
+ * @param array $fromMatches
+ * @param array $toMatches
+ *
+ * @return bool
+ */
+ private function detectUnmatchedLineEndings(array $fromMatches, array $toMatches)
+ {
+ return isset($fromMatches[0], $toMatches[0]) &&
+ \count($fromMatches[0]) === \count($toMatches[0]) &&
+ $fromMatches[0] !== $toMatches[0];
+ }
+
+ /**
+ * @param array $from
+ * @param array $to
+ *
+ * @return array
+ */
+ private static function getArrayDiffParted(array &$from, array &$to)
+ {
+ $start = array();
+ $end = array();
+
+ \reset($to);
+
+ foreach ($from as $k => $v) {
+ $toK = \key($to);
+
+ if ($toK === $k && $v === $to[$k]) {
+ $start[$k] = $v;
+
+ unset($from[$k], $to[$k]);
+ } else {
+ break;
+ }
+ }
+
+ \end($from);
+ \end($to);
+
+ do {
+ $fromK = \key($from);
+ $toK = \key($to);
+
+ if (null === $fromK || null === $toK || \current($from) !== \current($to)) {
+ break;
+ }
+
+ \prev($from);
+ \prev($to);
+
+ $end = array($fromK => $from[$fromK]) + $end;
+ unset($from[$fromK], $to[$toK]);
+ } while (true);
+
+ return array($from, $to, $start, $end);
+ }
+}
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff\LCS;
+
+/**
+ * Interface for implementations of longest common subsequence calculation.
+ */
+interface LongestCommonSubsequence
+{
+ /**
+ * Calculates the longest common subsequence of two arrays.
+ *
+ * @param array $from
+ * @param array $to
+ *
+ * @return array
+ */
+ public function calculate(array $from, array $to);
+}
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff\LCS;
+
+/**
+ * Memory-efficient implementation of longest common subsequence calculation.
+ */
+class MemoryEfficientImplementation implements LongestCommonSubsequence
+{
+ /**
+ * Calculates the longest common subsequence of two arrays.
+ *
+ * @param array $from
+ * @param array $to
+ *
+ * @return array
+ */
+ public function calculate(array $from, array $to)
+ {
+ $cFrom = \count($from);
+ $cTo = \count($to);
+
+ if ($cFrom === 0) {
+ return array();
+ }
+
+ if ($cFrom === 1) {
+ if (\in_array($from[0], $to, true)) {
+ return array($from[0]);
+ }
+
+ return array();
+ }
+
+ $i = (int) ($cFrom / 2);
+ $fromStart = \array_slice($from, 0, $i);
+ $fromEnd = \array_slice($from, $i);
+ $llB = $this->length($fromStart, $to);
+ $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to));
+ $jMax = 0;
+ $max = 0;
+
+ for ($j = 0; $j <= $cTo; $j++) {
+ $m = $llB[$j] + $llE[$cTo - $j];
+
+ if ($m >= $max) {
+ $max = $m;
+ $jMax = $j;
+ }
+ }
+
+ $toStart = \array_slice($to, 0, $jMax);
+ $toEnd = \array_slice($to, $jMax);
+
+ return \array_merge(
+ $this->calculate($fromStart, $toStart),
+ $this->calculate($fromEnd, $toEnd)
+ );
+ }
+
+ /**
+ * @param array $from
+ * @param array $to
+ *
+ * @return array
+ */
+ private function length(array $from, array $to)
+ {
+ $current = \array_fill(0, \count($to) + 1, 0);
+ $cFrom = \count($from);
+ $cTo = \count($to);
+
+ for ($i = 0; $i < $cFrom; $i++) {
+ $prev = $current;
+
+ for ($j = 0; $j < $cTo; $j++) {
+ if ($from[$i] === $to[$j]) {
+ $current[$j + 1] = $prev[$j] + 1;
+ } else {
+ $current[$j + 1] = \max($current[$j], $prev[$j + 1]);
+ }
+ }
+ }
+
+ return $current;
+ }
+}
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff\LCS;
+
+/**
+ * Time-efficient implementation of longest common subsequence calculation.
+ */
+class TimeEfficientImplementation implements LongestCommonSubsequence
+{
+ /**
+ * Calculates the longest common subsequence of two arrays.
+ *
+ * @param array $from
+ * @param array $to
+ *
+ * @return array
+ */
+ public function calculate(array $from, array $to)
+ {
+ $common = array();
+ $fromLength = \count($from);
+ $toLength = \count($to);
+ $width = $fromLength + 1;
+ $matrix = new \SplFixedArray($width * ($toLength + 1));
+
+ for ($i = 0; $i <= $fromLength; ++$i) {
+ $matrix[$i] = 0;
+ }
+
+ for ($j = 0; $j <= $toLength; ++$j) {
+ $matrix[$j * $width] = 0;
+ }
+
+ for ($i = 1; $i <= $fromLength; ++$i) {
+ for ($j = 1; $j <= $toLength; ++$j) {
+ $o = ($j * $width) + $i;
+ $matrix[$o] = \max(
+ $matrix[$o - 1],
+ $matrix[$o - $width],
+ $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0
+ );
+ }
+ }
+
+ $i = $fromLength;
+ $j = $toLength;
+
+ while ($i > 0 && $j > 0) {
+ if ($from[$i - 1] === $to[$j - 1]) {
+ $common[] = $from[$i - 1];
+ --$i;
+ --$j;
+ } else {
+ $o = ($j * $width) + $i;
+
+ if ($matrix[$o - $width] > $matrix[$o - 1]) {
+ --$j;
+ } else {
+ --$i;
+ }
+ }
+ }
+
+ return \array_reverse($common);
+ }
+}
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff;
+
+class Line
+{
+ const ADDED = 1;
+ const REMOVED = 2;
+ const UNCHANGED = 3;
+
+ /**
+ * @var int
+ */
+ private $type;
+
+ /**
+ * @var string
+ */
+ private $content;
+
+ /**
+ * @param int $type
+ * @param string $content
+ */
+ public function __construct($type = self::UNCHANGED, $content = '')
+ {
+ $this->type = $type;
+ $this->content = $content;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * @return int
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+}
+<?php
+/*
+ * This file is part of sebastian/diff.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Diff;
+
+/**
+ * Unified diff parser.
+ */
+class Parser
+{
+ /**
+ * @param string $string
+ *
+ * @return Diff[]
+ */
+ public function parse($string)
+ {
+ $lines = \preg_split('(\r\n|\r|\n)', $string);
+
+ if (!empty($lines) && $lines[\count($lines) - 1] == '') {
+ \array_pop($lines);
+ }
+
+ $lineCount = \count($lines);
+ $diffs = array();
+ $diff = null;
+ $collected = array();
+
+ for ($i = 0; $i < $lineCount; ++$i) {
+ if (\preg_match('(^---\\s+(?P<file>\\S+))', $lines[$i], $fromMatch) &&
+ \preg_match('(^\\+\\+\\+\\s+(?P<file>\\S+))', $lines[$i + 1], $toMatch)) {
+ if ($diff !== null) {
+ $this->parseFileDiff($diff, $collected);
+
+ $diffs[] = $diff;
+ $collected = array();
+ }
+
+ $diff = new Diff($fromMatch['file'], $toMatch['file']);
+
+ ++$i;
+ } else {
+ if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) {
+ continue;
+ }
+
+ $collected[] = $lines[$i];
+ }
+ }
+
+ if ($diff !== null && \count($collected)) {
+ $this->parseFileDiff($diff, $collected);
+
+ $diffs[] = $diff;
+ }
+
+ return $diffs;
+ }
+
+ /**
+ * @param Diff $diff
+ * @param array $lines
+ */
+ private function parseFileDiff(Diff $diff, array $lines)
+ {
+ $chunks = array();
+ $chunk = null;
+
+ foreach ($lines as $line) {
+ if (\preg_match('/^@@\s+-(?P<start>\d+)(?:,\s*(?P<startrange>\d+))?\s+\+(?P<end>\d+)(?:,\s*(?P<endrange>\d+))?\s+@@/', $line, $match)) {
+ $chunk = new Chunk(
+ $match['start'],
+ isset($match['startrange']) ? \max(1, $match['startrange']) : 1,
+ $match['end'],
+ isset($match['endrange']) ? \max(1, $match['endrange']) : 1
+ );
+
+ $chunks[] = $chunk;
+ $diffLines = array();
+
+ continue;
+ }
+
+ if (\preg_match('/^(?P<type>[+ -])?(?P<line>.*)/', $line, $match)) {
+ $type = Line::UNCHANGED;
+
+ if ($match['type'] === '+') {
+ $type = Line::ADDED;
+ } elseif ($match['type'] === '-') {
+ $type = Line::REMOVED;
+ }
+
+ $diffLines[] = new Line($type, $match['line']);
+
+ if (null !== $chunk) {
+ $chunk->setLines($diffLines);
+ }
+ }
+ }
+
+ $diff->setChunks($chunks);
+ }
+}
+Environment
+
+Copyright (c) 2014-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the Environment package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Environment;
+
+/**
+ */
+class Console
+{
+ const STDIN = 0;
+ const STDOUT = 1;
+ const STDERR = 2;
+
+ /**
+ * Returns true if STDOUT supports colorization.
+ *
+ * This code has been copied and adapted from
+ * Symfony\Component\Console\Output\OutputStream.
+ *
+ * @return bool
+ */
+ public function hasColorSupport()
+ {
+ if (DIRECTORY_SEPARATOR == '\\') {
+ return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM');
+ }
+
+ if (!defined('STDOUT')) {
+ return false;
+ }
+
+ return $this->isInteractive(STDOUT);
+ }
+
+ /**
+ * Returns the number of columns of the terminal.
+ *
+ * @return int
+ */
+ public function getNumberOfColumns()
+ {
+ if (DIRECTORY_SEPARATOR == '\\') {
+ $columns = 80;
+
+ if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
+ $columns = $matches[1];
+ } elseif (function_exists('proc_open')) {
+ $process = proc_open(
+ 'mode CON',
+ [
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', 'w']
+ ],
+ $pipes,
+ null,
+ null,
+ ['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)) {
+ $columns = $matches[2];
+ }
+ }
+ }
+
+ return $columns - 1;
+ }
+
+ if (!$this->isInteractive(self::STDIN)) {
+ return 80;
+ }
+
+ if (function_exists('shell_exec') && preg_match('#\d+ (\d+)#', shell_exec('stty size'), $match) === 1) {
+ if ((int) $match[1] > 0) {
+ return (int) $match[1];
+ }
+ }
+
+ if (function_exists('shell_exec') && preg_match('#columns = (\d+);#', shell_exec('stty'), $match) === 1) {
+ if ((int) $match[1] > 0) {
+ return (int) $match[1];
+ }
+ }
+
+ return 80;
+ }
+
+ /**
+ * Returns if the file descriptor is an interactive terminal or not.
+ *
+ * @param int|resource $fileDescriptor
+ *
+ * @return bool
+ */
+ public function isInteractive($fileDescriptor = self::STDOUT)
+ {
+ return function_exists('posix_isatty') && @posix_isatty($fileDescriptor);
+ }
+}
+<?php
+/*
+ * This file is part of the Environment package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Environment;
+
+/**
+ * Utility class for HHVM/PHP environment handling.
+ */
+class Runtime
+{
+ /**
+ * @var string
+ */
+ private static $binary;
+
+ /**
+ * Returns true when Xdebug is supported or
+ * the runtime used is PHPDBG (PHP >= 7.0).
+ *
+ * @return bool
+ */
+ public function canCollectCodeCoverage()
+ {
+ return $this->hasXdebug() || $this->hasPHPDBGCodeCoverage();
+ }
+
+ /**
+ * Returns the path to the binary of the current runtime.
+ * Appends ' --php' to the path when the runtime is HHVM.
+ *
+ * @return string
+ */
+ public function getBinary()
+ {
+ // HHVM
+ if (self::$binary === null && $this->isHHVM()) {
+ if ((self::$binary = getenv('PHP_BINARY')) === false) {
+ self::$binary = PHP_BINARY;
+ }
+
+ self::$binary = escapeshellarg(self::$binary) . ' --php';
+ }
+
+ // PHP >= 5.4.0
+ if (self::$binary === null && defined('PHP_BINARY')) {
+ if (PHP_BINARY !== '') {
+ self::$binary = escapeshellarg(PHP_BINARY);
+ }
+ }
+
+ // PHP < 5.4.0
+ if (self::$binary === null) {
+ if (PHP_SAPI == 'cli' && isset($_SERVER['_'])) {
+ if (strpos($_SERVER['_'], 'phpunit') !== false) {
+ $file = file($_SERVER['_']);
+
+ if (strpos($file[0], ' ') !== false) {
+ $tmp = explode(' ', $file[0]);
+ self::$binary = escapeshellarg(trim($tmp[1]));
+ } else {
+ self::$binary = escapeshellarg(ltrim(trim($file[0]), '#!'));
+ }
+ } elseif (strpos(basename($_SERVER['_']), 'php') !== false) {
+ self::$binary = escapeshellarg($_SERVER['_']);
+ }
+ }
+ }
+
+ if (self::$binary === null) {
+ $possibleBinaryLocations = [
+ PHP_BINDIR . '/php',
+ PHP_BINDIR . '/php-cli.exe',
+ PHP_BINDIR . '/php.exe'
+ ];
+
+ foreach ($possibleBinaryLocations as $binary) {
+ if (is_readable($binary)) {
+ self::$binary = escapeshellarg($binary);
+ break;
+ }
+ }
+ }
+
+ if (self::$binary === null) {
+ self::$binary = 'php';
+ }
+
+ return self::$binary;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNameWithVersion()
+ {
+ return $this->getName() . ' ' . $this->getVersion();
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ if ($this->isHHVM()) {
+ return 'HHVM';
+ } elseif ($this->isPHPDBG()) {
+ return 'PHPDBG';
+ } else {
+ return 'PHP';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getVendorUrl()
+ {
+ if ($this->isHHVM()) {
+ return 'http://hhvm.com/';
+ } else {
+ return 'https://secure.php.net/';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getVersion()
+ {
+ if ($this->isHHVM()) {
+ return HHVM_VERSION;
+ } else {
+ return PHP_VERSION;
+ }
+ }
+
+ /**
+ * Returns true when the runtime used is PHP and Xdebug is loaded.
+ *
+ * @return bool
+ */
+ public function hasXdebug()
+ {
+ return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug');
+ }
+
+ /**
+ * Returns true when the runtime used is HHVM.
+ *
+ * @return bool
+ */
+ public function isHHVM()
+ {
+ return defined('HHVM_VERSION');
+ }
+
+ /**
+ * Returns true when the runtime used is PHP without the PHPDBG SAPI.
+ *
+ * @return bool
+ */
+ public function isPHP()
+ {
+ return !$this->isHHVM() && !$this->isPHPDBG();
+ }
+
+ /**
+ * Returns true when the runtime used is PHP with the PHPDBG SAPI.
+ *
+ * @return bool
+ */
+ public function isPHPDBG()
+ {
+ return PHP_SAPI === 'phpdbg' && !$this->isHHVM();
+ }
+
+ /**
+ * Returns true when the runtime used is PHP with the PHPDBG SAPI
+ * and the phpdbg_*_oplog() functions are available (PHP >= 7.0).
+ *
+ * @return bool
+ */
+ public function hasPHPDBGCodeCoverage()
+ {
+ return $this->isPHPDBG() && function_exists('phpdbg_start_oplog');
+ }
+}
+Exporter
+
+Copyright (c) 2002-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the Exporter package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\Exporter;
+
+use SebastianBergmann\RecursionContext\Context;
+
+/**
+ * A nifty utility for visualizing PHP variables.
+ *
+ * <code>
+ * <?php
+ * use SebastianBergmann\Exporter\Exporter;
+ *
+ * $exporter = new Exporter;
+ * print $exporter->export(new Exception);
+ * </code>
+ */
+class Exporter
+{
+ /**
+ * Exports a value as a string
+ *
+ * The output of this method is similar to the output of print_r(), but
+ * improved in various aspects:
+ *
+ * - NULL is rendered as "null" (instead of "")
+ * - TRUE is rendered as "true" (instead of "1")
+ * - FALSE is rendered as "false" (instead of "")
+ * - Strings are always quoted with single quotes
+ * - Carriage returns and newlines are normalized to \n
+ * - Recursion and repeated rendering is treated properly
+ *
+ * @param mixed $value
+ * @param int $indentation The indentation level of the 2nd+ line
+ * @return string
+ */
+ public function export($value, $indentation = 0)
+ {
+ return $this->recursiveExport($value, $indentation);
+ }
+
+ /**
+ * @param mixed $data
+ * @param Context $context
+ * @return string
+ */
+ public function shortenedRecursiveExport(&$data, Context $context = null)
+ {
+ $result = array();
+ $exporter = new self();
+
+ if (!$context) {
+ $context = new Context;
+ }
+
+ $array = $data;
+ $context->add($data);
+
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ if ($context->contains($data[$key]) !== false) {
+ $result[] = '*RECURSION*';
+ }
+
+ else {
+ $result[] = sprintf(
+ 'array(%s)',
+ $this->shortenedRecursiveExport($data[$key], $context)
+ );
+ }
+ }
+
+ else {
+ $result[] = $exporter->shortenedExport($value);
+ }
+ }
+
+ return implode(', ', $result);
+ }
+
+ /**
+ * Exports a value into a single-line string
+ *
+ * The output of this method is similar to the output of
+ * SebastianBergmann\Exporter\Exporter::export().
+ *
+ * Newlines are replaced by the visible string '\n'.
+ * Contents of arrays and objects (if any) are replaced by '...'.
+ *
+ * @param mixed $value
+ * @return string
+ * @see SebastianBergmann\Exporter\Exporter::export
+ */
+ public function shortenedExport($value)
+ {
+ if (is_string($value)) {
+ $string = $this->export($value);
+
+ if (function_exists('mb_strlen')) {
+ if (mb_strlen($string) > 40) {
+ $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7);
+ }
+ } else {
+ if (strlen($string) > 40) {
+ $string = substr($string, 0, 30) . '...' . substr($string, -7);
+ }
+ }
+
+ return str_replace("\n", '\n', $string);
+ }
+
+ if (is_object($value)) {
+ return sprintf(
+ '%s Object (%s)',
+ get_class($value),
+ count($this->toArray($value)) > 0 ? '...' : ''
+ );
+ }
+
+ if (is_array($value)) {
+ return sprintf(
+ 'Array (%s)',
+ count($value) > 0 ? '...' : ''
+ );
+ }
+
+ return $this->export($value);
+ }
+
+ /**
+ * Converts an object to an array containing all of its private, protected
+ * and public properties.
+ *
+ * @param mixed $value
+ * @return array
+ */
+ public function toArray($value)
+ {
+ if (!is_object($value)) {
+ return (array) $value;
+ }
+
+ $array = array();
+
+ foreach ((array) $value as $key => $val) {
+ // properties are transformed to keys in the following way:
+ // private $property => "\0Classname\0property"
+ // protected $property => "\0*\0property"
+ // public $property => "property"
+ if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) {
+ $key = $matches[1];
+ }
+
+ // See https://github.com/php/php-src/commit/5721132
+ if ($key === "\0gcdata") {
+ continue;
+ }
+
+ $array[$key] = $val;
+ }
+
+ // Some internal classes like SplObjectStorage don't work with the
+ // above (fast) mechanism nor with reflection in Zend.
+ // Format the output similarly to print_r() in this case
+ if ($value instanceof \SplObjectStorage) {
+ // However, the fast method does work in HHVM, and exposes the
+ // internal implementation. Hide it again.
+ if (property_exists('\SplObjectStorage', '__storage')) {
+ unset($array['__storage']);
+ } elseif (property_exists('\SplObjectStorage', 'storage')) {
+ unset($array['storage']);
+ }
+
+ if (property_exists('\SplObjectStorage', '__key')) {
+ unset($array['__key']);
+ }
+
+ foreach ($value as $key => $val) {
+ $array[spl_object_hash($val)] = array(
+ 'obj' => $val,
+ 'inf' => $value->getInfo(),
+ );
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Recursive implementation of export
+ *
+ * @param mixed $value The value to export
+ * @param int $indentation The indentation level of the 2nd+ line
+ * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects
+ * @return string
+ * @see SebastianBergmann\Exporter\Exporter::export
+ */
+ protected function recursiveExport(&$value, $indentation, $processed = null)
+ {
+ if ($value === null) {
+ return 'null';
+ }
+
+ if ($value === true) {
+ return 'true';
+ }
+
+ if ($value === false) {
+ return 'false';
+ }
+
+ if (is_float($value) && floatval(intval($value)) === $value) {
+ return "$value.0";
+ }
+
+ if (is_resource($value)) {
+ return sprintf(
+ 'resource(%d) of type (%s)',
+ $value,
+ get_resource_type($value)
+ );
+ }
+
+ if (is_string($value)) {
+ // Match for most non printable chars somewhat taking multibyte chars into account
+ if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) {
+ return 'Binary String: 0x' . bin2hex($value);
+ }
+
+ return "'" .
+ str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) .
+ "'";
+ }
+
+ $whitespace = str_repeat(' ', 4 * $indentation);
+
+ if (!$processed) {
+ $processed = new Context;
+ }
+
+ if (is_array($value)) {
+ if (($key = $processed->contains($value)) !== false) {
+ return 'Array &' . $key;
+ }
+
+ $array = $value;
+ $key = $processed->add($value);
+ $values = '';
+
+ if (count($array) > 0) {
+ foreach ($array as $k => $v) {
+ $values .= sprintf(
+ '%s %s => %s' . "\n",
+ $whitespace,
+ $this->recursiveExport($k, $indentation),
+ $this->recursiveExport($value[$k], $indentation + 1, $processed)
+ );
+ }
+
+ $values = "\n" . $values . $whitespace;
+ }
+
+ return sprintf('Array &%s (%s)', $key, $values);
+ }
+
+ if (is_object($value)) {
+ $class = get_class($value);
+
+ if ($hash = $processed->contains($value)) {
+ return sprintf('%s Object &%s', $class, $hash);
+ }
+
+ $hash = $processed->add($value);
+ $values = '';
+ $array = $this->toArray($value);
+
+ if (count($array) > 0) {
+ foreach ($array as $k => $v) {
+ $values .= sprintf(
+ '%s %s => %s' . "\n",
+ $whitespace,
+ $this->recursiveExport($k, $indentation),
+ $this->recursiveExport($v, $indentation + 1, $processed)
+ );
+ }
+
+ $values = "\n" . $values . $whitespace;
+ }
+
+ return sprintf('%s Object &%s (%s)', $class, $hash, $values);
+ }
+
+ return var_export($value, true);
+ }
+}
+Recursion Context
+
+Copyright (c) 2002-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the Recursion Context package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\RecursionContext;
+
+/**
+ * A context containing previously processed arrays and objects
+ * when recursively processing a value.
+ */
+final class Context
+{
+ /**
+ * @var array[]
+ */
+ private $arrays;
+
+ /**
+ * @var \SplObjectStorage
+ */
+ private $objects;
+
+ /**
+ * Initialises the context
+ */
+ public function __construct()
+ {
+ $this->arrays = array();
+ $this->objects = new \SplObjectStorage;
+ }
+
+ /**
+ * Adds a value to the context.
+ *
+ * @param array|object $value The value to add.
+ *
+ * @return int|string The ID of the stored value, either as a string or integer.
+ *
+ * @throws InvalidArgumentException Thrown if $value is not an array or object
+ */
+ public function add(&$value)
+ {
+ if (is_array($value)) {
+ return $this->addArray($value);
+ } elseif (is_object($value)) {
+ return $this->addObject($value);
+ }
+
+ throw new InvalidArgumentException(
+ 'Only arrays and objects are supported'
+ );
+ }
+
+ /**
+ * Checks if the given value exists within the context.
+ *
+ * @param array|object $value The value to check.
+ *
+ * @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored.
+ *
+ * @throws InvalidArgumentException Thrown if $value is not an array or object
+ */
+ public function contains(&$value)
+ {
+ if (is_array($value)) {
+ return $this->containsArray($value);
+ } elseif (is_object($value)) {
+ return $this->containsObject($value);
+ }
+
+ throw new InvalidArgumentException(
+ 'Only arrays and objects are supported'
+ );
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return bool|int
+ */
+ private function addArray(array &$array)
+ {
+ $key = $this->containsArray($array);
+
+ if ($key !== false) {
+ return $key;
+ }
+
+ $key = count($this->arrays);
+ $this->arrays[] = &$array;
+
+ if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) {
+ $array[] = $key;
+ $array[] = $this->objects;
+ } else { /* cover the improbable case too */
+ do {
+ $key = random_int(PHP_INT_MIN, PHP_INT_MAX);
+ } while (isset($array[$key]));
+
+ $array[$key] = $key;
+
+ do {
+ $key = random_int(PHP_INT_MIN, PHP_INT_MAX);
+ } while (isset($array[$key]));
+
+ $array[$key] = $this->objects;
+ }
+
+ return $key;
+ }
+
+ /**
+ * @param object $object
+ *
+ * @return string
+ */
+ private function addObject($object)
+ {
+ if (!$this->objects->contains($object)) {
+ $this->objects->attach($object);
+ }
+
+ return spl_object_hash($object);
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return int|false
+ */
+ private function containsArray(array &$array)
+ {
+ $end = array_slice($array, -2);
+
+ return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false;
+ }
+
+ /**
+ * @param object $value
+ *
+ * @return string|false
+ */
+ private function containsObject($value)
+ {
+ if ($this->objects->contains($value)) {
+ return spl_object_hash($value);
+ }
+
+ return false;
+ }
+
+ public function __destruct()
+ {
+ foreach ($this->arrays as &$array) {
+ if (is_array($array)) {
+ array_pop($array);
+ array_pop($array);
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the Recursion Context package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\RecursionContext;
+
+/**
+ */
+interface Exception
+{
+}
+<?php
+/*
+ * This file is part of the Recursion Context package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\RecursionContext;
+
+/**
+ */
+final class InvalidArgumentException extends \InvalidArgumentException implements Exception
+{
+}
+Resource Operations
+
+Copyright (c) 2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of resource-operations.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\ResourceOperations;
+
+class ResourceOperations
+{
+ /**
+ * @return string[]
+ */
+ public static function getFunctions()
+ {
+ return [
+ 'Directory::close',
+ 'Directory::read',
+ 'Directory::rewind',
+ 'HttpResponse::getRequestBodyStream',
+ 'HttpResponse::getStream',
+ 'MongoGridFSCursor::__construct',
+ 'MongoGridFSFile::getResource',
+ 'MysqlndUhConnection::stmtInit',
+ 'MysqlndUhConnection::storeResult',
+ 'MysqlndUhConnection::useResult',
+ 'PDF_new',
+ 'PDO::pgsqlLOBOpen',
+ 'RarEntry::getStream',
+ 'SQLite3::openBlob',
+ 'XMLWriter::openMemory',
+ 'XMLWriter::openURI',
+ 'ZipArchive::getStream',
+ 'bbcode_create',
+ 'bzopen',
+ 'crack_opendict',
+ 'cubrid_connect',
+ 'cubrid_connect_with_url',
+ 'cubrid_get_query_timeout',
+ 'cubrid_lob2_bind',
+ 'cubrid_lob2_close',
+ 'cubrid_lob2_export',
+ 'cubrid_lob2_import',
+ 'cubrid_lob2_new',
+ 'cubrid_lob2_read',
+ 'cubrid_lob2_seek',
+ 'cubrid_lob2_seek64',
+ 'cubrid_lob2_size',
+ 'cubrid_lob2_size64',
+ 'cubrid_lob2_tell',
+ 'cubrid_lob2_tell64',
+ 'cubrid_lob2_write',
+ 'cubrid_pconnect',
+ 'cubrid_pconnect_with_url',
+ 'cubrid_prepare',
+ 'cubrid_query',
+ 'cubrid_set_query_timeout',
+ 'cubrid_unbuffered_query',
+ 'curl_copy_handle',
+ 'curl_getinfo',
+ 'curl_init',
+ 'curl_multi_add_handle',
+ 'curl_multi_close',
+ 'curl_multi_exec',
+ 'curl_multi_getcontent',
+ 'curl_multi_info_read',
+ 'curl_multi_init',
+ 'curl_multi_remove_handle',
+ 'curl_multi_select',
+ 'curl_multi_setopt',
+ 'curl_pause',
+ 'curl_reset',
+ 'curl_setopt',
+ 'curl_setopt_array',
+ 'curl_share_close',
+ 'curl_share_init',
+ 'curl_share_setopt',
+ 'curl_unescape',
+ 'cyrus_connect',
+ 'db2_column_privileges',
+ 'db2_columns',
+ 'db2_connect',
+ 'db2_exec',
+ 'db2_foreign_keys',
+ 'db2_next_result',
+ 'db2_pconnect',
+ 'db2_prepare',
+ 'db2_primary_keys',
+ 'db2_procedure_columns',
+ 'db2_procedures',
+ 'db2_special_columns',
+ 'db2_statistics',
+ 'db2_table_privileges',
+ 'db2_tables',
+ 'dba_fetch',
+ 'dba_fetch 1',
+ 'dba_open',
+ 'dba_popen',
+ 'dbplus_aql',
+ 'dbplus_open',
+ 'dbplus_rcreate',
+ 'dbplus_ropen',
+ 'dbplus_rquery',
+ 'dbplus_sql',
+ 'deflate_init',
+ 'dio_open',
+ 'eio_busy',
+ 'eio_cancel',
+ 'eio_chmod',
+ 'eio_chown',
+ 'eio_close',
+ 'eio_custom',
+ 'eio_dup2',
+ 'eio_fallocate',
+ 'eio_fchmod',
+ 'eio_fchown',
+ 'eio_fdatasync',
+ 'eio_fstat',
+ 'eio_fstatvfs',
+ 'eio_fsync',
+ 'eio_ftruncate',
+ 'eio_futime',
+ 'eio_get_last_error',
+ 'eio_grp',
+ 'eio_grp_add',
+ 'eio_grp_cancel',
+ 'eio_grp_limit',
+ 'eio_link',
+ 'eio_lstat',
+ 'eio_mkdir',
+ 'eio_mknod',
+ 'eio_nop',
+ 'eio_open',
+ 'eio_read',
+ 'eio_readahead',
+ 'eio_readdir',
+ 'eio_readlink',
+ 'eio_realpath',
+ 'eio_rename',
+ 'eio_rmdir',
+ 'eio_seek',
+ 'eio_sendfile',
+ 'eio_stat',
+ 'eio_statvfs',
+ 'eio_symlink',
+ 'eio_sync',
+ 'eio_sync_file_range',
+ 'eio_syncfs',
+ 'eio_truncate',
+ 'eio_unlink',
+ 'eio_utime',
+ 'eio_write',
+ 'enchant_broker_free_dict',
+ 'enchant_broker_init',
+ 'enchant_broker_request_dict',
+ 'enchant_broker_request_pwl_dict',
+ 'event_base_new',
+ 'event_base_reinit',
+ 'event_buffer_new',
+ 'event_new',
+ 'event_priority_set',
+ 'event_timer_set',
+ 'expect_popen',
+ 'fam_monitor_collection',
+ 'fam_monitor_directory',
+ 'fam_monitor_file',
+ 'fam_open',
+ 'fann_cascadetrain_on_data',
+ 'fann_cascadetrain_on_file',
+ 'fann_clear_scaling_params',
+ 'fann_copy',
+ 'fann_create_from_file',
+ 'fann_create_shortcut_array',
+ 'fann_create_standard',
+ 'fann_create_standard_array',
+ 'fann_create_train',
+ 'fann_create_train_from_callback',
+ 'fann_descale_input',
+ 'fann_descale_output',
+ 'fann_descale_train',
+ 'fann_destroy',
+ 'fann_destroy_train',
+ 'fann_duplicate_train_data',
+ 'fann_get_MSE',
+ 'fann_get_activation_function',
+ 'fann_get_activation_steepness',
+ 'fann_get_bias_array',
+ 'fann_get_bit_fail',
+ 'fann_get_bit_fail_limit',
+ 'fann_get_cascade_activation_functions',
+ 'fann_get_cascade_activation_functions_count',
+ 'fann_get_cascade_activation_steepnesses',
+ 'fann_get_cascade_activation_steepnesses_count',
+ 'fann_get_cascade_candidate_change_fraction',
+ 'fann_get_cascade_candidate_limit',
+ 'fann_get_cascade_candidate_stagnation_epochs',
+ 'fann_get_cascade_max_cand_epochs',
+ 'fann_get_cascade_max_out_epochs',
+ 'fann_get_cascade_min_cand_epochs',
+ 'fann_get_cascade_min_out_epochs',
+ 'fann_get_cascade_num_candidate_groups',
+ 'fann_get_cascade_num_candidates',
+ 'fann_get_cascade_output_change_fraction',
+ 'fann_get_cascade_output_stagnation_epochs',
+ 'fann_get_cascade_weight_multiplier',
+ 'fann_get_connection_array',
+ 'fann_get_connection_rate',
+ 'fann_get_errno',
+ 'fann_get_errstr',
+ 'fann_get_layer_array',
+ 'fann_get_learning_momentum',
+ 'fann_get_learning_rate',
+ 'fann_get_network_type',
+ 'fann_get_num_input',
+ 'fann_get_num_layers',
+ 'fann_get_num_output',
+ 'fann_get_quickprop_decay',
+ 'fann_get_quickprop_mu',
+ 'fann_get_rprop_decrease_factor',
+ 'fann_get_rprop_delta_max',
+ 'fann_get_rprop_delta_min',
+ 'fann_get_rprop_delta_zero',
+ 'fann_get_rprop_increase_factor',
+ 'fann_get_sarprop_step_error_shift',
+ 'fann_get_sarprop_step_error_threshold_factor',
+ 'fann_get_sarprop_temperature',
+ 'fann_get_sarprop_weight_decay_shift',
+ 'fann_get_total_connections',
+ 'fann_get_total_neurons',
+ 'fann_get_train_error_function',
+ 'fann_get_train_stop_function',
+ 'fann_get_training_algorithm',
+ 'fann_init_weights',
+ 'fann_length_train_data',
+ 'fann_merge_train_data',
+ 'fann_num_input_train_data',
+ 'fann_num_output_train_data',
+ 'fann_randomize_weights',
+ 'fann_read_train_from_file',
+ 'fann_reset_errno',
+ 'fann_reset_errstr',
+ 'fann_run',
+ 'fann_save',
+ 'fann_save_train',
+ 'fann_scale_input',
+ 'fann_scale_input_train_data',
+ 'fann_scale_output',
+ 'fann_scale_output_train_data',
+ 'fann_scale_train',
+ 'fann_scale_train_data',
+ 'fann_set_activation_function',
+ 'fann_set_activation_function_hidden',
+ 'fann_set_activation_function_layer',
+ 'fann_set_activation_function_output',
+ 'fann_set_activation_steepness',
+ 'fann_set_activation_steepness_hidden',
+ 'fann_set_activation_steepness_layer',
+ 'fann_set_activation_steepness_output',
+ 'fann_set_bit_fail_limit',
+ 'fann_set_callback',
+ 'fann_set_cascade_activation_functions',
+ 'fann_set_cascade_activation_steepnesses',
+ 'fann_set_cascade_candidate_change_fraction',
+ 'fann_set_cascade_candidate_limit',
+ 'fann_set_cascade_candidate_stagnation_epochs',
+ 'fann_set_cascade_max_cand_epochs',
+ 'fann_set_cascade_max_out_epochs',
+ 'fann_set_cascade_min_cand_epochs',
+ 'fann_set_cascade_min_out_epochs',
+ 'fann_set_cascade_num_candidate_groups',
+ 'fann_set_cascade_output_change_fraction',
+ 'fann_set_cascade_output_stagnation_epochs',
+ 'fann_set_cascade_weight_multiplier',
+ 'fann_set_error_log',
+ 'fann_set_input_scaling_params',
+ 'fann_set_learning_momentum',
+ 'fann_set_learning_rate',
+ 'fann_set_output_scaling_params',
+ 'fann_set_quickprop_decay',
+ 'fann_set_quickprop_mu',
+ 'fann_set_rprop_decrease_factor',
+ 'fann_set_rprop_delta_max',
+ 'fann_set_rprop_delta_min',
+ 'fann_set_rprop_delta_zero',
+ 'fann_set_rprop_increase_factor',
+ 'fann_set_sarprop_step_error_shift',
+ 'fann_set_sarprop_step_error_threshold_factor',
+ 'fann_set_sarprop_temperature',
+ 'fann_set_sarprop_weight_decay_shift',
+ 'fann_set_scaling_params',
+ 'fann_set_train_error_function',
+ 'fann_set_train_stop_function',
+ 'fann_set_training_algorithm',
+ 'fann_set_weight',
+ 'fann_set_weight_array',
+ 'fann_shuffle_train_data',
+ 'fann_subset_train_data',
+ 'fann_test',
+ 'fann_test_data',
+ 'fann_train',
+ 'fann_train_epoch',
+ 'fann_train_on_data',
+ 'fann_train_on_file',
+ 'fbsql_connect',
+ 'fbsql_db_query',
+ 'fbsql_list_dbs',
+ 'fbsql_list_fields',
+ 'fbsql_list_tables',
+ 'fbsql_pconnect',
+ 'fbsql_query',
+ 'fdf_create',
+ 'fdf_open',
+ 'fdf_open_string',
+ 'finfo::buffer',
+ 'finfo_buffer',
+ 'finfo_close',
+ 'finfo_file',
+ 'finfo_open',
+ 'finfo_set_flags',
+ 'fopen',
+ 'fsockopen',
+ 'ftp_alloc',
+ 'ftp_cdup',
+ 'ftp_chdir',
+ 'ftp_chmod',
+ 'ftp_close',
+ 'ftp_connect',
+ 'ftp_delete',
+ 'ftp_exec',
+ 'ftp_fget',
+ 'ftp_fput',
+ 'ftp_get',
+ 'ftp_get_option',
+ 'ftp_login',
+ 'ftp_mdtm',
+ 'ftp_mkdir',
+ 'ftp_nb_continue',
+ 'ftp_nb_fget',
+ 'ftp_nb_fput',
+ 'ftp_nb_get',
+ 'ftp_nb_put',
+ 'ftp_nlist',
+ 'ftp_pasv',
+ 'ftp_put',
+ 'ftp_pwd',
+ 'ftp_raw',
+ 'ftp_rawlist',
+ 'ftp_rename',
+ 'ftp_rmdir',
+ 'ftp_set_option',
+ 'ftp_site',
+ 'ftp_size',
+ 'ftp_ssl_connect',
+ 'ftp_systype',
+ 'gnupg_init',
+ 'gupnp_context_new',
+ 'gupnp_control_point_new',
+ 'gupnp_device_info_get_service',
+ 'gupnp_root_device_new',
+ 'gzopen',
+ 'hash_copy',
+ 'hash_final',
+ 'hash_init',
+ 'hash_update',
+ 'hash_update_file',
+ 'hash_update_stream',
+ 'http_get_request_body_stream',
+ 'ibase_blob_create',
+ 'ibase_blob_open',
+ 'ibase_blob_open 1',
+ 'ibase_connect',
+ 'ibase_pconnect',
+ 'ibase_prepare',
+ 'ibase_service_attach',
+ 'ibase_set_event_handler',
+ 'ibase_set_event_handler 1',
+ 'ibase_trans',
+ 'ifx_connect',
+ 'ifx_pconnect',
+ 'ifx_prepare',
+ 'ifx_query',
+ 'imageaffine',
+ 'imageconvolution',
+ 'imagecreate',
+ 'imagecreatefromgd',
+ 'imagecreatefromgd2',
+ 'imagecreatefromgd2part',
+ 'imagecreatefromgif',
+ 'imagecreatefromjpeg',
+ 'imagecreatefrompng',
+ 'imagecreatefromstring',
+ 'imagecreatefromwbmp',
+ 'imagecreatefromwebp',
+ 'imagecreatefromxbm',
+ 'imagecreatefromxpm',
+ 'imagecreatetruecolor',
+ 'imagegrabscreen',
+ 'imagegrabwindow',
+ 'imagepalettetotruecolor',
+ 'imagepsloadfont',
+ 'imagerotate',
+ 'imagescale',
+ 'imap_open',
+ 'inflate_init',
+ 'ingres_connect',
+ 'ingres_pconnect',
+ 'inotify_init',
+ 'kadm5_init_with_password',
+ 'ldap_connect',
+ 'ldap_first_entry',
+ 'ldap_first_reference',
+ 'ldap_list',
+ 'ldap_next_entry',
+ 'ldap_next_reference',
+ 'ldap_read',
+ 'ldap_search',
+ 'm_initconn',
+ 'mailparse_msg_create',
+ 'mailparse_msg_get_part',
+ 'mailparse_msg_parse_file',
+ 'maxdb::use_result',
+ 'maxdb_connect',
+ 'maxdb_embedded_connect',
+ 'maxdb_init',
+ 'maxdb_stmt::result_metadata',
+ 'maxdb_stmt_result_metadata',
+ 'maxdb_use_result',
+ 'mcrypt_module_open',
+ 'msg_get_queue',
+ 'msql_connect',
+ 'msql_db_query',
+ 'msql_list_dbs',
+ 'msql_list_fields',
+ 'msql_list_tables',
+ 'msql_pconnect',
+ 'msql_query',
+ 'mssql_connect',
+ 'mssql_init',
+ 'mssql_pconnect',
+ 'mysql_connect',
+ 'mysql_db_query',
+ 'mysql_list_dbs',
+ 'mysql_list_fields',
+ 'mysql_list_processes',
+ 'mysql_list_tables',
+ 'mysql_pconnect',
+ 'mysql_query',
+ 'mysql_unbuffered_query',
+ 'mysqlnd_uh_convert_to_mysqlnd',
+ 'ncurses_new_panel',
+ 'ncurses_newpad',
+ 'ncurses_newwin',
+ 'ncurses_panel_above',
+ 'ncurses_panel_below',
+ 'ncurses_panel_window',
+ 'newt_button',
+ 'newt_button_bar',
+ 'newt_checkbox',
+ 'newt_checkbox_tree',
+ 'newt_checkbox_tree_multi',
+ 'newt_compact_button',
+ 'newt_create_grid',
+ 'newt_entry',
+ 'newt_form',
+ 'newt_form_get_current',
+ 'newt_grid_basic_window',
+ 'newt_grid_h_close_stacked',
+ 'newt_grid_h_stacked',
+ 'newt_grid_simple_window',
+ 'newt_grid_v_close_stacked',
+ 'newt_grid_v_stacked',
+ 'newt_label',
+ 'newt_listbox',
+ 'newt_listitem',
+ 'newt_radio_get_current',
+ 'newt_radiobutton',
+ 'newt_run_form',
+ 'newt_scale',
+ 'newt_textbox',
+ 'newt_textbox_reflowed',
+ 'newt_vertical_scrollbar',
+ 'oci_connect',
+ 'oci_get_implicit_resultset',
+ 'oci_new_connect',
+ 'oci_new_cursor',
+ 'oci_parse',
+ 'oci_pconnect',
+ 'odbc_columnprivileges',
+ 'odbc_columns',
+ 'odbc_connect',
+ 'odbc_exec',
+ 'odbc_foreignkeys',
+ 'odbc_gettypeinfo',
+ 'odbc_pconnect',
+ 'odbc_prepare',
+ 'odbc_primarykeys',
+ 'odbc_procedurecolumns',
+ 'odbc_procedures',
+ 'odbc_specialcolumns',
+ 'odbc_statistics',
+ 'odbc_tableprivileges',
+ 'odbc_tables',
+ 'openal_buffer_create',
+ 'openal_context_create',
+ 'openal_device_open',
+ 'openal_source_create',
+ 'openal_stream',
+ 'openssl_csr_new',
+ 'openssl_csr_sign',
+ 'openssl_pkey_get_private',
+ 'openssl_pkey_get_public',
+ 'openssl_pkey_new',
+ 'openssl_x509_read',
+ 'pfsockopen',
+ 'pg_cancel_query',
+ 'pg_client_encoding',
+ 'pg_close',
+ 'pg_connect',
+ 'pg_connect_poll',
+ 'pg_connection_busy',
+ 'pg_connection_reset',
+ 'pg_connection_status',
+ 'pg_consume_input',
+ 'pg_copy_from',
+ 'pg_copy_to',
+ 'pg_dbname',
+ 'pg_end_copy',
+ 'pg_escape_bytea',
+ 'pg_escape_identifier',
+ 'pg_escape_identifier 1',
+ 'pg_escape_literal',
+ 'pg_escape_string',
+ 'pg_execute',
+ 'pg_execute 1',
+ 'pg_flush',
+ 'pg_free_result',
+ 'pg_get_notify',
+ 'pg_get_pid',
+ 'pg_get_result',
+ 'pg_host',
+ 'pg_last_error',
+ 'pg_last_notice',
+ 'pg_lo_create',
+ 'pg_lo_export',
+ 'pg_lo_import',
+ 'pg_lo_open',
+ 'pg_lo_unlink',
+ 'pg_options',
+ 'pg_parameter_status',
+ 'pg_pconnect',
+ 'pg_ping',
+ 'pg_port',
+ 'pg_prepare',
+ 'pg_prepare 1',
+ 'pg_put_line',
+ 'pg_query',
+ 'pg_query 1',
+ 'pg_query_params',
+ 'pg_query_params 1',
+ 'pg_send_execute',
+ 'pg_send_prepare',
+ 'pg_send_query',
+ 'pg_send_query_params',
+ 'pg_set_client_encoding',
+ 'pg_set_client_encoding 1',
+ 'pg_set_error_verbosity',
+ 'pg_socket',
+ 'pg_trace',
+ 'pg_transaction_status',
+ 'pg_tty',
+ 'pg_untrace',
+ 'pg_version',
+ 'php_user_filter::filter',
+ 'popen',
+ 'proc_open',
+ 'ps_new',
+ 'px_new',
+ 'radius_acct_open',
+ 'radius_auth_open',
+ 'radius_salt_encrypt_attr',
+ 'rpm_open',
+ 'sem_get',
+ 'shm_attach',
+ 'socket_accept',
+ 'socket_create',
+ 'socket_create_listen',
+ 'socket_recvmsg',
+ 'socket_sendmsg',
+ 'sqlite_open',
+ 'sqlite_popen',
+ 'sqlsrv_begin_transaction',
+ 'sqlsrv_cancel',
+ 'sqlsrv_client_info',
+ 'sqlsrv_close',
+ 'sqlsrv_commit',
+ 'sqlsrv_connect',
+ 'sqlsrv_execute',
+ 'sqlsrv_fetch',
+ 'sqlsrv_fetch_array',
+ 'sqlsrv_fetch_object',
+ 'sqlsrv_field_metadata',
+ 'sqlsrv_free_stmt',
+ 'sqlsrv_get_field',
+ 'sqlsrv_has_rows',
+ 'sqlsrv_next_result',
+ 'sqlsrv_num_fields',
+ 'sqlsrv_num_rows',
+ 'sqlsrv_prepare',
+ 'sqlsrv_query',
+ 'sqlsrv_rollback',
+ 'sqlsrv_rows_affected',
+ 'sqlsrv_send_stream_data',
+ 'sqlsrv_server_info',
+ 'ssh2_auth_agent',
+ 'ssh2_connect',
+ 'ssh2_exec',
+ 'ssh2_fetch_stream',
+ 'ssh2_publickey_init',
+ 'ssh2_sftp',
+ 'ssh2_sftp_chmod',
+ 'ssh2_shell',
+ 'ssh2_tunnel',
+ 'stomp_connect',
+ 'streamWrapper::stream_cast',
+ 'stream_bucket_new',
+ 'stream_context_create',
+ 'stream_context_get_default',
+ 'stream_context_set_default',
+ 'stream_filter_append',
+ 'stream_filter_prepend',
+ 'stream_socket_accept',
+ 'stream_socket_client',
+ 'stream_socket_server',
+ 'svn_fs_apply_text',
+ 'svn_fs_begin_txn2',
+ 'svn_fs_file_contents',
+ 'svn_fs_revision_root',
+ 'svn_fs_txn_root',
+ 'svn_repos_create',
+ 'svn_repos_fs',
+ 'svn_repos_fs_begin_txn_for_commit',
+ 'svn_repos_open',
+ 'sybase_connect',
+ 'sybase_pconnect',
+ 'sybase_unbuffered_query',
+ 'tmpfile',
+ 'udm_alloc_agent',
+ 'udm_alloc_agent_array',
+ 'udm_find',
+ 'unlink',
+ 'w32api_init_dtype',
+ 'wddx_packet_start',
+ 'xml_parser_create',
+ 'xml_parser_create_ns',
+ 'xml_parser_free',
+ 'xml_parser_get_option',
+ 'xml_parser_set_option',
+ 'xmlrpc_server_create',
+ 'xmlwriter_open_memory',
+ 'xmlwriter_open_uri',
+ 'xslt_create',
+ 'zip_open',
+ 'zip_read',
+ ];
+ }
+}
+GlobalState
+
+Copyright (c) 2001-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the GlobalState package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\GlobalState;
+
+use ReflectionClass;
+
+/**
+ * A blacklist for global state elements that should not be snapshotted.
+ */
+class Blacklist
+{
+ /**
+ * @var array
+ */
+ private $globalVariables = array();
+
+ /**
+ * @var array
+ */
+ private $classes = array();
+
+ /**
+ * @var array
+ */
+ private $classNamePrefixes = array();
+
+ /**
+ * @var array
+ */
+ private $parentClasses = array();
+
+ /**
+ * @var array
+ */
+ private $interfaces = array();
+
+ /**
+ * @var array
+ */
+ private $staticAttributes = array();
+
+ /**
+ * @param string $variableName
+ */
+ public function addGlobalVariable($variableName)
+ {
+ $this->globalVariables[$variableName] = true;
+ }
+
+ /**
+ * @param string $className
+ */
+ public function addClass($className)
+ {
+ $this->classes[] = $className;
+ }
+
+ /**
+ * @param string $className
+ */
+ public function addSubclassesOf($className)
+ {
+ $this->parentClasses[] = $className;
+ }
+
+ /**
+ * @param string $interfaceName
+ */
+ public function addImplementorsOf($interfaceName)
+ {
+ $this->interfaces[] = $interfaceName;
+ }
+
+ /**
+ * @param string $classNamePrefix
+ */
+ public function addClassNamePrefix($classNamePrefix)
+ {
+ $this->classNamePrefixes[] = $classNamePrefix;
+ }
+
+ /**
+ * @param string $className
+ * @param string $attributeName
+ */
+ public function addStaticAttribute($className, $attributeName)
+ {
+ if (!isset($this->staticAttributes[$className])) {
+ $this->staticAttributes[$className] = array();
+ }
+
+ $this->staticAttributes[$className][$attributeName] = true;
+ }
+
+ /**
+ * @param string $variableName
+ * @return bool
+ */
+ public function isGlobalVariableBlacklisted($variableName)
+ {
+ return isset($this->globalVariables[$variableName]);
+ }
+
+ /**
+ * @param string $className
+ * @param string $attributeName
+ * @return bool
+ */
+ public function isStaticAttributeBlacklisted($className, $attributeName)
+ {
+ if (in_array($className, $this->classes)) {
+ return true;
+ }
+
+ foreach ($this->classNamePrefixes as $prefix) {
+ if (strpos($className, $prefix) === 0) {
+ return true;
+ }
+ }
+
+ $class = new ReflectionClass($className);
+
+ foreach ($this->parentClasses as $type) {
+ if ($class->isSubclassOf($type)) {
+ return true;
+ }
+ }
+
+ foreach ($this->interfaces as $type) {
+ if ($class->implementsInterface($type)) {
+ return true;
+ }
+ }
+
+ if (isset($this->staticAttributes[$className][$attributeName])) {
+ return true;
+ }
+
+ return false;
+ }
+}
+<?php
+/*
+ * This file is part of the GlobalState package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\GlobalState;
+
+/**
+ * Exports parts of a Snapshot as PHP code.
+ */
+class CodeExporter
+{
+ /**
+ * @param Snapshot $snapshot
+ * @return string
+ */
+ public function constants(Snapshot $snapshot)
+ {
+ $result = '';
+
+ foreach ($snapshot->constants() as $name => $value) {
+ $result .= sprintf(
+ 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n",
+ $name,
+ $name,
+ $this->exportVariable($value)
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param Snapshot $snapshot
+ * @return string
+ */
+ public function iniSettings(Snapshot $snapshot)
+ {
+ $result = '';
+
+ foreach ($snapshot->iniSettings() as $key => $value) {
+ $result .= sprintf(
+ '@ini_set(%s, %s);' . "\n",
+ $this->exportVariable($key),
+ $this->exportVariable($value)
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param mixed $variable
+ * @return string
+ */
+ private function exportVariable($variable)
+ {
+ if (is_scalar($variable) || is_null($variable) ||
+ (is_array($variable) && $this->arrayOnlyContainsScalars($variable))) {
+ return var_export($variable, true);
+ }
+
+ return 'unserialize(' . var_export(serialize($variable), true) . ')';
+ }
+
+ /**
+ * @param array $array
+ * @return bool
+ */
+ private function arrayOnlyContainsScalars(array $array)
+ {
+ $result = true;
+
+ foreach ($array as $element) {
+ if (is_array($element)) {
+ $result = self::arrayOnlyContainsScalars($element);
+ } elseif (!is_scalar($element) && !is_null($element)) {
+ $result = false;
+ }
+
+ if ($result === false) {
+ break;
+ }
+ }
+
+ return $result;
+ }
+}
+<?php
+/*
+ * This file is part of the GlobalState package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\GlobalState;
+
+/**
+ */
+interface Exception
+{
+}
+<?php
+/*
+ * This file is part of the GlobalState package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\GlobalState;
+
+use ReflectionProperty;
+
+/**
+ * Restorer of snapshots of global state.
+ */
+class Restorer
+{
+ /**
+ * Deletes function definitions that are not defined in a snapshot.
+ *
+ * @param Snapshot $snapshot
+ * @throws RuntimeException when the uopz_delete() function is not available
+ * @see https://github.com/krakjoe/uopz
+ */
+ public function restoreFunctions(Snapshot $snapshot)
+ {
+ if (!function_exists('uopz_delete')) {
+ throw new RuntimeException('The uopz_delete() function is required for this operation');
+ }
+
+ $functions = get_defined_functions();
+
+ foreach (array_diff($functions['user'], $snapshot->functions()) as $function) {
+ uopz_delete($function);
+ }
+ }
+
+ /**
+ * Restores all global and super-global variables from a snapshot.
+ *
+ * @param Snapshot $snapshot
+ */
+ public function restoreGlobalVariables(Snapshot $snapshot)
+ {
+ $superGlobalArrays = $snapshot->superGlobalArrays();
+
+ foreach ($superGlobalArrays as $superGlobalArray) {
+ $this->restoreSuperGlobalArray($snapshot, $superGlobalArray);
+ }
+
+ $globalVariables = $snapshot->globalVariables();
+
+ foreach (array_keys($GLOBALS) as $key) {
+ if ($key != 'GLOBALS' &&
+ !in_array($key, $superGlobalArrays) &&
+ !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) {
+ if (isset($globalVariables[$key])) {
+ $GLOBALS[$key] = $globalVariables[$key];
+ } else {
+ unset($GLOBALS[$key]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Restores all static attributes in user-defined classes from this snapshot.
+ *
+ * @param Snapshot $snapshot
+ */
+ public function restoreStaticAttributes(Snapshot $snapshot)
+ {
+ $current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false);
+ $newClasses = array_diff($current->classes(), $snapshot->classes());
+ unset($current);
+
+ foreach ($snapshot->staticAttributes() as $className => $staticAttributes) {
+ foreach ($staticAttributes as $name => $value) {
+ $reflector = new ReflectionProperty($className, $name);
+ $reflector->setAccessible(true);
+ $reflector->setValue($value);
+ }
+ }
+
+ foreach ($newClasses as $className) {
+ $class = new \ReflectionClass($className);
+ $defaults = $class->getDefaultProperties();
+
+ foreach ($class->getProperties() as $attribute) {
+ if (!$attribute->isStatic()) {
+ continue;
+ }
+
+ $name = $attribute->getName();
+
+ if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) {
+ continue;
+ }
+
+ if (!isset($defaults[$name])) {
+ continue;
+ }
+
+ $attribute->setAccessible(true);
+ $attribute->setValue($defaults[$name]);
+ }
+ }
+ }
+
+ /**
+ * Restores a super-global variable array from this snapshot.
+ *
+ * @param Snapshot $snapshot
+ * @param $superGlobalArray
+ */
+ private function restoreSuperGlobalArray(Snapshot $snapshot, $superGlobalArray)
+ {
+ $superGlobalVariables = $snapshot->superGlobalVariables();
+
+ if (isset($GLOBALS[$superGlobalArray]) &&
+ is_array($GLOBALS[$superGlobalArray]) &&
+ isset($superGlobalVariables[$superGlobalArray])) {
+ $keys = array_keys(
+ array_merge(
+ $GLOBALS[$superGlobalArray],
+ $superGlobalVariables[$superGlobalArray]
+ )
+ );
+
+ foreach ($keys as $key) {
+ if (isset($superGlobalVariables[$superGlobalArray][$key])) {
+ $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key];
+ } else {
+ unset($GLOBALS[$superGlobalArray][$key]);
+ }
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of the GlobalState package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\GlobalState;
+
+/**
+ */
+class RuntimeException extends \RuntimeException implements Exception
+{
+}
+<?php
+/*
+ * This file is part of the GlobalState package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\GlobalState;
+
+use ReflectionClass;
+use Serializable;
+
+/**
+ * A snapshot of global state.
+ */
+class Snapshot
+{
+ /**
+ * @var Blacklist
+ */
+ private $blacklist;
+
+ /**
+ * @var array
+ */
+ private $globalVariables = array();
+
+ /**
+ * @var array
+ */
+ private $superGlobalArrays = array();
+
+ /**
+ * @var array
+ */
+ private $superGlobalVariables = array();
+
+ /**
+ * @var array
+ */
+ private $staticAttributes = array();
+
+ /**
+ * @var array
+ */
+ private $iniSettings = array();
+
+ /**
+ * @var array
+ */
+ private $includedFiles = array();
+
+ /**
+ * @var array
+ */
+ private $constants = array();
+
+ /**
+ * @var array
+ */
+ private $functions = array();
+
+ /**
+ * @var array
+ */
+ private $interfaces = array();
+
+ /**
+ * @var array
+ */
+ private $classes = array();
+
+ /**
+ * @var array
+ */
+ private $traits = array();
+
+ /**
+ * Creates a snapshot of the current global state.
+ *
+ * @param Blacklist $blacklist
+ * @param bool $includeGlobalVariables
+ * @param bool $includeStaticAttributes
+ * @param bool $includeConstants
+ * @param bool $includeFunctions
+ * @param bool $includeClasses
+ * @param bool $includeInterfaces
+ * @param bool $includeTraits
+ * @param bool $includeIniSettings
+ * @param bool $includeIncludedFiles
+ */
+ public function __construct(Blacklist $blacklist = null, $includeGlobalVariables = true, $includeStaticAttributes = true, $includeConstants = true, $includeFunctions = true, $includeClasses = true, $includeInterfaces = true, $includeTraits = true, $includeIniSettings = true, $includeIncludedFiles = true)
+ {
+ if ($blacklist === null) {
+ $blacklist = new Blacklist;
+ }
+
+ $this->blacklist = $blacklist;
+
+ if ($includeConstants) {
+ $this->snapshotConstants();
+ }
+
+ if ($includeFunctions) {
+ $this->snapshotFunctions();
+ }
+
+ if ($includeClasses || $includeStaticAttributes) {
+ $this->snapshotClasses();
+ }
+
+ if ($includeInterfaces) {
+ $this->snapshotInterfaces();
+ }
+
+ if ($includeGlobalVariables) {
+ $this->setupSuperGlobalArrays();
+ $this->snapshotGlobals();
+ }
+
+ if ($includeStaticAttributes) {
+ $this->snapshotStaticAttributes();
+ }
+
+ if ($includeIniSettings) {
+ $this->iniSettings = ini_get_all(null, false);
+ }
+
+ if ($includeIncludedFiles) {
+ $this->includedFiles = get_included_files();
+ }
+
+ if (function_exists('get_declared_traits')) {
+ $this->traits = get_declared_traits();
+ }
+ }
+
+ /**
+ * @return Blacklist
+ */
+ public function blacklist()
+ {
+ return $this->blacklist;
+ }
+
+ /**
+ * @return array
+ */
+ public function globalVariables()
+ {
+ return $this->globalVariables;
+ }
+
+ /**
+ * @return array
+ */
+ public function superGlobalVariables()
+ {
+ return $this->superGlobalVariables;
+ }
+
+ /**
+ * Returns a list of all super-global variable arrays.
+ *
+ * @return array
+ */
+ public function superGlobalArrays()
+ {
+ return $this->superGlobalArrays;
+ }
+
+ /**
+ * @return array
+ */
+ public function staticAttributes()
+ {
+ return $this->staticAttributes;
+ }
+
+ /**
+ * @return array
+ */
+ public function iniSettings()
+ {
+ return $this->iniSettings;
+ }
+
+ /**
+ * @return array
+ */
+ public function includedFiles()
+ {
+ return $this->includedFiles;
+ }
+
+ /**
+ * @return array
+ */
+ public function constants()
+ {
+ return $this->constants;
+ }
+
+ /**
+ * @return array
+ */
+ public function functions()
+ {
+ return $this->functions;
+ }
+
+ /**
+ * @return array
+ */
+ public function interfaces()
+ {
+ return $this->interfaces;
+ }
+
+ /**
+ * @return array
+ */
+ public function classes()
+ {
+ return $this->classes;
+ }
+
+ /**
+ * @return array
+ */
+ public function traits()
+ {
+ return $this->traits;
+ }
+
+ /**
+ * Creates a snapshot user-defined constants.
+ */
+ private function snapshotConstants()
+ {
+ $constants = get_defined_constants(true);
+
+ if (isset($constants['user'])) {
+ $this->constants = $constants['user'];
+ }
+ }
+
+ /**
+ * Creates a snapshot user-defined functions.
+ */
+ private function snapshotFunctions()
+ {
+ $functions = get_defined_functions();
+
+ $this->functions = $functions['user'];
+ }
+
+ /**
+ * Creates a snapshot user-defined classes.
+ */
+ private function snapshotClasses()
+ {
+ foreach (array_reverse(get_declared_classes()) as $className) {
+ $class = new ReflectionClass($className);
+
+ if (!$class->isUserDefined()) {
+ break;
+ }
+
+ $this->classes[] = $className;
+ }
+
+ $this->classes = array_reverse($this->classes);
+ }
+
+ /**
+ * Creates a snapshot user-defined interfaces.
+ */
+ private function snapshotInterfaces()
+ {
+ foreach (array_reverse(get_declared_interfaces()) as $interfaceName) {
+ $class = new ReflectionClass($interfaceName);
+
+ if (!$class->isUserDefined()) {
+ break;
+ }
+
+ $this->interfaces[] = $interfaceName;
+ }
+
+ $this->interfaces = array_reverse($this->interfaces);
+ }
+
+ /**
+ * Creates a snapshot of all global and super-global variables.
+ */
+ private function snapshotGlobals()
+ {
+ $superGlobalArrays = $this->superGlobalArrays();
+
+ foreach ($superGlobalArrays as $superGlobalArray) {
+ $this->snapshotSuperGlobalArray($superGlobalArray);
+ }
+
+ foreach (array_keys($GLOBALS) as $key) {
+ if ($key != 'GLOBALS' &&
+ !in_array($key, $superGlobalArrays) &&
+ $this->canBeSerialized($GLOBALS[$key]) &&
+ !$this->blacklist->isGlobalVariableBlacklisted($key)) {
+ $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key]));
+ }
+ }
+ }
+
+ /**
+ * Creates a snapshot a super-global variable array.
+ *
+ * @param $superGlobalArray
+ */
+ private function snapshotSuperGlobalArray($superGlobalArray)
+ {
+ $this->superGlobalVariables[$superGlobalArray] = array();
+
+ if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) {
+ foreach ($GLOBALS[$superGlobalArray] as $key => $value) {
+ $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value));
+ }
+ }
+ }
+
+ /**
+ * Creates a snapshot of all static attributes in user-defined classes.
+ */
+ private function snapshotStaticAttributes()
+ {
+ foreach ($this->classes as $className) {
+ $class = new ReflectionClass($className);
+ $snapshot = array();
+
+ foreach ($class->getProperties() as $attribute) {
+ if ($attribute->isStatic()) {
+ $name = $attribute->getName();
+
+ if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) {
+ continue;
+ }
+
+ $attribute->setAccessible(true);
+ $value = $attribute->getValue();
+
+ if ($this->canBeSerialized($value)) {
+ $snapshot[$name] = unserialize(serialize($value));
+ }
+ }
+ }
+
+ if (!empty($snapshot)) {
+ $this->staticAttributes[$className] = $snapshot;
+ }
+ }
+ }
+
+ /**
+ * Returns a list of all super-global variable arrays.
+ *
+ * @return array
+ */
+ private function setupSuperGlobalArrays()
+ {
+ $this->superGlobalArrays = array(
+ '_ENV',
+ '_POST',
+ '_GET',
+ '_COOKIE',
+ '_SERVER',
+ '_FILES',
+ '_REQUEST'
+ );
+
+ if (ini_get('register_long_arrays') == '1') {
+ $this->superGlobalArrays = array_merge(
+ $this->superGlobalArrays,
+ array(
+ 'HTTP_ENV_VARS',
+ 'HTTP_POST_VARS',
+ 'HTTP_GET_VARS',
+ 'HTTP_COOKIE_VARS',
+ 'HTTP_SERVER_VARS',
+ 'HTTP_POST_FILES'
+ )
+ );
+ }
+ }
+
+ /**
+ * @param mixed $variable
+ * @return bool
+ * @todo Implement this properly
+ */
+ private function canBeSerialized($variable)
+ {
+ if (!is_object($variable)) {
+ return !is_resource($variable);
+ }
+
+ if ($variable instanceof \stdClass) {
+ return true;
+ }
+
+ $class = new ReflectionClass($variable);
+
+ do {
+ if ($class->isInternal()) {
+ return $variable instanceof Serializable;
+ }
+ } while ($class = $class->getParentClass());
+
+ return true;
+ }
+}
+Object Enumerator
+
+Copyright (c) 2016, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of Object Enumerator.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\ObjectEnumerator;
+
+use SebastianBergmann\RecursionContext\Context;
+
+/**
+ * Traverses array structures and object graphs
+ * to enumerate all referenced objects.
+ */
+class Enumerator
+{
+ /**
+ * Returns an array of all objects referenced either
+ * directly or indirectly by a variable.
+ *
+ * @param array|object $variable
+ *
+ * @return object[]
+ */
+ public function enumerate($variable)
+ {
+ if (!is_array($variable) && !is_object($variable)) {
+ throw new InvalidArgumentException;
+ }
+
+ if (isset(func_get_args()[1])) {
+ if (!func_get_args()[1] instanceof Context) {
+ throw new InvalidArgumentException;
+ }
+
+ $processed = func_get_args()[1];
+ } else {
+ $processed = new Context;
+ }
+
+ $objects = [];
+
+ if ($processed->contains($variable)) {
+ return $objects;
+ }
+
+ $array = $variable;
+ $processed->add($variable);
+
+ if (is_array($variable)) {
+ foreach ($array as $element) {
+ if (!is_array($element) && !is_object($element)) {
+ continue;
+ }
+
+ $objects = array_merge(
+ $objects,
+ $this->enumerate($element, $processed)
+ );
+ }
+ } else {
+ $objects[] = $variable;
+ $reflector = new \ReflectionObject($variable);
+
+ foreach ($reflector->getProperties() as $attribute) {
+ $attribute->setAccessible(true);
+
+ try {
+ $value = $attribute->getValue($variable);
+ } catch (\Throwable $e) {
+ continue;
+ } catch (\Exception $e) {
+ continue;
+ }
+
+ if (!is_array($value) && !is_object($value)) {
+ continue;
+ }
+
+ $objects = array_merge(
+ $objects,
+ $this->enumerate($value, $processed)
+ );
+ }
+ }
+
+ return $objects;
+ }
+}
+<?php
+/*
+ * This file is part of Object Enumerator.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\ObjectEnumerator;
+
+interface Exception
+{
+}
+<?php
+/*
+ * This file is part of Object Enumerator.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann\ObjectEnumerator;
+
+class InvalidArgumentException extends \InvalidArgumentException implements Exception
+{
+}
+Version
+
+Copyright (c) 2013-2015, Sebastian Bergmann <sebastian@phpunit.de>.
+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 Sebastian Bergmann nor the names of his
+ 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.
+<?php
+/*
+ * This file is part of the Version package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace SebastianBergmann;
+
+/**
+ * @since Class available since Release 1.0.0
+ */
+class Version
+{
+ /**
+ * @var string
+ */
+ private $path;
+
+ /**
+ * @var string
+ */
+ private $release;
+
+ /**
+ * @var string
+ */
+ private $version;
+
+ /**
+ * @param string $release
+ * @param string $path
+ */
+ public function __construct($release, $path)
+ {
+ $this->release = $release;
+ $this->path = $path;
+ }
+
+ /**
+ * @return string
+ */
+ public function getVersion()
+ {
+ if ($this->version === null) {
+ if (count(explode('.', $this->release)) == 3) {
+ $this->version = $this->release;
+ } else {
+ $this->version = $this->release . '-dev';
+ }
+
+ $git = $this->getGitInformation($this->path);
+
+ if ($git) {
+ if (count(explode('.', $this->release)) == 3) {
+ $this->version = $git;
+ } else {
+ $git = explode('-', $git);
+
+ $this->version = $this->release . '-' . end($git);
+ }
+ }
+ }
+
+ return $this->version;
+ }
+
+ /**
+ * @param string $path
+ *
+ * @return bool|string
+ */
+ private function getGitInformation($path)
+ {
+ if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) {
+ return false;
+ }
+
+ $process = proc_open(
+ 'git describe --tags',
+ [
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', 'w'],
+ ],
+ $pipes,
+ $path
+ );
+
+ if (!is_resource($process)) {
+ return false;
+ }
+
+ $result = trim(stream_get_contents($pipes[1]));
+
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+
+ $returnCode = proc_close($process);
+
+ if ($returnCode !== 0) {
+ return false;
+ }
+
+ return $result;
+ }
+}
+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.
+<?php
+/*
+ * 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Instantiator\Exception;
+
+/**
+ * Base exception marker interface for the instantiator component
+ *
+ * @author Marco Pivetta <ocramius@gmail.com>
+ */
+interface ExceptionInterface
+{
+}
+<?php
+/*
+ * 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Instantiator\Exception;
+
+use InvalidArgumentException as BaseInvalidArgumentException;
+use ReflectionClass;
+
+/**
+ * Exception for invalid arguments provided to the instantiator
+ *
+ * @author Marco Pivetta <ocramius@gmail.com>
+ */
+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()
+ ));
+ }
+}
+<?php
+/*
+ * 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+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 <ocramius@gmail.com>
+ */
+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)
+ );
+ }
+}
+<?php
+/*
+ * 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Instantiator;
+
+use Closure;
+use Doctrine\Instantiator\Exception\InvalidArgumentException;
+use Doctrine\Instantiator\Exception\UnexpectedValueException;
+use Exception;
+use ReflectionClass;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Marco Pivetta <ocramius@gmail.com>
+ */
+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');
+ }
+}
+<?php
+/*
+ * 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.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Instantiator;
+
+/**
+ * Instantiator provides utility methods to build objects without invoking their constructors
+ *
+ * @author Marco Pivetta <ocramius@gmail.com>
+ */
+interface InstantiatorInterface
+{
+ /**
+ * @param string $className
+ *
+ * @return object
+ *
+ * @throws \Doctrine\Instantiator\Exception\ExceptionInterface
+ */
+ public function instantiate($className);
+}
+Copyright (c) 2004-2017 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.
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml;
+
+/**
+ * Dumper dumps PHP variables to YAML strings.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Dumper
+{
+ /**
+ * The amount of spaces to use for indentation of nested nodes.
+ *
+ * @var int
+ */
+ protected $indentation;
+
+ /**
+ * @param int $indentation
+ */
+ public function __construct($indentation = 4)
+ {
+ if ($indentation < 1) {
+ throw new \InvalidArgumentException('The indentation must be greater than zero.');
+ }
+
+ $this->indentation = $indentation;
+ }
+
+ /**
+ * Sets the indentation.
+ *
+ * @param int $num The amount of spaces to use for indentation of nested nodes
+ */
+ public function setIndentation($num)
+ {
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 3.1 and will be removed in 4.0. Pass the indentation to the constructor instead.', E_USER_DEPRECATED);
+
+ $this->indentation = (int) $num;
+ }
+
+ /**
+ * Dumps a PHP value to YAML.
+ *
+ * @param mixed $input The PHP value
+ * @param int $inline The level where you switch to inline YAML
+ * @param int $indent The level of indentation (used internally)
+ * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
+ *
+ * @return string The YAML representation of the PHP value
+ */
+ public function dump($input, $inline = 0, $indent = 0, $flags = 0)
+ {
+ if (is_bool($flags)) {
+ @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
+
+ if ($flags) {
+ $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
+ } else {
+ $flags = 0;
+ }
+ }
+
+ if (func_num_args() >= 5) {
+ @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(4)) {
+ $flags |= Yaml::DUMP_OBJECT;
+ }
+ }
+
+ $output = '';
+ $prefix = $indent ? str_repeat(' ', $indent) : '';
+
+ if ($inline <= 0 || !is_array($input) || empty($input)) {
+ $output .= $prefix.Inline::dump($input, $flags);
+ } else {
+ $isAHash = Inline::isHash($input);
+
+ foreach ($input as $key => $value) {
+ if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && is_string($value) && false !== strpos($value, "\n")) {
+ $output .= sprintf("%s%s%s |\n", $prefix, $isAHash ? Inline::dump($key, $flags).':' : '-', '');
+
+ foreach (preg_split('/\n|\r\n/', $value) as $row) {
+ $output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row);
+ }
+
+ continue;
+ }
+
+ $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value);
+
+ $output .= sprintf('%s%s%s%s',
+ $prefix,
+ $isAHash ? Inline::dump($key, $flags).':' : '-',
+ $willBeInlined ? ' ' : "\n",
+ $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
+ ).($willBeInlined ? "\n" : '');
+ }
+ }
+
+ return $output;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml;
+
+/**
+ * Escaper encapsulates escaping rules for single and double-quoted
+ * YAML strings.
+ *
+ * @author Matthew Lewinski <matthew@lewinski.org>
+ *
+ * @internal
+ */
+class Escaper
+{
+ // Characters that would cause a dumped string to require double quoting.
+ const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";
+
+ // Mapping arrays for escaping a double quoted string. The backslash is
+ // first to ensure proper escaping because str_replace operates iteratively
+ // on the input arrays. This ordering of the characters avoids the use of strtr,
+ // which performs more slowly.
+ private static $escapees = array('\\', '\\\\', '\\"', '"',
+ "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
+ "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
+ "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
+ "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
+ "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
+ );
+ private static $escaped = array('\\\\', '\\"', '\\\\', '\\"',
+ '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a',
+ '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f',
+ '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
+ '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f',
+ '\\N', '\\_', '\\L', '\\P',
+ );
+
+ /**
+ * Determines if a PHP value would require double quoting in YAML.
+ *
+ * @param string $value A PHP value
+ *
+ * @return bool True if the value would require double quotes
+ */
+ public static function requiresDoubleQuoting($value)
+ {
+ return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value);
+ }
+
+ /**
+ * Escapes and surrounds a PHP value with double quotes.
+ *
+ * @param string $value A PHP value
+ *
+ * @return string The quoted, escaped string
+ */
+ public static function escapeWithDoubleQuotes($value)
+ {
+ return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value));
+ }
+
+ /**
+ * Determines if a PHP value would require single quoting in YAML.
+ *
+ * @param string $value A PHP value
+ *
+ * @return bool True if the value would require single quotes
+ */
+ public static function requiresSingleQuoting($value)
+ {
+ // Determines if a PHP value is entirely composed of a value that would
+ // require single quoting in YAML.
+ if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) {
+ return true;
+ }
+
+ // Determines if the PHP value contains any single characters that would
+ // cause it to require single quoting in YAML.
+ return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value);
+ }
+
+ /**
+ * Escapes and surrounds a PHP value with single quotes.
+ *
+ * @param string $value A PHP value
+ *
+ * @return string The quoted, escaped string
+ */
+ public static function escapeWithSingleQuotes($value)
+ {
+ return sprintf("'%s'", str_replace('\'', '\'\'', $value));
+ }
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml\Exception;
+
+/**
+ * Exception class thrown when an error occurs during dumping.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class DumpException extends RuntimeException
+{
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml\Exception;
+
+/**
+ * Exception interface for all exceptions thrown by the component.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface ExceptionInterface
+{
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml\Exception;
+
+/**
+ * Exception class thrown when an error occurs during parsing.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ParseException extends RuntimeException
+{
+ private $parsedFile;
+ private $parsedLine;
+ private $snippet;
+ private $rawMessage;
+
+ /**
+ * Constructor.
+ *
+ * @param string $message The error message
+ * @param int $parsedLine The line where the error occurred
+ * @param string|null $snippet The snippet of code near the problem
+ * @param string|null $parsedFile The file name where the error occurred
+ * @param \Exception|null $previous The previous exception
+ */
+ public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null)
+ {
+ $this->parsedFile = $parsedFile;
+ $this->parsedLine = $parsedLine;
+ $this->snippet = $snippet;
+ $this->rawMessage = $message;
+
+ $this->updateRepr();
+
+ parent::__construct($this->message, 0, $previous);
+ }
+
+ /**
+ * Gets the snippet of code near the error.
+ *
+ * @return string The snippet of code
+ */
+ public function getSnippet()
+ {
+ return $this->snippet;
+ }
+
+ /**
+ * Sets the snippet of code near the error.
+ *
+ * @param string $snippet The code snippet
+ */
+ public function setSnippet($snippet)
+ {
+ $this->snippet = $snippet;
+
+ $this->updateRepr();
+ }
+
+ /**
+ * Gets the filename where the error occurred.
+ *
+ * This method returns null if a string is parsed.
+ *
+ * @return string The filename
+ */
+ public function getParsedFile()
+ {
+ return $this->parsedFile;
+ }
+
+ /**
+ * Sets the filename where the error occurred.
+ *
+ * @param string $parsedFile The filename
+ */
+ public function setParsedFile($parsedFile)
+ {
+ $this->parsedFile = $parsedFile;
+
+ $this->updateRepr();
+ }
+
+ /**
+ * Gets the line where the error occurred.
+ *
+ * @return int The file line
+ */
+ public function getParsedLine()
+ {
+ return $this->parsedLine;
+ }
+
+ /**
+ * Sets the line where the error occurred.
+ *
+ * @param int $parsedLine The file line
+ */
+ public function setParsedLine($parsedLine)
+ {
+ $this->parsedLine = $parsedLine;
+
+ $this->updateRepr();
+ }
+
+ private function updateRepr()
+ {
+ $this->message = $this->rawMessage;
+
+ $dot = false;
+ if ('.' === substr($this->message, -1)) {
+ $this->message = substr($this->message, 0, -1);
+ $dot = true;
+ }
+
+ if (null !== $this->parsedFile) {
+ $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
+ }
+
+ if ($this->parsedLine >= 0) {
+ $this->message .= sprintf(' at line %d', $this->parsedLine);
+ }
+
+ if ($this->snippet) {
+ $this->message .= sprintf(' (near "%s")', $this->snippet);
+ }
+
+ if ($dot) {
+ $this->message .= '.';
+ }
+ }
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml\Exception;
+
+/**
+ * Exception class thrown when an error occurs during parsing.
+ *
+ * @author Romain Neutron <imprec@gmail.com>
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml;
+
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Exception\DumpException;
+
+/**
+ * Inline implements a YAML parser/dumper for the YAML inline syntax.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @internal
+ */
+class Inline
+{
+ const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
+
+ public static $parsedLineNumber;
+
+ private static $exceptionOnInvalidType = false;
+ private static $objectSupport = false;
+ private static $objectForMap = false;
+ private static $constantSupport = false;
+
+ /**
+ * Converts a YAML string to a PHP value.
+ *
+ * @param string $value A YAML string
+ * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
+ * @param array $references Mapping of variable names to values
+ *
+ * @return mixed A PHP value
+ *
+ * @throws ParseException
+ */
+ public static function parse($value, $flags = 0, $references = array())
+ {
+ if (is_bool($flags)) {
+ @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
+
+ if ($flags) {
+ $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
+ } else {
+ $flags = 0;
+ }
+ }
+
+ if (func_num_args() >= 3 && !is_array($references)) {
+ @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
+
+ if ($references) {
+ $flags |= Yaml::PARSE_OBJECT;
+ }
+
+ if (func_num_args() >= 4) {
+ @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(3)) {
+ $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
+ }
+ }
+
+ if (func_num_args() >= 5) {
+ $references = func_get_arg(4);
+ } else {
+ $references = array();
+ }
+ }
+
+ self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
+ self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
+ self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
+ self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);
+
+ $value = trim($value);
+
+ if ('' === $value) {
+ return '';
+ }
+
+ if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
+ $mbEncoding = mb_internal_encoding();
+ mb_internal_encoding('ASCII');
+ }
+
+ $i = 0;
+ switch ($value[0]) {
+ case '[':
+ $result = self::parseSequence($value, $flags, $i, $references);
+ ++$i;
+ break;
+ case '{':
+ $result = self::parseMapping($value, $flags, $i, $references);
+ ++$i;
+ break;
+ default:
+ $result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references);
+ }
+
+ // some comments are allowed at the end
+ if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
+ throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
+ }
+
+ if (isset($mbEncoding)) {
+ mb_internal_encoding($mbEncoding);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Dumps a given PHP variable to a YAML string.
+ *
+ * @param mixed $value The PHP variable to convert
+ * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
+ *
+ * @return string The YAML string representing the PHP value
+ *
+ * @throws DumpException When trying to dump PHP resource
+ */
+ public static function dump($value, $flags = 0)
+ {
+ if (is_bool($flags)) {
+ @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
+
+ if ($flags) {
+ $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
+ } else {
+ $flags = 0;
+ }
+ }
+
+ if (func_num_args() >= 3) {
+ @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(2)) {
+ $flags |= Yaml::DUMP_OBJECT;
+ }
+ }
+
+ switch (true) {
+ case is_resource($value):
+ if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
+ throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
+ }
+
+ return 'null';
+ case $value instanceof \DateTimeInterface:
+ return $value->format('c');
+ case is_object($value):
+ if (Yaml::DUMP_OBJECT & $flags) {
+ return '!php/object:'.serialize($value);
+ }
+
+ if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
+ return self::dumpArray((array) $value, $flags);
+ }
+
+ if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) {
+ throw new DumpException('Object support when dumping a YAML file has been disabled.');
+ }
+
+ return 'null';
+ case is_array($value):
+ return self::dumpArray($value, $flags);
+ case null === $value:
+ return 'null';
+ case true === $value:
+ return 'true';
+ case false === $value:
+ return 'false';
+ case ctype_digit($value):
+ return is_string($value) ? "'$value'" : (int) $value;
+ case is_numeric($value):
+ $locale = setlocale(LC_NUMERIC, 0);
+ if (false !== $locale) {
+ setlocale(LC_NUMERIC, 'C');
+ }
+ if (is_float($value)) {
+ $repr = (string) $value;
+ if (is_infinite($value)) {
+ $repr = str_ireplace('INF', '.Inf', $repr);
+ } elseif (floor($value) == $value && $repr == $value) {
+ // Preserve float data type since storing a whole number will result in integer value.
+ $repr = '!!float '.$repr;
+ }
+ } else {
+ $repr = is_string($value) ? "'$value'" : (string) $value;
+ }
+ if (false !== $locale) {
+ setlocale(LC_NUMERIC, $locale);
+ }
+
+ return $repr;
+ case '' == $value:
+ return "''";
+ case self::isBinaryString($value):
+ return '!!binary '.base64_encode($value);
+ case Escaper::requiresDoubleQuoting($value):
+ return Escaper::escapeWithDoubleQuotes($value);
+ case Escaper::requiresSingleQuoting($value):
+ case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value):
+ case Parser::preg_match(self::getHexRegex(), $value):
+ case Parser::preg_match(self::getTimestampRegex(), $value):
+ return Escaper::escapeWithSingleQuotes($value);
+ default:
+ return $value;
+ }
+ }
+
+ /**
+ * Check if given array is hash or just normal indexed array.
+ *
+ * @internal
+ *
+ * @param array $value The PHP array to check
+ *
+ * @return bool true if value is hash array, false otherwise
+ */
+ public static function isHash(array $value)
+ {
+ $expectedKey = 0;
+
+ foreach ($value as $key => $val) {
+ if ($key !== $expectedKey++) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Dumps a PHP array to a YAML string.
+ *
+ * @param array $value The PHP array to dump
+ * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
+ *
+ * @return string The YAML string representing the PHP array
+ */
+ private static function dumpArray($value, $flags)
+ {
+ // array
+ if ($value && !self::isHash($value)) {
+ $output = array();
+ foreach ($value as $val) {
+ $output[] = self::dump($val, $flags);
+ }
+
+ return sprintf('[%s]', implode(', ', $output));
+ }
+
+ // hash
+ $output = array();
+ foreach ($value as $key => $val) {
+ $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
+ }
+
+ return sprintf('{ %s }', implode(', ', $output));
+ }
+
+ /**
+ * Parses a YAML scalar.
+ *
+ * @param string $scalar
+ * @param int $flags
+ * @param string[] $delimiters
+ * @param string[] $stringDelimiters
+ * @param int &$i
+ * @param bool $evaluate
+ * @param array $references
+ *
+ * @return string
+ *
+ * @throws ParseException When malformed inline YAML string is parsed
+ *
+ * @internal
+ */
+ public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
+ {
+ if (in_array($scalar[$i], $stringDelimiters)) {
+ // quoted scalar
+ $output = self::parseQuotedScalar($scalar, $i);
+
+ if (null !== $delimiters) {
+ $tmp = ltrim(substr($scalar, $i), ' ');
+ if (!in_array($tmp[0], $delimiters)) {
+ throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
+ }
+ }
+ } else {
+ // "normal" string
+ if (!$delimiters) {
+ $output = substr($scalar, $i);
+ $i += strlen($output);
+
+ // remove comments
+ if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
+ $output = substr($output, 0, $match[0][1]);
+ }
+ } elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
+ $output = $match[1];
+ $i += strlen($output);
+ } else {
+ throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
+ }
+
+ // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
+ if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
+ throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
+ }
+
+ if ($output && '%' === $output[0]) {
+ @trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output), E_USER_DEPRECATED);
+ }
+
+ if ($evaluate) {
+ $output = self::evaluateScalar($output, $flags, $references);
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Parses a YAML quoted scalar.
+ *
+ * @param string $scalar
+ * @param int &$i
+ *
+ * @return string
+ *
+ * @throws ParseException When malformed inline YAML string is parsed
+ */
+ private static function parseQuotedScalar($scalar, &$i)
+ {
+ if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
+ throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
+ }
+
+ $output = substr($match[0], 1, strlen($match[0]) - 2);
+
+ $unescaper = new Unescaper();
+ if ('"' == $scalar[$i]) {
+ $output = $unescaper->unescapeDoubleQuotedString($output);
+ } else {
+ $output = $unescaper->unescapeSingleQuotedString($output);
+ }
+
+ $i += strlen($match[0]);
+
+ return $output;
+ }
+
+ /**
+ * Parses a YAML sequence.
+ *
+ * @param string $sequence
+ * @param int $flags
+ * @param int &$i
+ * @param array $references
+ *
+ * @return array
+ *
+ * @throws ParseException When malformed inline YAML string is parsed
+ */
+ private static function parseSequence($sequence, $flags, &$i = 0, $references = array())
+ {
+ $output = array();
+ $len = strlen($sequence);
+ ++$i;
+
+ // [foo, bar, ...]
+ while ($i < $len) {
+ switch ($sequence[$i]) {
+ case '[':
+ // nested sequence
+ $output[] = self::parseSequence($sequence, $flags, $i, $references);
+ break;
+ case '{':
+ // nested mapping
+ $output[] = self::parseMapping($sequence, $flags, $i, $references);
+ break;
+ case ']':
+ return $output;
+ case ',':
+ case ' ':
+ break;
+ default:
+ $isQuoted = in_array($sequence[$i], array('"', "'"));
+ $value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references);
+
+ // the value can be an array if a reference has been resolved to an array var
+ if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
+ // embedded mapping?
+ try {
+ $pos = 0;
+ $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references);
+ } catch (\InvalidArgumentException $e) {
+ // no, it's not
+ }
+ }
+
+ $output[] = $value;
+
+ --$i;
+ }
+
+ ++$i;
+ }
+
+ throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
+ }
+
+ /**
+ * Parses a YAML mapping.
+ *
+ * @param string $mapping
+ * @param int $flags
+ * @param int &$i
+ * @param array $references
+ *
+ * @return array|\stdClass
+ *
+ * @throws ParseException When malformed inline YAML string is parsed
+ */
+ private static function parseMapping($mapping, $flags, &$i = 0, $references = array())
+ {
+ $output = array();
+ $len = strlen($mapping);
+ ++$i;
+
+ // {foo: bar, bar:foo, ...}
+ while ($i < $len) {
+ switch ($mapping[$i]) {
+ case ' ':
+ case ',':
+ ++$i;
+ continue 2;
+ case '}':
+ if (self::$objectForMap) {
+ return (object) $output;
+ }
+
+ return $output;
+ }
+
+ // key
+ $key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
+
+ if (':' !== $key && false === $i = strpos($mapping, ':', $i)) {
+ break;
+ }
+
+ if (':' !== $key && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) {
+ @trigger_error('Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
+ }
+
+ // value
+ $done = false;
+
+ while ($i < $len) {
+ switch ($mapping[$i]) {
+ case '[':
+ // nested sequence
+ $value = self::parseSequence($mapping, $flags, $i, $references);
+ // Spec: Keys MUST be unique; first one wins.
+ // Parser cannot abort this mapping earlier, since lines
+ // are processed sequentially.
+ if (!isset($output[$key])) {
+ $output[$key] = $value;
+ } else {
+ @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
+ }
+ $done = true;
+ break;
+ case '{':
+ // nested mapping
+ $value = self::parseMapping($mapping, $flags, $i, $references);
+ // Spec: Keys MUST be unique; first one wins.
+ // Parser cannot abort this mapping earlier, since lines
+ // are processed sequentially.
+ if (!isset($output[$key])) {
+ $output[$key] = $value;
+ } else {
+ @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
+ }
+ $done = true;
+ break;
+ case ':':
+ case ' ':
+ break;
+ default:
+ $value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
+ // Spec: Keys MUST be unique; first one wins.
+ // Parser cannot abort this mapping earlier, since lines
+ // are processed sequentially.
+ if (!isset($output[$key])) {
+ $output[$key] = $value;
+ } else {
+ @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED);
+ }
+ $done = true;
+ --$i;
+ }
+
+ ++$i;
+
+ if ($done) {
+ continue 2;
+ }
+ }
+ }
+
+ throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
+ }
+
+ /**
+ * Evaluates scalars and replaces magic values.
+ *
+ * @param string $scalar
+ * @param int $flags
+ * @param array $references
+ *
+ * @return mixed The evaluated YAML string
+ *
+ * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
+ */
+ private static function evaluateScalar($scalar, $flags, $references = array())
+ {
+ $scalar = trim($scalar);
+ $scalarLower = strtolower($scalar);
+
+ if (0 === strpos($scalar, '*')) {
+ if (false !== $pos = strpos($scalar, '#')) {
+ $value = substr($scalar, 1, $pos - 2);
+ } else {
+ $value = substr($scalar, 1);
+ }
+
+ // an unquoted *
+ if (false === $value || '' === $value) {
+ throw new ParseException('A reference must contain at least one character.');
+ }
+
+ if (!array_key_exists($value, $references)) {
+ throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
+ }
+
+ return $references[$value];
+ }
+
+ switch (true) {
+ case 'null' === $scalarLower:
+ case '' === $scalar:
+ case '~' === $scalar:
+ return;
+ case 'true' === $scalarLower:
+ return true;
+ case 'false' === $scalarLower:
+ return false;
+ // Optimise for returning strings.
+ case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
+ switch (true) {
+ case 0 === strpos($scalar, '!str'):
+ return (string) substr($scalar, 5);
+ case 0 === strpos($scalar, '! '):
+ return (int) self::parseScalar(substr($scalar, 2), $flags);
+ case 0 === strpos($scalar, '!php/object:'):
+ if (self::$objectSupport) {
+ return unserialize(substr($scalar, 12));
+ }
+
+ if (self::$exceptionOnInvalidType) {
+ throw new ParseException('Object support when parsing a YAML file has been disabled.');
+ }
+
+ return;
+ case 0 === strpos($scalar, '!!php/object:'):
+ if (self::$objectSupport) {
+ @trigger_error('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead.', E_USER_DEPRECATED);
+
+ return unserialize(substr($scalar, 13));
+ }
+
+ if (self::$exceptionOnInvalidType) {
+ throw new ParseException('Object support when parsing a YAML file has been disabled.');
+ }
+
+ return;
+ case 0 === strpos($scalar, '!php/const:'):
+ if (self::$constantSupport) {
+ if (defined($const = substr($scalar, 11))) {
+ return constant($const);
+ }
+
+ throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
+ }
+ if (self::$exceptionOnInvalidType) {
+ throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar));
+ }
+
+ return;
+ case 0 === strpos($scalar, '!!float '):
+ return (float) substr($scalar, 8);
+ case Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
+ $scalar = str_replace('_', '', (string) $scalar);
+ // omitting the break / return as integers are handled in the next case
+ case ctype_digit($scalar):
+ $raw = $scalar;
+ $cast = (int) $scalar;
+
+ return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
+ case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
+ $raw = $scalar;
+ $cast = (int) $scalar;
+
+ return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
+ case is_numeric($scalar):
+ case Parser::preg_match(self::getHexRegex(), $scalar):
+ $scalar = str_replace('_', '', $scalar);
+
+ return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
+ case '.inf' === $scalarLower:
+ case '.nan' === $scalarLower:
+ return -log(0);
+ case '-.inf' === $scalarLower:
+ return log(0);
+ case 0 === strpos($scalar, '!!binary '):
+ return self::evaluateBinaryScalar(substr($scalar, 9));
+ case Parser::preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
+ case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
+ if (false !== strpos($scalar, ',')) {
+ @trigger_error('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0.', E_USER_DEPRECATED);
+ }
+
+ return (float) str_replace(array(',', '_'), '', $scalar);
+ case Parser::preg_match(self::getTimestampRegex(), $scalar):
+ if (Yaml::PARSE_DATETIME & $flags) {
+ // When no timezone is provided in the parsed date, YAML spec says we must assume UTC.
+ return new \DateTime($scalar, new \DateTimeZone('UTC'));
+ }
+
+ $timeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $time = strtotime($scalar);
+ date_default_timezone_set($timeZone);
+
+ return $time;
+ }
+ default:
+ return (string) $scalar;
+ }
+ }
+
+ /**
+ * @param string $scalar
+ *
+ * @return string
+ *
+ * @internal
+ */
+ public static function evaluateBinaryScalar($scalar)
+ {
+ $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
+
+ if (0 !== (strlen($parsedBinaryData) % 4)) {
+ throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData)));
+ }
+
+ if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
+ throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
+ }
+
+ return base64_decode($parsedBinaryData, true);
+ }
+
+ private static function isBinaryString($value)
+ {
+ return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value);
+ }
+
+ /**
+ * Gets a regex that matches a YAML date.
+ *
+ * @return string The regular expression
+ *
+ * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
+ */
+ private static function getTimestampRegex()
+ {
+ return <<<EOF
+ ~^
+ (?P<year>[0-9][0-9][0-9][0-9])
+ -(?P<month>[0-9][0-9]?)
+ -(?P<day>[0-9][0-9]?)
+ (?:(?:[Tt]|[ \t]+)
+ (?P<hour>[0-9][0-9]?)
+ :(?P<minute>[0-9][0-9])
+ :(?P<second>[0-9][0-9])
+ (?:\.(?P<fraction>[0-9]*))?
+ (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
+ (?::(?P<tz_minute>[0-9][0-9]))?))?)?
+ $~x
+EOF;
+ }
+
+ /**
+ * Gets a regex that matches a YAML number in hexadecimal notation.
+ *
+ * @return string
+ */
+ private static function getHexRegex()
+ {
+ return '~^0x[0-9a-f_]++$~i';
+ }
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml;
+
+use Symfony\Component\Yaml\Exception\ParseException;
+
+/**
+ * Parser parses YAML strings to convert them to PHP arrays.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Parser
+{
+ const TAG_PATTERN = '((?P<tag>![\w!.\/:-]+) +)?';
+ const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
+
+ private $offset = 0;
+ private $totalNumberOfLines;
+ private $lines = array();
+ private $currentLineNb = -1;
+ private $currentLine = '';
+ private $refs = array();
+ private $skippedLineNumbers = array();
+ private $locallySkippedLineNumbers = array();
+
+ /**
+ * Constructor.
+ *
+ * @param int $offset The offset of YAML document (used for line numbers in error messages)
+ * @param int|null $totalNumberOfLines The overall number of lines being parsed
+ * @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser
+ */
+ public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array())
+ {
+ $this->offset = $offset;
+ $this->totalNumberOfLines = $totalNumberOfLines;
+ $this->skippedLineNumbers = $skippedLineNumbers;
+ }
+
+ /**
+ * Parses a YAML string to a PHP value.
+ *
+ * @param string $value A YAML string
+ * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
+ *
+ * @return mixed A PHP value
+ *
+ * @throws ParseException If the YAML is not valid
+ */
+ public function parse($value, $flags = 0)
+ {
+ if (is_bool($flags)) {
+ @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
+
+ if ($flags) {
+ $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
+ } else {
+ $flags = 0;
+ }
+ }
+
+ if (func_num_args() >= 3) {
+ @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(2)) {
+ $flags |= Yaml::PARSE_OBJECT;
+ }
+ }
+
+ if (func_num_args() >= 4) {
+ @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(3)) {
+ $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
+ }
+ }
+
+ if (false === preg_match('//u', $value)) {
+ throw new ParseException('The YAML value does not appear to be valid UTF-8.');
+ }
+
+ $this->refs = array();
+
+ $mbEncoding = null;
+ $e = null;
+ $data = null;
+
+ if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
+ $mbEncoding = mb_internal_encoding();
+ mb_internal_encoding('UTF-8');
+ }
+
+ try {
+ $data = $this->doParse($value, $flags);
+ } catch (\Exception $e) {
+ } catch (\Throwable $e) {
+ }
+
+ if (null !== $mbEncoding) {
+ mb_internal_encoding($mbEncoding);
+ }
+
+ $this->lines = array();
+ $this->currentLine = '';
+ $this->refs = array();
+ $this->skippedLineNumbers = array();
+ $this->locallySkippedLineNumbers = array();
+
+ if (null !== $e) {
+ throw $e;
+ }
+
+ return $data;
+ }
+
+ private function doParse($value, $flags)
+ {
+ $this->currentLineNb = -1;
+ $this->currentLine = '';
+ $value = $this->cleanup($value);
+ $this->lines = explode("\n", $value);
+ $this->locallySkippedLineNumbers = array();
+
+ if (null === $this->totalNumberOfLines) {
+ $this->totalNumberOfLines = count($this->lines);
+ }
+
+ $data = array();
+ $context = null;
+ $allowOverwrite = false;
+
+ while ($this->moveToNextLine()) {
+ if ($this->isCurrentLineEmpty()) {
+ continue;
+ }
+
+ // tab?
+ if ("\t" === $this->currentLine[0]) {
+ throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+
+ $isRef = $mergeNode = false;
+ if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) {
+ if ($context && 'mapping' == $context) {
+ throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+ $context = 'sequence';
+
+ if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
+ $isRef = $matches['ref'];
+ $values['value'] = $matches['value'];
+ }
+
+ // array
+ if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
+ $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags);
+ } else {
+ if (isset($values['leadspaces'])
+ && self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($values['value']), $matches)
+ ) {
+ // this is a compact notation element, add to next block and parse
+ $block = $values['value'];
+ if ($this->isNextLineIndented()) {
+ $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
+ }
+
+ $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags);
+ } else {
+ $data[] = $this->parseValue($values['value'], $flags, $context);
+ }
+ }
+ if ($isRef) {
+ $this->refs[$isRef] = end($data);
+ }
+ } elseif (
+ self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
+ && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))
+ ) {
+ if ($context && 'sequence' == $context) {
+ throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
+ }
+ $context = 'mapping';
+
+ // force correct settings
+ Inline::parse(null, $flags, $this->refs);
+ try {
+ Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
+ $key = Inline::parseScalar($values['key']);
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+
+ // Convert float keys to strings, to avoid being converted to integers by PHP
+ if (is_float($key)) {
+ $key = (string) $key;
+ }
+
+ if ('<<' === $key) {
+ $mergeNode = true;
+ $allowOverwrite = true;
+ if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
+ $refName = substr($values['value'], 1);
+ if (!array_key_exists($refName, $this->refs)) {
+ throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+
+ $refValue = $this->refs[$refName];
+
+ if (!is_array($refValue)) {
+ throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+
+ $data += $refValue; // array union
+ } else {
+ if (isset($values['value']) && $values['value'] !== '') {
+ $value = $values['value'];
+ } else {
+ $value = $this->getNextEmbedBlock();
+ }
+ $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
+
+ if (!is_array($parsed)) {
+ throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+
+ if (isset($parsed[0])) {
+ // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes
+ // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier
+ // in the sequence override keys specified in later mapping nodes.
+ foreach ($parsed as $parsedItem) {
+ if (!is_array($parsedItem)) {
+ throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
+ }
+
+ $data += $parsedItem; // array union
+ }
+ } else {
+ // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the
+ // current mapping, unless the key already exists in it.
+ $data += $parsed; // array union
+ }
+ }
+ } elseif (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
+ $isRef = $matches['ref'];
+ $values['value'] = $matches['value'];
+ }
+
+ if ($mergeNode) {
+ // Merge keys
+ } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) {
+ // hash
+ // if next line is less indented or equal, then it means that the current value is null
+ if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) {
+ // Spec: Keys MUST be unique; first one wins.
+ // But overwriting is allowed when a merge node is used in current block.
+ if ($allowOverwrite || !isset($data[$key])) {
+ $data[$key] = null;
+ } else {
+ @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
+ }
+ } else {
+ // remember the parsed line number here in case we need it to provide some contexts in error messages below
+ $realCurrentLineNbKey = $this->getRealCurrentLineNb();
+ $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
+ // Spec: Keys MUST be unique; first one wins.
+ // But overwriting is allowed when a merge node is used in current block.
+ if ($allowOverwrite || !isset($data[$key])) {
+ $data[$key] = $value;
+ } else {
+ @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $realCurrentLineNbKey + 1), E_USER_DEPRECATED);
+ }
+ }
+ } else {
+ $value = $this->parseValue($values['value'], $flags, $context);
+ // Spec: Keys MUST be unique; first one wins.
+ // But overwriting is allowed when a merge node is used in current block.
+ if ($allowOverwrite || !isset($data[$key])) {
+ $data[$key] = $value;
+ } else {
+ @trigger_error(sprintf('Duplicate key "%s" detected on line %d whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
+ }
+ }
+ if ($isRef) {
+ $this->refs[$isRef] = $data[$key];
+ }
+ } else {
+ // multiple documents are not supported
+ if ('---' === $this->currentLine) {
+ throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine);
+ }
+
+ // 1-liner optionally followed by newline(s)
+ if (is_string($value) && $this->lines[0] === trim($value)) {
+ try {
+ Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
+ $value = Inline::parse($this->lines[0], $flags, $this->refs);
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+
+ return $value;
+ }
+
+ throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+ }
+
+ if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) {
+ $object = new \stdClass();
+
+ foreach ($data as $key => $value) {
+ $object->$key = $value;
+ }
+
+ $data = $object;
+ }
+
+ return empty($data) ? null : $data;
+ }
+
+ private function parseBlock($offset, $yaml, $flags)
+ {
+ $skippedLineNumbers = $this->skippedLineNumbers;
+
+ foreach ($this->locallySkippedLineNumbers as $lineNumber) {
+ if ($lineNumber < $offset) {
+ continue;
+ }
+
+ $skippedLineNumbers[] = $lineNumber;
+ }
+
+ $parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers);
+ $parser->refs = &$this->refs;
+
+ return $parser->doParse($yaml, $flags);
+ }
+
+ /**
+ * Returns the current line number (takes the offset into account).
+ *
+ * @return int The current line number
+ */
+ private function getRealCurrentLineNb()
+ {
+ $realCurrentLineNumber = $this->currentLineNb + $this->offset;
+
+ foreach ($this->skippedLineNumbers as $skippedLineNumber) {
+ if ($skippedLineNumber > $realCurrentLineNumber) {
+ break;
+ }
+
+ ++$realCurrentLineNumber;
+ }
+
+ return $realCurrentLineNumber;
+ }
+
+ /**
+ * Returns the current line indentation.
+ *
+ * @return int The current line indentation
+ */
+ private function getCurrentLineIndentation()
+ {
+ return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' '));
+ }
+
+ /**
+ * Returns the next embed block of YAML.
+ *
+ * @param int $indentation The indent level at which the block is to be read, or null for default
+ * @param bool $inSequence True if the enclosing data structure is a sequence
+ *
+ * @return string A YAML string
+ *
+ * @throws ParseException When indentation problem are detected
+ */
+ private function getNextEmbedBlock($indentation = null, $inSequence = false)
+ {
+ $oldLineIndentation = $this->getCurrentLineIndentation();
+ $blockScalarIndentations = array();
+
+ if ($this->isBlockScalarHeader()) {
+ $blockScalarIndentations[] = $this->getCurrentLineIndentation();
+ }
+
+ if (!$this->moveToNextLine()) {
+ return;
+ }
+
+ if (null === $indentation) {
+ $newIndent = $this->getCurrentLineIndentation();
+
+ $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem();
+
+ if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
+ throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+ } else {
+ $newIndent = $indentation;
+ }
+
+ $data = array();
+ if ($this->getCurrentLineIndentation() >= $newIndent) {
+ $data[] = substr($this->currentLine, $newIndent);
+ } else {
+ $this->moveToPreviousLine();
+
+ return;
+ }
+
+ if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) {
+ // the previous line contained a dash but no item content, this line is a sequence item with the same indentation
+ // and therefore no nested list or mapping
+ $this->moveToPreviousLine();
+
+ return;
+ }
+
+ $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem();
+
+ if (empty($blockScalarIndentations) && $this->isBlockScalarHeader()) {
+ $blockScalarIndentations[] = $this->getCurrentLineIndentation();
+ }
+
+ $previousLineIndentation = $this->getCurrentLineIndentation();
+
+ while ($this->moveToNextLine()) {
+ $indent = $this->getCurrentLineIndentation();
+
+ // terminate all block scalars that are more indented than the current line
+ if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && trim($this->currentLine) !== '') {
+ foreach ($blockScalarIndentations as $key => $blockScalarIndentation) {
+ if ($blockScalarIndentation >= $this->getCurrentLineIndentation()) {
+ unset($blockScalarIndentations[$key]);
+ }
+ }
+ }
+
+ if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) {
+ $blockScalarIndentations[] = $this->getCurrentLineIndentation();
+ }
+
+ $previousLineIndentation = $indent;
+
+ if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) {
+ $this->moveToPreviousLine();
+ break;
+ }
+
+ if ($this->isCurrentLineBlank()) {
+ $data[] = substr($this->currentLine, $newIndent);
+ continue;
+ }
+
+ // we ignore "comment" lines only when we are not inside a scalar block
+ if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) {
+ // remember ignored comment lines (they are used later in nested
+ // parser calls to determine real line numbers)
+ //
+ // CAUTION: beware to not populate the global property here as it
+ // will otherwise influence the getRealCurrentLineNb() call here
+ // for consecutive comment lines and subsequent embedded blocks
+ $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb();
+
+ continue;
+ }
+
+ if ($indent >= $newIndent) {
+ $data[] = substr($this->currentLine, $newIndent);
+ } elseif (0 == $indent) {
+ $this->moveToPreviousLine();
+
+ break;
+ } else {
+ throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
+ }
+ }
+
+ return implode("\n", $data);
+ }
+
+ /**
+ * Moves the parser to the next line.
+ *
+ * @return bool
+ */
+ private function moveToNextLine()
+ {
+ if ($this->currentLineNb >= count($this->lines) - 1) {
+ return false;
+ }
+
+ $this->currentLine = $this->lines[++$this->currentLineNb];
+
+ return true;
+ }
+
+ /**
+ * Moves the parser to the previous line.
+ *
+ * @return bool
+ */
+ private function moveToPreviousLine()
+ {
+ if ($this->currentLineNb < 1) {
+ return false;
+ }
+
+ $this->currentLine = $this->lines[--$this->currentLineNb];
+
+ return true;
+ }
+
+ /**
+ * Parses a YAML value.
+ *
+ * @param string $value A YAML value
+ * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
+ * @param string $context The parser context (either sequence or mapping)
+ *
+ * @return mixed A PHP value
+ *
+ * @throws ParseException When reference does not exist
+ */
+ private function parseValue($value, $flags, $context)
+ {
+ if (0 === strpos($value, '*')) {
+ if (false !== $pos = strpos($value, '#')) {
+ $value = substr($value, 1, $pos - 2);
+ } else {
+ $value = substr($value, 1);
+ }
+
+ if (!array_key_exists($value, $this->refs)) {
+ throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine);
+ }
+
+ return $this->refs[$value];
+ }
+
+ if (self::preg_match('/^'.self::TAG_PATTERN.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
+ $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
+
+ $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers));
+
+ if (isset($matches['tag']) && '!!binary' === $matches['tag']) {
+ return Inline::evaluateBinaryScalar($data);
+ }
+
+ return $data;
+ }
+
+ try {
+ $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null;
+
+ // do not take following lines into account when the current line is a quoted single line value
+ if (null !== $quotation && preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) {
+ return Inline::parse($value, $flags, $this->refs);
+ }
+
+ while ($this->moveToNextLine()) {
+ // unquoted strings end before the first unindented line
+ if (null === $quotation && $this->getCurrentLineIndentation() === 0) {
+ $this->moveToPreviousLine();
+
+ break;
+ }
+
+ $value .= ' '.trim($this->currentLine);
+
+ // quoted string values end with a line that is terminated with the quotation character
+ if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) {
+ break;
+ }
+ }
+
+ Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
+ $parsedValue = Inline::parse($value, $flags, $this->refs);
+
+ if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
+ throw new ParseException('A colon cannot be used in an unquoted mapping value.');
+ }
+
+ return $parsedValue;
+ } catch (ParseException $e) {
+ $e->setParsedLine($this->getRealCurrentLineNb() + 1);
+ $e->setSnippet($this->currentLine);
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Parses a block scalar.
+ *
+ * @param string $style The style indicator that was used to begin this block scalar (| or >)
+ * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -)
+ * @param int $indentation The indentation indicator that was used to begin this block scalar
+ *
+ * @return string The text value
+ */
+ private function parseBlockScalar($style, $chomping = '', $indentation = 0)
+ {
+ $notEOF = $this->moveToNextLine();
+ if (!$notEOF) {
+ return '';
+ }
+
+ $isCurrentLineBlank = $this->isCurrentLineBlank();
+ $blockLines = array();
+
+ // leading blank lines are consumed before determining indentation
+ while ($notEOF && $isCurrentLineBlank) {
+ // newline only if not EOF
+ if ($notEOF = $this->moveToNextLine()) {
+ $blockLines[] = '';
+ $isCurrentLineBlank = $this->isCurrentLineBlank();
+ }
+ }
+
+ // determine indentation if not specified
+ if (0 === $indentation) {
+ if (self::preg_match('/^ +/', $this->currentLine, $matches)) {
+ $indentation = strlen($matches[0]);
+ }
+ }
+
+ if ($indentation > 0) {
+ $pattern = sprintf('/^ {%d}(.*)$/', $indentation);
+
+ while (
+ $notEOF && (
+ $isCurrentLineBlank ||
+ self::preg_match($pattern, $this->currentLine, $matches)
+ )
+ ) {
+ if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) {
+ $blockLines[] = substr($this->currentLine, $indentation);
+ } elseif ($isCurrentLineBlank) {
+ $blockLines[] = '';
+ } else {
+ $blockLines[] = $matches[1];
+ }
+
+ // newline only if not EOF
+ if ($notEOF = $this->moveToNextLine()) {
+ $isCurrentLineBlank = $this->isCurrentLineBlank();
+ }
+ }
+ } elseif ($notEOF) {
+ $blockLines[] = '';
+ }
+
+ if ($notEOF) {
+ $blockLines[] = '';
+ $this->moveToPreviousLine();
+ } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) {
+ $blockLines[] = '';
+ }
+
+ // folded style
+ if ('>' === $style) {
+ $text = '';
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+
+ for ($i = 0, $blockLinesCount = count($blockLines); $i < $blockLinesCount; ++$i) {
+ if ('' === $blockLines[$i]) {
+ $text .= "\n";
+ $previousLineIndented = false;
+ $previousLineBlank = true;
+ } elseif (' ' === $blockLines[$i][0]) {
+ $text .= "\n".$blockLines[$i];
+ $previousLineIndented = true;
+ $previousLineBlank = false;
+ } elseif ($previousLineIndented) {
+ $text .= "\n".$blockLines[$i];
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+ } elseif ($previousLineBlank || 0 === $i) {
+ $text .= $blockLines[$i];
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+ } else {
+ $text .= ' '.$blockLines[$i];
+ $previousLineIndented = false;
+ $previousLineBlank = false;
+ }
+ }
+ } else {
+ $text = implode("\n", $blockLines);
+ }
+
+ // deal with trailing newlines
+ if ('' === $chomping) {
+ $text = preg_replace('/\n+$/', "\n", $text);
+ } elseif ('-' === $chomping) {
+ $text = preg_replace('/\n+$/', '', $text);
+ }
+
+ return $text;
+ }
+
+ /**
+ * Returns true if the next line is indented.
+ *
+ * @return bool Returns true if the next line is indented, false otherwise
+ */
+ private function isNextLineIndented()
+ {
+ $currentIndentation = $this->getCurrentLineIndentation();
+ $EOF = !$this->moveToNextLine();
+
+ while (!$EOF && $this->isCurrentLineEmpty()) {
+ $EOF = !$this->moveToNextLine();
+ }
+
+ if ($EOF) {
+ return false;
+ }
+
+ $ret = $this->getCurrentLineIndentation() > $currentIndentation;
+
+ $this->moveToPreviousLine();
+
+ return $ret;
+ }
+
+ /**
+ * Returns true if the current line is blank or if it is a comment line.
+ *
+ * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise
+ */
+ private function isCurrentLineEmpty()
+ {
+ return $this->isCurrentLineBlank() || $this->isCurrentLineComment();
+ }
+
+ /**
+ * Returns true if the current line is blank.
+ *
+ * @return bool Returns true if the current line is blank, false otherwise
+ */
+ private function isCurrentLineBlank()
+ {
+ return '' == trim($this->currentLine, ' ');
+ }
+
+ /**
+ * Returns true if the current line is a comment line.
+ *
+ * @return bool Returns true if the current line is a comment line, false otherwise
+ */
+ private function isCurrentLineComment()
+ {
+ //checking explicitly the first char of the trim is faster than loops or strpos
+ $ltrimmedLine = ltrim($this->currentLine, ' ');
+
+ return '' !== $ltrimmedLine && $ltrimmedLine[0] === '#';
+ }
+
+ private function isCurrentLineLastLineInDocument()
+ {
+ return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1);
+ }
+
+ /**
+ * Cleanups a YAML string to be parsed.
+ *
+ * @param string $value The input YAML string
+ *
+ * @return string A cleaned up YAML string
+ */
+ private function cleanup($value)
+ {
+ $value = str_replace(array("\r\n", "\r"), "\n", $value);
+
+ // strip YAML header
+ $count = 0;
+ $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count);
+ $this->offset += $count;
+
+ // remove leading comments
+ $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count);
+ if ($count == 1) {
+ // items have been removed, update the offset
+ $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
+ $value = $trimmedValue;
+ }
+
+ // remove start of the document marker (---)
+ $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count);
+ if ($count == 1) {
+ // items have been removed, update the offset
+ $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n");
+ $value = $trimmedValue;
+
+ // remove end of the document marker (...)
+ $value = preg_replace('#\.\.\.\s*$#', '', $value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Returns true if the next line starts unindented collection.
+ *
+ * @return bool Returns true if the next line starts unindented collection, false otherwise
+ */
+ private function isNextLineUnIndentedCollection()
+ {
+ $currentIndentation = $this->getCurrentLineIndentation();
+ $notEOF = $this->moveToNextLine();
+
+ while ($notEOF && $this->isCurrentLineEmpty()) {
+ $notEOF = $this->moveToNextLine();
+ }
+
+ if (false === $notEOF) {
+ return false;
+ }
+
+ $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();
+
+ $this->moveToPreviousLine();
+
+ return $ret;
+ }
+
+ /**
+ * Returns true if the string is un-indented collection item.
+ *
+ * @return bool Returns true if the string is un-indented collection item, false otherwise
+ */
+ private function isStringUnIndentedCollectionItem()
+ {
+ return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- ');
+ }
+
+ /**
+ * Tests whether or not the current line is the header of a block scalar.
+ *
+ * @return bool
+ */
+ private function isBlockScalarHeader()
+ {
+ return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
+ }
+
+ /**
+ * A local wrapper for `preg_match` which will throw a ParseException if there
+ * is an internal error in the PCRE engine.
+ *
+ * This avoids us needing to check for "false" every time PCRE is used
+ * in the YAML engine
+ *
+ * @throws ParseException on a PCRE internal error
+ *
+ * @see preg_last_error()
+ *
+ * @internal
+ */
+ public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0)
+ {
+ if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) {
+ switch (preg_last_error()) {
+ case PREG_INTERNAL_ERROR:
+ $error = 'Internal PCRE error.';
+ break;
+ case PREG_BACKTRACK_LIMIT_ERROR:
+ $error = 'pcre.backtrack_limit reached.';
+ break;
+ case PREG_RECURSION_LIMIT_ERROR:
+ $error = 'pcre.recursion_limit reached.';
+ break;
+ case PREG_BAD_UTF8_ERROR:
+ $error = 'Malformed UTF-8 data.';
+ break;
+ case PREG_BAD_UTF8_OFFSET_ERROR:
+ $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
+ break;
+ default:
+ $error = 'Error.';
+ }
+
+ throw new ParseException($error);
+ }
+
+ return $ret;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml;
+
+use Symfony\Component\Yaml\Exception\ParseException;
+
+/**
+ * Unescaper encapsulates unescaping rules for single and double-quoted
+ * YAML strings.
+ *
+ * @author Matthew Lewinski <matthew@lewinski.org>
+ *
+ * @internal
+ */
+class Unescaper
+{
+ /**
+ * Regex fragment that matches an escaped character in a double quoted string.
+ */
+ const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';
+
+ /**
+ * Unescapes a single quoted string.
+ *
+ * @param string $value A single quoted string
+ *
+ * @return string The unescaped string
+ */
+ public function unescapeSingleQuotedString($value)
+ {
+ return str_replace('\'\'', '\'', $value);
+ }
+
+ /**
+ * Unescapes a double quoted string.
+ *
+ * @param string $value A double quoted string
+ *
+ * @return string The unescaped string
+ */
+ public function unescapeDoubleQuotedString($value)
+ {
+ $callback = function ($match) {
+ return $this->unescapeCharacter($match[0]);
+ };
+
+ // evaluate the string
+ return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value);
+ }
+
+ /**
+ * Unescapes a character that was found in a double-quoted string.
+ *
+ * @param string $value An escaped character
+ *
+ * @return string The unescaped character
+ */
+ private function unescapeCharacter($value)
+ {
+ switch ($value[1]) {
+ case '0':
+ return "\x0";
+ case 'a':
+ return "\x7";
+ case 'b':
+ return "\x8";
+ case 't':
+ return "\t";
+ case "\t":
+ return "\t";
+ case 'n':
+ return "\n";
+ case 'v':
+ return "\xB";
+ case 'f':
+ return "\xC";
+ case 'r':
+ return "\r";
+ case 'e':
+ return "\x1B";
+ case ' ':
+ return ' ';
+ case '"':
+ return '"';
+ case '/':
+ return '/';
+ case '\\':
+ return '\\';
+ case 'N':
+ // U+0085 NEXT LINE
+ return "\xC2\x85";
+ case '_':
+ // U+00A0 NO-BREAK SPACE
+ return "\xC2\xA0";
+ case 'L':
+ // U+2028 LINE SEPARATOR
+ return "\xE2\x80\xA8";
+ case 'P':
+ // U+2029 PARAGRAPH SEPARATOR
+ return "\xE2\x80\xA9";
+ case 'x':
+ return self::utf8chr(hexdec(substr($value, 2, 2)));
+ case 'u':
+ return self::utf8chr(hexdec(substr($value, 2, 4)));
+ case 'U':
+ return self::utf8chr(hexdec(substr($value, 2, 8)));
+ default:
+ throw new ParseException(sprintf('Found unknown escape character "%s".', $value));
+ }
+ }
+
+ /**
+ * Get the UTF-8 character for the given code point.
+ *
+ * @param int $c The unicode code point
+ *
+ * @return string The corresponding UTF-8 character
+ */
+ private static function utf8chr($c)
+ {
+ if (0x80 > $c %= 0x200000) {
+ return chr($c);
+ }
+ if (0x800 > $c) {
+ return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
+ }
+ if (0x10000 > $c) {
+ return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
+ }
+
+ return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Yaml;
+
+use Symfony\Component\Yaml\Exception\ParseException;
+
+/**
+ * Yaml offers convenience methods to load and dump YAML.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Yaml
+{
+ const DUMP_OBJECT = 1;
+ const PARSE_EXCEPTION_ON_INVALID_TYPE = 2;
+ const PARSE_OBJECT = 4;
+ const PARSE_OBJECT_FOR_MAP = 8;
+ const DUMP_EXCEPTION_ON_INVALID_TYPE = 16;
+ const PARSE_DATETIME = 32;
+ const DUMP_OBJECT_AS_MAP = 64;
+ const DUMP_MULTI_LINE_LITERAL_BLOCK = 128;
+ const PARSE_CONSTANT = 256;
+
+ /**
+ * Parses YAML into a PHP value.
+ *
+ * Usage:
+ * <code>
+ * $array = Yaml::parse(file_get_contents('config.yml'));
+ * print_r($array);
+ * </code>
+ *
+ * @param string $input A string containing YAML
+ * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
+ *
+ * @return mixed The YAML converted to a PHP value
+ *
+ * @throws ParseException If the YAML is not valid
+ */
+ public static function parse($input, $flags = 0)
+ {
+ if (is_bool($flags)) {
+ @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
+
+ if ($flags) {
+ $flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE;
+ } else {
+ $flags = 0;
+ }
+ }
+
+ if (func_num_args() >= 3) {
+ @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(2)) {
+ $flags |= self::PARSE_OBJECT;
+ }
+ }
+
+ if (func_num_args() >= 4) {
+ @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(3)) {
+ $flags |= self::PARSE_OBJECT_FOR_MAP;
+ }
+ }
+
+ $yaml = new Parser();
+
+ return $yaml->parse($input, $flags);
+ }
+
+ /**
+ * Dumps a PHP value to a YAML string.
+ *
+ * The dump method, when supplied with an array, will do its best
+ * to convert the array into friendly YAML.
+ *
+ * @param mixed $input The PHP value
+ * @param int $inline The level where you switch to inline YAML
+ * @param int $indent The amount of spaces to use for indentation of nested nodes
+ * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string
+ *
+ * @return string A YAML string representing the original PHP value
+ */
+ public static function dump($input, $inline = 2, $indent = 4, $flags = 0)
+ {
+ if (is_bool($flags)) {
+ @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);
+
+ if ($flags) {
+ $flags = self::DUMP_EXCEPTION_ON_INVALID_TYPE;
+ } else {
+ $flags = 0;
+ }
+ }
+
+ if (func_num_args() >= 5) {
+ @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_OBJECT flag instead.', E_USER_DEPRECATED);
+
+ if (func_get_arg(4)) {
+ $flags |= self::DUMP_OBJECT;
+ }
+ }
+
+ $yaml = new Dumper($indent);
+
+ return $yaml->dump($input, $inline, 0, $flags);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Can be used as a foundation for new DatabaseTesters.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_AbstractTester implements PHPUnit_Extensions_Database_ITester
+{
+ /**
+ * @var PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ protected $setUpOperation;
+
+ /**
+ * @var PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ protected $tearDownOperation;
+
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ protected $dataSet;
+
+ /**
+ * @var string
+ */
+ protected $schema;
+
+ /**
+ * Creates a new database tester.
+ */
+ public function __construct()
+ {
+ $this->setUpOperation = PHPUnit_Extensions_Database_Operation_Factory::CLEAN_INSERT();
+ $this->tearDownOperation = PHPUnit_Extensions_Database_Operation_Factory::NONE();
+ }
+
+ /**
+ * Closes the specified connection.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ */
+ public function closeConnection(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ $connection->close();
+ }
+
+ /**
+ * Returns the test dataset.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ public function getDataSet()
+ {
+ return $this->dataSet;
+ }
+
+ /**
+ * TestCases must call this method inside setUp().
+ */
+ public function onSetUp()
+ {
+ $this->getSetUpOperation()->execute($this->getConnection(), $this->getDataSet());
+ }
+
+ /**
+ * TestCases must call this method inside tearDown().
+ */
+ public function onTearDown()
+ {
+ $this->getTearDownOperation()->execute($this->getConnection(), $this->getDataSet());
+ }
+
+ /**
+ * Sets the test dataset to use.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet
+ */
+ public function setDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ $this->dataSet = $dataSet;
+ }
+
+ /**
+ * Sets the schema value.
+ *
+ * @param string $schema
+ */
+ public function setSchema($schema)
+ {
+ $this->schema = $schema;
+ }
+
+ /**
+ * Sets the DatabaseOperation to call when starting the test.
+ *
+ * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $setUpOperation
+ */
+ public function setSetUpOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $setUpOperation)
+ {
+ $this->setUpOperation = $setUpOperation;
+ }
+
+ /**
+ * Sets the DatabaseOperation to call when ending the test.
+ *
+ * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $tearDownOperation
+ */
+ public function setTearDownOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $tearDownOperation)
+ {
+ $this->tearDownOperation = $tearDownOperation;
+ }
+
+ /**
+ * Returns the schema value
+ *
+ * @return string
+ */
+ protected function getSchema()
+ {
+ return $this->schema;
+ }
+
+ /**
+ * Returns the database operation that will be called when starting the test.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation
+ */
+ protected function getSetUpOperation()
+ {
+ return $this->setUpOperation;
+ }
+
+ /**
+ * Returns the database operation that will be called when ending the test.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation
+ */
+ protected function getTearDownOperation()
+ {
+ return $this->tearDownOperation;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Asserts whether or not two dbunit datasets are equal.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Constraint_DataSetIsEqual extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ protected $value;
+
+ /**
+ * @var string
+ */
+ protected $failure_reason;
+
+ /**
+ * Creates a new constraint.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $value
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_IDataSet $value)
+ {
+ parent::__construct();
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns TRUE if the
+ * constraint is met, FALSE otherwise.
+ *
+ * This method can be overridden to implement the evaluation algorithm.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ if (!$other instanceof PHPUnit_Extensions_Database_DataSet_IDataSet) {
+ throw new InvalidArgumentException(
+ 'PHPUnit_Extensions_Database_DataSet_IDataSet expected'
+ );
+ }
+
+ return $this->value->matches($other);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return $other->__toString() . ' ' . $this->toString();
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'is equal to expected %s', $this->value->__toString()
+ );
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Asserts whether or not two dbunit tables are equal.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Constraint_TableIsEqual extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ protected $value;
+
+ /**
+ * @var string
+ */
+ protected $failure_reason;
+
+ /**
+ * Creates a new constraint.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $value
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_ITable $value)
+ {
+ parent::__construct();
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns TRUE if the
+ * constraint is met, FALSE otherwise.
+ *
+ * This method can be overridden to implement the evaluation algorithm.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ if (!$other instanceof PHPUnit_Extensions_Database_DataSet_ITable) {
+ throw new InvalidArgumentException(
+ 'PHPUnit_Extensions_Database_DataSet_ITable expected'
+ );
+ }
+
+ return $this->value->matches($other);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return $other->__toString() . ' ' . $this->toString();
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'is equal to expected %s', $this->value->__toString()
+ );
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Asserts the row count in a table
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Constraint_TableRowCount extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var int
+ */
+ protected $value;
+
+ /**
+ * @var string
+ */
+ protected $tableName;
+
+ /**
+ * Creates a new constraint.
+ *
+ * @param $tableName
+ * @param $value
+ */
+ public function __construct($tableName, $value)
+ {
+ parent::__construct();
+ $this->tableName = $tableName;
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns TRUE if the
+ * constraint is met, FALSE otherwise.
+ *
+ * This method can be overridden to implement the evaluation algorithm.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $other == $this->value;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf('is equal to expected row count %d', $this->value);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides access to a database instance as a data set.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_DataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * An array of ITable objects.
+ *
+ * @var array
+ */
+ protected $tables = [];
+
+ /**
+ * The database connection this dataset is using.
+ *
+ * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ */
+ protected $databaseConnection;
+
+ /**
+ * Creates a new dataset using the given database connection.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection
+ */
+ public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection)
+ {
+ $this->databaseConnection = $databaseConnection;
+ }
+
+ /**
+ * Creates the query necessary to pull all of the data from a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData
+ * @return unknown
+ */
+ public static function buildTableSelect(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData, PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection = NULL)
+ {
+ if ($tableMetaData->getTableName() == '') {
+ $e = new Exception('Empty Table Name');
+ echo $e->getTraceAsString();
+ throw $e;
+ }
+
+ $columns = $tableMetaData->getColumns();
+ if ($databaseConnection) {
+ $columns = array_map([$databaseConnection, 'quoteSchemaObject'], $columns);
+ }
+ $columnList = implode(', ', $columns);
+
+ if ($databaseConnection) {
+ $tableName = $databaseConnection->quoteSchemaObject($tableMetaData->getTableName());
+ } else {
+ $tableName = $tableMetaData->getTableName();
+ }
+
+ $primaryKeys = $tableMetaData->getPrimaryKeys();
+ if ($databaseConnection) {
+ $primaryKeys = array_map([$databaseConnection, 'quoteSchemaObject'], $primaryKeys);
+ }
+ if (count($primaryKeys)) {
+ $orderBy = 'ORDER BY ' . implode(' ASC, ', $primaryKeys) . ' ASC';
+ } else {
+ $orderBy = '';
+ }
+
+ return "SELECT {$columnList} FROM {$tableName} {$orderBy}";
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DB_TableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_DB_TableIterator($this->getTableNames(), $this, $reverse);
+ }
+
+ /**
+ * Returns a table object for the given table.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DB_Table
+ */
+ public function getTable($tableName)
+ {
+ if (!in_array($tableName, $this->getTableNames())) {
+ throw new InvalidArgumentException("$tableName is not a table in the current database.");
+ }
+
+ if (empty($this->tables[$tableName])) {
+ $this->tables[$tableName] = new PHPUnit_Extensions_Database_DB_Table($this->getTableMetaData($tableName), $this->databaseConnection);
+ }
+
+ return $this->tables[$tableName];
+ }
+
+ /**
+ * Returns a table meta data object for the given table.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData
+ */
+ public function getTableMetaData($tableName)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $this->databaseConnection->getMetaData()->getTableColumns($tableName), $this->databaseConnection->getMetaData()->getTablePrimaryKeys($tableName));
+ }
+
+ /**
+ * Returns a list of table names for the database
+ *
+ * @return Array
+ */
+ public function getTableNames()
+ {
+ return $this->databaseConnection->getMetaData()->getTableNames();
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface for communicating with a database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection implements PHPUnit_Extensions_Database_DB_IDatabaseConnection
+{
+ /**
+ * @var PDO
+ */
+ protected $connection;
+
+ /**
+ * The metadata object used to retrieve table meta data from the database.
+ *
+ * @var PHPUnit_Extensions_Database_DB_IMetaData
+ */
+ protected $metaData;
+
+ /**
+ * Creates a new database connection
+ *
+ * @param PDO $connection
+ * @param string $schema - The name of the database schema you will be testing against.
+ */
+ public function __construct(PDO $connection, $schema = '')
+ {
+ $this->connection = $connection;
+ $this->metaData = PHPUnit_Extensions_Database_DB_MetaData::createMetaData($connection, $schema);
+ $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ }
+
+ /**
+ * Close this connection.
+ */
+ public function close()
+ {
+ unset($this->connection);
+ }
+
+ /**
+ * Returns a database metadata object that can be used to retrieve table
+ * meta data from the database.
+ *
+ * @return PHPUnit_Extensions_Database_DB_IMetaData
+ */
+ public function getMetaData()
+ {
+ return $this->metaData;
+ }
+
+ /**
+ * Returns the schema for the connection.
+ *
+ * @return string
+ */
+ public function getSchema()
+ {
+ return $this->getMetaData()->getSchema();
+ }
+
+ /**
+ * Creates a dataset containing the specified table names. If no table
+ * names are specified then it will created a dataset over the entire
+ * database.
+ *
+ * @param array $tableNames
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ * @todo Implement the filtered data set.
+ */
+ public function createDataSet(array $tableNames = NULL)
+ {
+ if (empty($tableNames)) {
+ return new PHPUnit_Extensions_Database_DB_DataSet($this);
+ } else {
+ return new PHPUnit_Extensions_Database_DB_FilteredDataSet($this, $tableNames);
+ }
+ }
+
+ /**
+ * Creates a table with the result of the specified SQL statement.
+ *
+ * @param string $resultName
+ * @param string $sql
+ * @return PHPUnit_Extensions_Database_DB_Table
+ */
+ public function createQueryTable($resultName, $sql)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_QueryTable($resultName, $sql, $this);
+ }
+
+ /**
+ * Returns this connection database configuration
+ *
+ * @return PHPUnit_Extensions_Database_Database_DatabaseConfig
+ */
+ public function getConfig()
+ {
+ }
+
+ /**
+ * Returns a PDO Connection
+ *
+ * @return PDO
+ */
+ public function getConnection()
+ {
+ return $this->connection;
+ }
+
+ /**
+ * Returns the number of rows in the given table. You can specify an
+ * optional where clause to return a subset of the table.
+ *
+ * @param string $tableName
+ * @param string $whereClause
+ * @return int
+ */
+ public function getRowCount($tableName, $whereClause = NULL)
+ {
+ $query = 'SELECT COUNT(*) FROM ' . $this->quoteSchemaObject($tableName);
+
+ if (isset($whereClause)) {
+ $query .= " WHERE {$whereClause}";
+ }
+
+ return (int) $this->connection->query($query)->fetchColumn();
+ }
+
+ /**
+ * Returns a quoted schema object. (table name, column name, etc)
+ *
+ * @param string $object
+ * @return string
+ */
+ public function quoteSchemaObject($object)
+ {
+ return $this->getMetaData()->quoteSchemaObject($object);
+ }
+
+ /**
+ * Returns the command used to truncate a table.
+ *
+ * @return string
+ */
+ public function getTruncateCommand()
+ {
+ return $this->getMetaData()->getTruncateCommand();
+ }
+
+ /**
+ * Returns true if the connection allows cascading
+ *
+ * @return bool
+ */
+ public function allowsCascading()
+ {
+ return $this->getMetaData()->allowsCascading();
+ }
+
+ /**
+ * Disables primary keys if connection does not allow setting them otherwise
+ *
+ * @param string $tableName
+ */
+ public function disablePrimaryKeys($tableName)
+ {
+ $this->getMetaData()->disablePrimaryKeys($tableName);
+ }
+
+ /**
+ * Reenables primary keys after they have been disabled
+ *
+ * @param string $tableName
+ */
+ public function enablePrimaryKeys($tableName)
+ {
+ $this->getMetaData()->enablePrimaryKeys($tableName);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides access to a database instance as a data set.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_FilteredDataSet extends PHPUnit_Extensions_Database_DB_DataSet
+{
+ /**
+ * @var Array
+ */
+ protected $tableNames;
+
+ /**
+ * Creates a new dataset using the given database connection.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection
+ */
+ public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection, Array $tableNames)
+ {
+ parent::__construct($databaseConnection);
+ $this->tableNames = $tableNames;
+ }
+
+ /**
+ * Returns a list of table names for the database
+ *
+ * @return Array
+ */
+ public function getTableNames()
+ {
+ return $this->tableNames;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface for communicating with a database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DB_IDatabaseConnection
+{
+ /**
+ * Close this connection.
+ */
+ public function close();
+
+ /**
+ * Creates a dataset containing the specified table names. If no table
+ * names are specified then it will created a dataset over the entire
+ * database.
+ *
+ * @param array $tableNames
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ public function createDataSet(Array $tableNames = NULL);
+
+ /**
+ * Creates a table with the result of the specified SQL statement.
+ *
+ * @param string $resultName
+ * @param string $sql
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function createQueryTable($resultName, $sql);
+
+ /**
+ * Returns a PDO Connection
+ *
+ * @return PDO
+ */
+ public function getConnection();
+
+ /**
+ * Returns a database metadata object that can be used to retrieve table
+ * meta data from the database.
+ *
+ * @return PHPUnit_Extensions_Database_DB_IMetaData
+ */
+ public function getMetaData();
+
+ /**
+ * Returns the number of rows in the given table. You can specify an
+ * optional where clause to return a subset of the table.
+ *
+ * @param string $tableName
+ * @param string $whereClause
+ * @param int
+ */
+ public function getRowCount($tableName, $whereClause = NULL);
+
+ /**
+ * Returns the schema for the connection.
+ *
+ * @return string
+ */
+ public function getSchema();
+
+ /**
+ * Returns a quoted schema object. (table name, column name, etc)
+ *
+ * @param string $object
+ * @return string
+ */
+ public function quoteSchemaObject($object);
+
+ /**
+ * Returns the command used to truncate a table.
+ *
+ * @return string
+ */
+ public function getTruncateCommand();
+
+ /**
+ * Returns true if the connection allows cascading
+ *
+ * @return bool
+ */
+ public function allowsCascading();
+
+ /**
+ * Disables primary keys if connection does not allow setting them otherwise
+ *
+ * @param string $tableName
+ */
+ public function disablePrimaryKeys($tableName);
+
+ /**
+ * Reenables primary keys after they have been disabled
+ *
+ * @param string $tableName
+ */
+ public function enablePrimaryKeys($tableName);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface for retreiving metadata from a database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DB_IMetaData
+{
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames();
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName);
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName);
+
+ /**
+ * Returns the name of the default schema.
+ *
+ * @return string
+ */
+ public function getSchema();
+
+ /**
+ * Returns a quoted schema object. (table name, column name, etc)
+ *
+ * @param string $object
+ * @return string
+ */
+ public function quoteSchemaObject($object);
+
+ /**
+ * Returns true if the rdbms allows cascading
+ *
+ * @return bool
+ */
+ public function allowsCascading();
+
+ /**
+ * Disables primary keys if rdbms does not allow setting them otherwise
+ *
+ * @param string $tableName
+ */
+ public function disablePrimaryKeys($tableName);
+
+ /**
+ * Reenables primary keys after they have been disabled
+ *
+ * @param string $tableName
+ */
+ public function enablePrimaryKeys($tableName);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic constructor for all meta data classes and a factory for
+ * generating the appropriate meta data class.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_DB_MetaData implements PHPUnit_Extensions_Database_DB_IMetaData
+{
+ protected static $metaDataClassMap = [
+ 'pgsql' => 'PHPUnit_Extensions_Database_DB_MetaData_PgSQL',
+ 'mysql' => 'PHPUnit_Extensions_Database_DB_MetaData_MySQL',
+ 'oci' => 'PHPUnit_Extensions_Database_DB_MetaData_Oci',
+ 'sqlite' => 'PHPUnit_Extensions_Database_DB_MetaData_Sqlite',
+ 'sqlite2' => 'PHPUnit_Extensions_Database_DB_MetaData_Sqlite',
+ 'sqlsrv' => 'PHPUnit_Extensions_Database_DB_MetaData_SqlSrv',
+ 'firebird' => 'PHPUnit_Extensions_Database_DB_MetaData_Firebird',
+ 'dblib' => 'PHPUnit_Extensions_Database_DB_MetaData_Dblib'
+ ];
+
+ /**
+ * The PDO connection used to retreive database meta data
+ *
+ * @var PDO
+ */
+ protected $pdo;
+
+ /**
+ * The default schema name for the meta data object.
+ *
+ * @var string
+ */
+ protected $schema;
+
+ /**
+ * The character used to quote schema objects.
+ */
+ protected $schemaObjectQuoteChar = '"';
+
+ /**
+ * The command used to perform a TRUNCATE operation.
+ */
+ protected $truncateCommand = 'TRUNCATE';
+
+ /**
+ * Creates a new database meta data object using the given pdo connection
+ * and schema name.
+ *
+ * @param PDO $pdo
+ * @param string $schema
+ */
+ public final function __construct(PDO $pdo, $schema = '')
+ {
+ $this->pdo = $pdo;
+ $this->schema = $schema;
+ }
+
+ /**
+ * Creates a meta data object based on the driver of given $pdo object and
+ * $schema name.
+ *
+ * @param PDO $pdo
+ * @param string $schema
+ * @return PHPUnit_Extensions_Database_DB_MetaData
+ */
+ public static function createMetaData(PDO $pdo, $schema = '')
+ {
+ $driverName = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
+ if (isset(self::$metaDataClassMap[$driverName])) {
+ $className = self::$metaDataClassMap[$driverName];
+
+ if ($className instanceof ReflectionClass) {
+ return $className->newInstance($pdo, $schema);
+ } else {
+ return self::registerClassWithDriver($className, $driverName)->newInstance($pdo, $schema);
+ }
+ } else {
+ throw new PHPUnit_Extensions_Database_Exception("Could not find a meta data driver for {$driverName} pdo driver.");
+ }
+ }
+
+ /**
+ * Validates and registers the given $className with the given $pdoDriver.
+ * It should be noted that this function will not attempt to include /
+ * require the file. The $pdoDriver can be determined by the value of the
+ * PDO::ATTR_DRIVER_NAME attribute for a pdo object.
+ *
+ * A reflection of the $className is returned.
+ *
+ * @param string $className
+ * @param string $pdoDriver
+ * @return ReflectionClass
+ */
+ public static function registerClassWithDriver($className, $pdoDriver)
+ {
+ if (!class_exists($className)) {
+ throw new PHPUnit_Extensions_Database_Exception("Specified class for {$pdoDriver} driver ({$className}) does not exist.");
+ }
+
+ $reflection = new ReflectionClass($className);
+ if ($reflection->isSubclassOf('PHPUnit_Extensions_Database_DB_MetaData')) {
+ return self::$metaDataClassMap[$pdoDriver] = $reflection;
+ } else {
+ throw new PHPUnit_Extensions_Database_Exception("Specified class for {$pdoDriver} driver ({$className}) does not extend PHPUnit_Extensions_Database_DB_MetaData.");
+ }
+ }
+
+ /**
+ * Returns the schema for the connection.
+ *
+ * @return string
+ */
+ public function getSchema()
+ {
+ return $this->schema;
+ }
+
+ /**
+ * Returns a quoted schema object. (table name, column name, etc)
+ *
+ * @param string $object
+ * @return string
+ */
+ public function quoteSchemaObject($object)
+ {
+ $parts = explode('.', $object);
+ $quotedParts = [];
+
+ foreach ($parts as $part) {
+ $quotedParts[] = $this->schemaObjectQuoteChar .
+ str_replace($this->schemaObjectQuoteChar, $this->schemaObjectQuoteChar . $this->schemaObjectQuoteChar, $part) .
+ $this->schemaObjectQuoteChar;
+ }
+
+ return implode('.', $quotedParts);
+ }
+
+ /**
+ * Seperates the schema and the table from a fully qualified table name.
+ *
+ * Returns an associative array containing the 'schema' and the 'table'.
+ *
+ * @param string $fullTableName
+ * @return array
+ */
+ public function splitTableName($fullTableName)
+ {
+ if (($dot = strpos($fullTableName, '.')) !== FALSE) {
+ return [
+ 'schema' => substr($fullTableName, 0, $dot),
+ 'table' => substr($fullTableName, $dot + 1)
+ ];
+ } else {
+ return [
+ 'schema' => NULL,
+ 'table' => $fullTableName
+ ];
+ }
+ }
+
+ /**
+ * Returns the command for the database to truncate a table.
+ *
+ * @return string
+ */
+ public function getTruncateCommand()
+ {
+ return $this->truncateCommand;
+ }
+
+ /**
+ * Returns true if the rdbms allows cascading
+ *
+ * @return bool
+ */
+ public function allowsCascading()
+ {
+ return FALSE;
+ }
+
+ /**
+ * Disables primary keys if the rdbms does not allow setting them otherwise
+ *
+ * @param string $tableName
+ */
+ public function disablePrimaryKeys($tableName)
+ {
+ return;
+ }
+
+ /**
+ * Reenables primary keys after they have been disabled
+ *
+ * @param string $tableName
+ */
+ public function enablePrimaryKeys($tableName)
+ {
+ return;
+ }
+}
+<?php
+/**
+ * Provides functionality to retrieve meta data from an Dblib (SQL Server) database.
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_Dblib extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ /**
+ * No character used to quote schema objects.
+ * @var string
+ */
+ protected $schemaObjectQuoteChar = '';
+
+ /**
+ * The command used to perform a TRUNCATE operation.
+ * @var string
+ */
+ protected $truncateCommand = 'TRUNCATE TABLE';
+
+ /**
+ * @var array
+ */
+ protected $columns = [];
+
+ /**
+ * @var array
+ */
+ protected $keys = [];
+
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $tableNames = [];
+
+ $query = 'SELECT name
+ FROM sys.tables
+ ORDER BY name';
+
+ $result = $this->pdo->query($query);
+
+ while ($tableName = $result->fetchColumn(0)) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ if (!isset($this->columns[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->columns[$tableName];
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ if (!isset($this->keys[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->keys[$tableName];
+ }
+
+ /**
+ * Loads column info from a sql server database.
+ *
+ * @param string $tableName
+ */
+ protected function loadColumnInfo($tableName)
+ {
+ $query = "SELECT name
+ FROM sys.columns
+ WHERE object_id = OBJECT_ID('" . $tableName . "')
+ ORDER BY column_id";
+
+ $result = $this->pdo->query($query);
+
+ while ($columnName = $result->fetchColumn(0)) {
+ $this->columns[$tableName][] = $columnName;
+ }
+
+ $keyQuery = "SELECT COL_NAME(ic.OBJECT_ID,ic.column_id) AS ColumnName
+ FROM sys.indexes AS i INNER JOIN
+ sys.index_columns AS ic ON i.OBJECT_ID = ic.OBJECT_ID
+ AND i.index_id = ic.index_id
+ WHERE i.is_primary_key = 1 AND OBJECT_NAME(ic.OBJECT_ID) = '" . $tableName . "'";
+
+ $result = $this->pdo->query($keyQuery);
+
+ while ($columnName = $result->fetchColumn(0)) {
+ $this->keys[$tableName][] = $columnName;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides functionality to retrieve meta data from a Firebird database.
+ *
+ * @version Release: 1.1.2
+ * @since
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_Firebird extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ /**
+ * The command used to perform a TRUNCATE operation.
+ * @var string
+ */
+ protected $truncateCommand = 'DELETE FROM';
+
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $query = "
+ SELECT DISTINCT
+ TABLE_NAME
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE
+ TABLE_TYPE='BASE TABLE' AND
+ TABLE_SCHEMA = ?
+ ORDER BY TABLE_NAME
+ ";
+
+ $query = "
+ select
+ RDB$RELATION_NAME as TABLE_NAME
+ from RDB$RELATIONS
+ where
+ ((RDB$RELATION_TYPE = 0) or
+ (RDB$RELATION_TYPE is null)) and
+ (RDB$SYSTEM_FLAG = 0)
+ order by (RDB$RELATION_NAME)
+ ";
+
+ $statement = $this->pdo->prepare($query);
+ $statement->execute([$this->getSchema()]);
+
+ $tableNames = [];
+ while ($tableName = $statement->fetchColumn(0)) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ if (!isset($this->columns[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->columns[$tableName];
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ if (!isset($this->keys[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->keys[$tableName];
+ }
+
+ /**
+ * Loads column info from a database table.
+ *
+ * @param string $tableName
+ */
+ protected function loadColumnInfo($tableName)
+ {
+ $this->columns[$tableName] = [];
+ $this->keys[$tableName] = [];
+
+ $columnQuery = '
+ SELECT DISTINCT
+ COLUMN_NAME, ORDINAL_POSITION
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE
+ TABLE_NAME = ? AND
+ TABLE_SCHEMA = ?
+ ORDER BY ORDINAL_POSITION
+ ';
+
+ $columnQuery = "
+ select
+ rf.RDB\$FIELD_NAME as COLUMN_NAME,
+ rf.RDB\$FIELD_POSITION as ORDINAL_POSITION
+ from RDB\$RELATION_FIELDS as rf
+ where
+ upper(RDB\$RELATION_NAME) = upper(?)
+ order by
+ ORDINAL_POSITION
+
+ ";
+
+ $columnStatement = $this->pdo->prepare($columnQuery);
+ $columnStatement->execute([$tableName]);
+
+ while ($columName = $columnStatement->fetchColumn(0)) {
+ $this->columns[$tableName][] = $columName;
+ }
+
+ $keyQuery = "
+ SELECT
+ KCU.COLUMN_NAME,
+ KCU.ORDINAL_POSITION
+ FROM
+ INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU
+ LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS as TC
+ ON TC.TABLE_NAME = KCU.TABLE_NAME
+ WHERE
+ TC.CONSTRAINT_TYPE = 'PRIMARY KEY' AND
+ TC.TABLE_NAME = ? AND
+ TC.TABLE_SCHEMA = ?
+ ORDER BY
+ KCU.ORDINAL_POSITION ASC
+ ";
+
+ $keyQuery = "
+ select
+ idseg.rdb\$field_name as COLUMN_NAME,
+ idseg.rdb\$field_position as ORDINAL_POSITION,
+ rc.rdb\$relation_name as tablename,
+ rc.rdb\$constraint_name as pk_name
+ from
+ RDB\$RELATION_CONSTRAINTS AS rc
+ left join
+ rdb\$index_segments as idseg on
+ (rc.rdb\$index_name = idseg.rdb\$index_name)
+ where
+ rc.RDB\$CONSTRAINT_TYPE = 'PRIMARY KEY'
+ and upper(rc.RDB\$RELATION_NAME) = upper(?)
+ order by
+ rc.rdb\$constraint_name, idseg.rdb\$field_position
+ ";
+
+ $keyStatement = $this->pdo->prepare($keyQuery);
+ $keyStatement->execute([$tableName]);
+
+ while ($columName = $keyStatement->fetchColumn(0)) {
+ $this->keys[$tableName][] = $columName;
+ }
+ }
+
+ /**
+ * Returns the schema for the connection.
+ *
+ * @return string
+ */
+ public function getSchema()
+ {
+ if (empty($this->schema)) {
+ return 'public';
+ } else {
+ return $this->schema;
+ }
+ }
+
+ /**
+ * Returns true if the rdbms allows cascading
+ *
+ * @return bool
+ */
+ public function allowsCascading()
+ {
+ return false;
+ }
+
+ /**
+ * Returns a quoted schema object. (table name, column name, etc)
+ *
+ * @param string $object
+ * @return string
+ */
+ public function quoteSchemaObject($object) {
+ return $object; //firebird does not allow object quoting
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides functionality to retrieve meta data from a database with information_schema support.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_InformationSchema extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ protected $columns = [];
+
+ protected $keys = [];
+
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $query = "
+ SELECT DISTINCT
+ TABLE_NAME
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE
+ TABLE_TYPE='BASE TABLE' AND
+ TABLE_SCHEMA = ?
+ ORDER BY TABLE_NAME
+ ";
+
+ $statement = $this->pdo->prepare($query);
+ $statement->execute([$this->getSchema()]);
+
+ $tableNames = [];
+ while ($tableName = $statement->fetchColumn(0)) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ if (!isset($this->columns[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->columns[$tableName];
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ if (!isset($this->keys[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->keys[$tableName];
+ }
+
+ /**
+ * Loads column info from a sqlite database.
+ *
+ * @param string $tableName
+ */
+ protected function loadColumnInfo($tableName)
+ {
+ $this->columns[$tableName] = [];
+ $this->keys[$tableName] = [];
+
+ $columnQuery = '
+ SELECT DISTINCT
+ COLUMN_NAME
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE
+ TABLE_NAME = ? AND
+ TABLE_SCHEMA = ?
+ ORDER BY ORDINAL_POSITION
+ ';
+
+ $columnStatement = $this->pdo->prepare($columnQuery);
+ $columnStatement->execute([$tableName, $this->getSchema()]);
+
+ while ($columName = $columnStatement->fetchColumn(0)) {
+ $this->columns[$tableName][] = $columName;
+ }
+
+ $keyQuery = "
+ SELECT
+ KCU.COLUMN_NAME
+ FROM
+ INFORMATION_SCHEMA.TABLE_CONSTRAINTS as TC,
+ INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU
+ WHERE
+ TC.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME AND
+ TC.TABLE_NAME = KCU.TABLE_NAME AND
+ TC.TABLE_SCHEMA = KCU.TABLE_SCHEMA AND
+ TC.CONSTRAINT_TYPE = 'PRIMARY KEY' AND
+ TC.TABLE_NAME = ? AND
+ TC.TABLE_SCHEMA = ?
+ ORDER BY
+ KCU.ORDINAL_POSITION ASC
+ ";
+
+ $keyStatement = $this->pdo->prepare($keyQuery);
+ $keyStatement->execute([$tableName, $this->getSchema()]);
+
+ while ($columName = $keyStatement->fetchColumn(0)) {
+ $this->keys[$tableName][] = $columName;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides functionality to retrieve meta data from a MySQL database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_MySQL extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ protected $schemaObjectQuoteChar = '`';
+
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $query = 'SHOW TABLES';
+ $statement = $this->pdo->prepare($query);
+ $statement->execute();
+
+ $tableNames = [];
+ while (($tableName = $statement->fetchColumn(0))) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ $query = 'SHOW COLUMNS FROM ' . $this->quoteSchemaObject($tableName);
+ $statement = $this->pdo->prepare($query);
+ $statement->execute();
+
+ $columnNames = [];
+ while (($columnName = $statement->fetchColumn(0))) {
+ $columnNames[] = $columnName;
+ }
+
+ return $columnNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ $query = 'SHOW INDEX FROM ' . $this->quoteSchemaObject($tableName);
+ $statement = $this->pdo->prepare($query);
+ $statement->execute();
+ $statement->setFetchMode(PDO::FETCH_ASSOC);
+
+ $columnNames = [];
+ while (($column = $statement->fetch())) {
+ if ($column['Key_name'] == 'PRIMARY') {
+ $columnNames[] = $column['Column_name'];
+ }
+ }
+
+ return $columnNames;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides functionality to retrieve meta data from an Oracle database.
+ *
+ * @since Class available since Release 3.2.3
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_Oci extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ /**
+ * No character used to quote schema objects.
+ * @var string
+ */
+ protected $schemaObjectQuoteChar = '';
+
+ /**
+ * The command used to perform a TRUNCATE operation.
+ * @var string
+ */
+ protected $truncateCommand = 'TRUNCATE TABLE';
+
+ /**
+ * @var array
+ */
+ protected $columns = [];
+
+ /**
+ * @var array
+ */
+ protected $keys = [];
+
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $tableNames = [];
+
+ $query = "SELECT table_name
+ FROM cat
+ WHERE table_type='TABLE'
+ ORDER BY table_name";
+
+ $result = $this->pdo->query($query);
+
+ while ($tableName = $result->fetchColumn(0)) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ if (!isset($this->columns[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->columns[$tableName];
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ if (!isset($this->keys[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->keys[$tableName];
+ }
+
+ /**
+ * Loads column info from a oracle database.
+ *
+ * @param string $tableName
+ */
+ protected function loadColumnInfo($tableName)
+ {
+ $ownerQuery = '';
+ $conOwnerQuery = '';
+ $tableParts = $this->splitTableName($tableName);
+
+ $this->columns[$tableName] = [];
+ $this->keys[$tableName] = [];
+
+ if (!empty($tableParts['schema']))
+ {
+ $ownerQuery = " AND OWNER = '{$tableParts['schema']}'";
+ $conOwnerQuery = " AND a.owner = '{$tableParts['schema']}'";
+ }
+
+ $query = "SELECT DISTINCT COLUMN_NAME
+ FROM USER_TAB_COLUMNS
+ WHERE TABLE_NAME='" . $tableParts['table'] . "'
+ $ownerQuery
+ ORDER BY COLUMN_NAME";
+
+ $result = $this->pdo->query($query);
+
+ while ($columnName = $result->fetchColumn(0)) {
+ $this->columns[$tableName][] = $columnName;
+ }
+
+ $keyQuery = "SELECT b.column_name
+ FROM user_constraints a, user_cons_columns b
+ WHERE a.constraint_type='P'
+ AND a.constraint_name=b.constraint_name
+ $conOwnerQuery
+ AND a.table_name = '" . $tableParts['table'] . "' ";
+
+ $result = $this->pdo->query($keyQuery);
+
+ while ($columnName = $result->fetchColumn(0)) {
+ $this->keys[$tableName][] = $columnName;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides functionality to retrieve meta data from a PostgreSQL database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_PgSQL extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $query = "
+ SELECT DISTINCT
+ TABLE_NAME
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE
+ TABLE_TYPE='BASE TABLE' AND
+ TABLE_SCHEMA = ?
+ ORDER BY TABLE_NAME
+ ";
+
+ $statement = $this->pdo->prepare($query);
+ $statement->execute([$this->getSchema()]);
+
+ $tableNames = [];
+ while ($tableName = $statement->fetchColumn(0)) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ if (!isset($this->columns[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->columns[$tableName];
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ if (!isset($this->keys[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->keys[$tableName];
+ }
+
+ /**
+ * Loads column info from a database table.
+ *
+ * @param string $tableName
+ */
+ protected function loadColumnInfo($tableName)
+ {
+ $this->columns[$tableName] = [];
+ $this->keys[$tableName] = [];
+
+ $columnQuery = '
+ SELECT DISTINCT
+ COLUMN_NAME, ORDINAL_POSITION
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE
+ TABLE_NAME = ? AND
+ TABLE_SCHEMA = ?
+ ORDER BY ORDINAL_POSITION
+ ';
+
+ $columnStatement = $this->pdo->prepare($columnQuery);
+ $columnStatement->execute([$tableName, $this->getSchema()]);
+
+ while ($columName = $columnStatement->fetchColumn(0)) {
+ $this->columns[$tableName][] = $columName;
+ }
+
+ $keyQuery = "
+ SELECT
+ KCU.COLUMN_NAME,
+ KCU.ORDINAL_POSITION
+ FROM
+ INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU
+ LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS as TC
+ ON TC.TABLE_NAME = KCU.TABLE_NAME AND
+ TC.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME
+ WHERE
+ TC.CONSTRAINT_TYPE = 'PRIMARY KEY' AND
+ TC.TABLE_NAME = ? AND
+ TC.TABLE_SCHEMA = ?
+ ORDER BY
+ KCU.ORDINAL_POSITION ASC
+ ";
+
+ $keyStatement = $this->pdo->prepare($keyQuery);
+ $keyStatement->execute([$tableName, $this->getSchema()]);
+
+ while ($columName = $keyStatement->fetchColumn(0)) {
+ $this->keys[$tableName][] = $columName;
+ }
+ }
+
+ /**
+ * Returns the schema for the connection.
+ *
+ * @return string
+ */
+ public function getSchema()
+ {
+ if (empty($this->schema)) {
+ return 'public';
+ } else {
+ return $this->schema;
+ }
+ }
+
+ /**
+ * Returns true if the rdbms allows cascading
+ *
+ * @return bool
+ */
+ public function allowsCascading()
+ {
+ return TRUE;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides functionality to retrieve meta data from a Microsoft SQL Server database.
+ *
+ * @since Class available since Release 1.1.0
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_SqlSrv extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ /**
+ * No character used to quote schema objects.
+ * @var string
+ */
+ protected $schemaObjectQuoteChar = '';
+
+ /**
+ * The command used to perform a TRUNCATE operation.
+ * @var string
+ */
+ protected $truncateCommand = 'TRUNCATE TABLE';
+
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $query = "SELECT name
+ FROM sysobjects
+ WHERE type='U'";
+
+ $statement = $this->pdo->prepare($query);
+ $statement->execute();
+
+ $tableNames = [];
+ while (($tableName = $statement->fetchColumn(0))) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ $query = "SELECT c.name
+ FROM syscolumns c
+ LEFT JOIN sysobjects o ON c.id = o.id
+ WHERE o.name = '$tableName'";
+
+ $statement = $this->pdo->prepare($query);
+ $statement->execute();
+
+ $columnNames = [];
+ while (($columnName = $statement->fetchColumn(0))) {
+ $columnNames[] = $columnName;
+ }
+
+ return $columnNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ $query = "EXEC sp_statistics '$tableName'";
+ $statement = $this->pdo->prepare($query);
+ $statement->execute();
+ $statement->setFetchMode(PDO::FETCH_ASSOC);
+
+ $columnNames = [];
+ while (($column = $statement->fetch())) {
+ if ($column['TYPE'] == 1) {
+ $columnNames[] = $column['COLUMN_NAME'];
+ }
+ }
+
+ return $columnNames;
+ }
+
+ /**
+ * Allow overwriting identities for the given table.
+ *
+ * @param string $tableName
+ */
+ public function disablePrimaryKeys($tableName)
+ {
+ try {
+ $query = "SET IDENTITY_INSERT $tableName ON";
+ $this->pdo->exec($query);
+ }
+ catch (PDOException $e) {
+ // ignore the error here - can happen if primary key is not an identity
+ }
+ }
+
+ /**
+ * Reenable auto creation of identities for the given table.
+ *
+ * @param string $tableName
+ */
+ public function enablePrimaryKeys($tableName)
+ {
+ try {
+ $query = "SET IDENTITY_INSERT $tableName OFF";
+ $this->pdo->exec($query);
+ }
+ catch (PDOException $e) {
+ // ignore the error here - can happen if primary key is not an identity
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides functionality to retrieve meta data from an Sqlite database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_MetaData_Sqlite extends PHPUnit_Extensions_Database_DB_MetaData
+{
+ protected $columns = [];
+
+ protected $keys = [];
+
+ protected $truncateCommand = 'DELETE FROM';
+ /**
+ * Returns an array containing the names of all the tables in the database.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $query = "
+ SELECT name
+ FROM sqlite_master
+ WHERE
+ type='table' AND
+ name <> 'sqlite_sequence'
+ ORDER BY name
+ ";
+
+ $result = $this->pdo->query($query);
+
+ $tableNames = [];
+
+ while ($tableName = $result->fetchColumn(0)) {
+ $tableNames[] = $tableName;
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns an array containing the names of all the columns in the
+ * $tableName table,
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableColumns($tableName)
+ {
+ if (!isset($this->columns[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->columns[$tableName];
+ }
+
+ /**
+ * Returns an array containing the names of all the primary key columns in
+ * the $tableName table.
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTablePrimaryKeys($tableName)
+ {
+ if (!isset($this->keys[$tableName])) {
+ $this->loadColumnInfo($tableName);
+ }
+
+ return $this->keys[$tableName];
+ }
+
+ /**
+ * Loads column info from a sqlite database.
+ *
+ * @param string $tableName
+ */
+ protected function loadColumnInfo($tableName)
+ {
+ $query = "PRAGMA table_info('{$tableName}')";
+ $statement = $this->pdo->query($query);
+
+ /* @var $statement PDOStatement */
+ $this->columns[$tableName] = [];
+ $this->keys[$tableName] = [];
+
+ while ($columnData = $statement->fetch(PDO::FETCH_NUM)) {
+ $this->columns[$tableName][] = $columnData[1];
+
+ if ((int)$columnData[5] !== 0) {
+ $this->keys[$tableName][] = $columnData[1];
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides the functionality to represent a database result set as a DBUnit
+ * table.
+ *
+ * @deprecated The PHPUnit_Extension_Database_DataSet_QueryTable should be used instead
+ * @see PHPUnit_Extension_Database_DataSet_QueryTable
+ * @see PHPUnit_Extension_Database_DataSet_QueryDataSet
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_ResultSetTable extends PHPUnit_Extensions_Database_DataSet_AbstractTable
+{
+ /**
+ * Creates a new result set table.
+ *
+ * @param string $tableName
+ * @param PDOStatement $pdoStatement
+ */
+ public function __construct($tableName, PDOStatement $pdoStatement)
+ {
+ $this->data = $pdoStatement->fetchAll(PDO::FETCH_ASSOC);
+
+ if (count($this->data)) {
+ $columns = array_keys($this->data[0]);
+ } else {
+ $columns = [];
+ }
+
+ $this->setTableMetaData(new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns));
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides the functionality to represent a database table.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_Table extends PHPUnit_Extensions_Database_DataSet_AbstractTable
+{
+ /**
+ * Creates a new database table object.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData, PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection)
+ {
+ $this->setTableMetaData($tableMetaData);
+
+ $pdoStatement = $databaseConnection->getConnection()->prepare(PHPUnit_Extensions_Database_DB_DataSet::buildTableSelect($tableMetaData, $databaseConnection));
+ $pdoStatement->execute();
+ $this->data = $pdoStatement->fetchAll(PDO::FETCH_ASSOC);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides iterative access to tables from a database instance.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_TableIterator implements PHPUnit_Extensions_Database_DataSet_ITableIterator
+{
+ /**
+ * An array of tablenames.
+ *
+ * @var Array
+ */
+ protected $tableNames;
+
+ /**
+ * If this property is true then the tables will be iterated in reverse
+ * order.
+ *
+ * @var bool
+ */
+ protected $reverse;
+
+ /**
+ * The database dataset that this iterator iterates over.
+ *
+ * @var PHPUnit_Extensions_Database_DB_DataSet
+ */
+ protected $dataSet;
+
+ public function __construct($tableNames, PHPUnit_Extensions_Database_DB_DataSet $dataSet, $reverse = FALSE)
+ {
+ $this->tableNames = $tableNames;
+ $this->dataSet = $dataSet;
+ $this->reverse = $reverse;
+
+ $this->rewind();
+ }
+
+ /**
+ * Returns the current table.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function getTable()
+ {
+ return $this->current();
+ }
+
+ /**
+ * Returns the current table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData()
+ {
+ return $this->current()->getTableMetaData();
+ }
+
+ /**
+ * Returns the current table.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function current()
+ {
+ $tableName = current($this->tableNames);
+
+ return $this->dataSet->getTable($tableName);
+ }
+
+ /**
+ * Returns the name of the current table.
+ *
+ * @return string
+ */
+ public function key()
+ {
+ return $this->current()->getTableMetaData()->getTableName();
+ }
+
+ /**
+ * advances to the next element.
+ */
+ public function next()
+ {
+ if ($this->reverse) {
+ prev($this->tableNames);
+ } else {
+ next($this->tableNames);
+ }
+ }
+
+ /**
+ * Rewinds to the first element
+ */
+ public function rewind()
+ {
+ if ($this->reverse) {
+ end($this->tableNames);
+ } else {
+ reset($this->tableNames);
+ }
+ }
+
+ /**
+ * Returns true if the current index is valid
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return (current($this->tableNames) !== FALSE);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This class loads a table metadata object with database metadata.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DB_TableMetaData extends PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData
+{
+ public function __construct($tableName, PHPUnit_Extensions_Database_DB_IMetaData $databaseMetaData)
+ {
+ $this->tableName = $tableName;
+ $this->columns = $databaseMetaData->getTableColumns($tableName);
+ $this->primaryKeys = $databaseMetaData->getTablePrimaryKeys($tableName);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Implements the basic functionality of data sets.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_DataSet_AbstractDataSet implements PHPUnit_Extensions_Database_DataSet_IDataSet
+{
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected abstract function createIterator($reverse = FALSE);
+
+ /**
+ * Returns an array of table names contained in the dataset.
+ *
+ * @return array
+ */
+ public function getTableNames()
+ {
+ $tableNames = [];
+
+ foreach ($this->getIterator() as $table) {
+ /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */
+ $tableNames[] = $table->getTableMetaData()->getTableName();
+ }
+
+ return $tableNames;
+ }
+
+ /**
+ * Returns a table meta data object for the given table.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData($tableName)
+ {
+ return $this->getTable($tableName)->getTableMetaData();
+ }
+
+ /**
+ * Returns a table object for the given table.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function getTable($tableName)
+ {
+ foreach ($this->getIterator() as $table) {
+ /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */
+ if ($table->getTableMetaData()->getTableName() == $tableName) {
+ return $table;
+ }
+ }
+ }
+
+ /**
+ * Returns an iterator for all table objects in the given dataset.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ public function getIterator()
+ {
+ return $this->createIterator();
+ }
+
+ /**
+ * Returns a reverse iterator for all table objects in the given dataset.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ public function getReverseIterator()
+ {
+ return $this->createIterator(TRUE);
+ }
+
+ /**
+ * Asserts that the given data set matches this data set.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_IDataSet $other)
+ {
+ $thisTableNames = $this->getTableNames();
+ $otherTableNames = $other->getTableNames();
+
+ sort($thisTableNames);
+ sort($otherTableNames);
+
+ if ($thisTableNames != $otherTableNames) {
+ return FALSE;
+ }
+
+ foreach ($thisTableNames as $tableName) {
+ $table = $this->getTable($tableName);
+
+ if (!$table->matches($other->getTable($tableName))) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ public function __toString()
+ {
+ $iterator = $this->getIterator();
+
+ $dataSetString = '';
+ foreach ($iterator as $table) {
+ $dataSetString .= $table->__toString();
+ }
+
+ return $dataSetString;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic functionality for dbunit tables
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_AbstractTable implements PHPUnit_Extensions_Database_DataSet_ITable
+{
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ protected $tableMetaData;
+
+ /**
+ * A 2-dimensional array containing the data for this table.
+ *
+ * @var array
+ */
+ protected $data;
+
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_ITable|null
+ */
+ private $other;
+
+ /**
+ * Sets the metadata for this table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData
+ * @deprecated
+ */
+ protected function setTableMetaData(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData)
+ {
+ $this->tableMetaData = $tableMetaData;
+ }
+
+ /**
+ * Returns the table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData()
+ {
+ return $this->tableMetaData;
+ }
+
+ /**
+ * Returns the number of rows in this table.
+ *
+ * @return int
+ */
+ public function getRowCount()
+ {
+ return count($this->data);
+ }
+
+ /**
+ * Returns the value for the given column on the given row.
+ *
+ * @param int $row
+ * @param int $column
+ * @todo reorganize this function to throw the exception first.
+ */
+ public function getValue($row, $column)
+ {
+ if (isset($this->data[$row][$column])) {
+ $value = $this->data[$row][$column];
+
+ return ($value instanceof SimpleXMLElement) ? (string) $value : $value;
+ } else {
+ if (!in_array($column, $this->getTableMetaData()->getColumns()) || $this->getRowCount() <= $row) {
+ throw new InvalidArgumentException("The given row ({$row}) and column ({$column}) do not exist in table {$this->getTableMetaData()->getTableName()}");
+ } else {
+ return NULL;
+ }
+ }
+ }
+
+ /**
+ * Returns the an associative array keyed by columns for the given row.
+ *
+ * @param int $row
+ * @return array
+ */
+ public function getRow($row)
+ {
+ if (isset($this->data[$row])) {
+ return $this->data[$row];
+ } else {
+ if ($this->getRowCount() <= $row) {
+ throw new InvalidArgumentException("The given row ({$row}) does not exist in table {$this->getTableMetaData()->getTableName()}");
+ } else {
+ return NULL;
+ }
+ }
+ }
+
+ /**
+ * Asserts that the given table matches this table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other)
+ {
+ $thisMetaData = $this->getTableMetaData();
+ $otherMetaData = $other->getTableMetaData();
+
+ if (!$thisMetaData->matches($otherMetaData) ||
+ $this->getRowCount() != $other->getRowCount()) {
+ return FALSE;
+ }
+
+ $columns = $thisMetaData->getColumns();
+ $rowCount = $this->getRowCount();
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ foreach ($columns as $columnName) {
+ $thisValue = $this->getValue($i, $columnName);
+ $otherValue = $other->getValue($i, $columnName);
+ if (is_numeric($thisValue) && is_numeric($otherValue)) {
+ if ($thisValue != $otherValue) {
+ $this->other = $other;
+
+ return FALSE;
+ }
+ } elseif ($thisValue !== $otherValue) {
+ $this->other = $other;
+
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Checks if a given row is in the table
+ *
+ * @param array $row
+ *
+ * @return bool
+ */
+ public function assertContainsRow(array $row)
+ {
+ return in_array($row, $this->data);
+ }
+
+ public function __toString()
+ {
+ $columns = $this->getTableMetaData()->getColumns();
+ $lineSeperator = str_repeat('+----------------------', count($columns)) . "+\n";
+ $lineLength = strlen($lineSeperator) - 1;
+
+ $tableString = $lineSeperator;
+ $tableString .= '| ' . str_pad($this->getTableMetaData()->getTableName(), $lineLength - 4, ' ', STR_PAD_RIGHT) . " |\n";
+ $tableString .= $lineSeperator;
+ $tableString .= $this->rowToString($columns);
+ $tableString .= $lineSeperator;
+
+ $rowCount = $this->getRowCount();
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ $values = [];
+
+ foreach ($columns as $columnName) {
+ if ($this->other) {
+ try {
+ if ($this->getValue($i, $columnName) != $this->other->getValue($i, $columnName)) {
+ $values[] = sprintf(
+ '%s != actual %s',
+ var_export($this->getValue($i, $columnName), TRUE),
+ var_export($this->other->getValue($i, $columnName), TRUE)
+ );
+ } else {
+ $values[] = $this->getValue($i, $columnName);
+ }
+ } catch (\InvalidArgumentException $ex) {
+ $values[] = $this->getValue($i, $columnName) . ': no row';
+ }
+ } else {
+ $values[] = $this->getValue($i, $columnName);
+ }
+ }
+
+ $tableString .= $this->rowToString($values) . $lineSeperator;
+ }
+
+ return ($this->other ? '(table diff enabled)' : '') . "\n" . $tableString . "\n";
+ }
+
+ protected function rowToString(Array $row)
+ {
+ $rowString = '';
+
+ foreach ($row as $value) {
+ if (is_null($value)) {
+ $value = 'NULL';
+ }
+
+ $rowString .= '| ' . str_pad(substr($value, 0, 20), 20, ' ', STR_PAD_BOTH) . ' ';
+ }
+
+ return $rowString . "|\n";
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides basic functionality for table meta data.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_DataSet_AbstractTableMetaData implements PHPUnit_Extensions_Database_DataSet_ITableMetaData
+{
+ /**
+ * The names of all columns in the table.
+ *
+ * @var Array
+ */
+ protected $columns;
+
+ /**
+ * The names of all the primary keys in the table.
+ *
+ * @var Array
+ */
+ protected $primaryKeys;
+
+ /**
+ * @var string
+ */
+ protected $tableName;
+
+ /**
+ * Returns the names of the columns in the table.
+ *
+ * @return array
+ */
+ public function getColumns()
+ {
+ return $this->columns;
+ }
+
+ /**
+ * Returns the names of the primary key columns in the table.
+ *
+ * @return array
+ */
+ public function getPrimaryKeys()
+ {
+ return $this->primaryKeys;
+ }
+
+ /**
+ * Returns the name of the table.
+ *
+ * @return string
+ */
+ public function getTableName()
+ {
+ return $this->tableName;
+ }
+
+ /**
+ * Asserts that the given tableMetaData matches this tableMetaData.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_ITableMetaData $other)
+ {
+ if ($this->getTableName() != $other->getTableName() ||
+ $this->getColumns() != $other->getColumns()) {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default implementation of a data set.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * @var array
+ */
+ protected $tables;
+
+ /**
+ * @var SimpleXmlElement
+ */
+ protected $xmlFileContents;
+
+ /**
+ * Creates a new dataset using the given tables.
+ *
+ * @param array $tables
+ */
+ public function __construct($xmlFile)
+ {
+ if (!is_file($xmlFile)) {
+ throw new InvalidArgumentException(
+ "Could not find xml file: {$xmlFile}"
+ );
+ }
+
+ $libxmlErrorReporting = libxml_use_internal_errors(TRUE);
+ $this->xmlFileContents = simplexml_load_file($xmlFile, 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_PARSEHUGE);
+
+ if (!$this->xmlFileContents) {
+ $message = '';
+
+ foreach (libxml_get_errors() as $error) {
+ $message .= print_r($error, true);
+ }
+
+ throw new RuntimeException($message);
+ }
+
+ libxml_clear_errors();
+ libxml_use_internal_errors($libxmlErrorReporting);
+
+ $tableColumns = [];
+ $tableValues = [];
+
+ $this->getTableInfo($tableColumns, $tableValues);
+ $this->createTables($tableColumns, $tableValues);
+ }
+
+ /**
+ * Reads the simple xml object and creates the appropriate tables and meta
+ * data for this dataset.
+ */
+ protected abstract function getTableInfo(Array &$tableColumns, Array &$tableValues);
+
+ protected function createTables(Array &$tableColumns, Array &$tableValues)
+ {
+ foreach ($tableValues as $tableName => $values) {
+ $table = $this->getOrCreateTable($tableName, $tableColumns[$tableName]);
+ foreach ($values as $value) {
+ $table->addRow($value);
+ }
+ }
+ }
+
+ /**
+ * Returns the table with the matching name. If the table does not exist
+ * an empty one is created.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ protected function getOrCreateTable($tableName, $tableColumns)
+ {
+ if (empty($this->tables[$tableName])) {
+ $tableMetaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $tableColumns);
+ $this->tables[$tableName] = new PHPUnit_Extensions_Database_DataSet_DefaultTable($tableMetaData);
+ }
+
+ return $this->tables[$tableName];
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Implements the basic functionality of data sets using a PHP array.
+ *
+ * @since Class available since Release 1.3.2
+ */
+class PHPUnit_Extensions_Database_DataSet_ArrayDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * @var array
+ */
+ protected $tables = [];
+
+ /**
+ * Constructor to build a new ArrayDataSet with the given array.
+ * The array parameter is an associative array of tables where the key is
+ * the table name and the value an array of rows. Each row is an associative
+ * array by itself with keys representing the field names and the values the
+ * actual data.
+ * For example:
+ * array(
+ * "addressbook" => array(
+ * array("id" => 1, "name" => "...", "address" => "..."),
+ * array("id" => 2, "name" => "...", "address" => "...")
+ * )
+ * )
+ *
+ * @param array $data
+ */
+ public function __construct(array $data)
+ {
+ foreach ($data AS $tableName => $rows) {
+ $columns = [];
+ if (isset($rows[0])) {
+ $columns = array_keys($rows[0]);
+ }
+
+ $metaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns);
+ $table = new PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData);
+
+ foreach ($rows AS $row) {
+ $table->addRow($row);
+ }
+ $this->tables[$tableName] = $table;
+ }
+ }
+
+ protected function createIterator($reverse = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse);
+ }
+
+ public function getTable($tableName)
+ {
+ if (!isset($this->tables[$tableName])) {
+ throw new InvalidArgumentException("$tableName is not a table in the current database.");
+ }
+
+ return $this->tables[$tableName];
+ }
+}
+?><?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates Composite Datasets
+ *
+ * Allows for creating datasets from multiple sources (csv, query, xml, etc.)
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_CompositeDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ protected $motherDataSet;
+
+ /**
+ * Creates a new Composite dataset
+ *
+ * You can pass in any data set that implements PHPUnit_Extensions_Database_DataSet_IDataSet
+ *
+ * @param string $delimiter
+ * @param string $enclosure
+ * @param string $escape
+ */
+ public function __construct(Array $dataSets = [])
+ {
+ $this->motherDataSet = new PHPUnit_Extensions_Database_DataSet_DefaultDataSet();
+
+ foreach ($dataSets as $dataSet)
+ {
+ $this->addDataSet($dataSet);
+ }
+ }
+
+ /**
+ * Adds a new data set to the composite.
+ *
+ * The dataset may not define tables that already exist in the composite.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet
+ */
+ public function addDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ foreach ($dataSet->getTableNames() as $tableName)
+ {
+ if (!in_array($tableName, $this->getTableNames())) {
+ $this->motherDataSet->addTable($dataSet->getTable($tableName));
+ } else {
+ $other = $dataSet->getTable($tableName);
+ $table = $this->getTable($tableName);
+
+ if (!$table->getTableMetaData()->matches($other->getTableMetaData()))
+ {
+ throw new InvalidArgumentException("There is already a table named $tableName with different table definition");
+ }
+
+ $table->addTableRows($dataSet->getTable($tableName));
+ }
+ }
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ if ($reverse) {
+ return $this->motherDataSet->getReverseIterator();
+ } else {
+ return $this->motherDataSet->getIterator();
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates CsvDataSets.
+ *
+ * You can incrementally add CSV files as tables to your datasets
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_CsvDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * @var array
+ */
+ protected $tables = [];
+
+ /**
+ * @var string
+ */
+ protected $delimiter = ',';
+
+ /**
+ * @var string
+ */
+ protected $enclosure = '"';
+
+ /**
+ * @var string
+ */
+ protected $escape = '"';
+
+ /**
+ * Creates a new CSV dataset
+ *
+ * You can pass in the parameters for how csv files will be read.
+ *
+ * @param string $delimiter
+ * @param string $enclosure
+ * @param string $escape
+ */
+ public function __construct($delimiter = ',', $enclosure = '"', $escape = '"')
+ {
+ $this->delimiter = $delimiter;
+ $this->enclosure = $enclosure;
+ $this->escape = $escape;
+ }
+
+ /**
+ * Adds a table to the dataset
+ *
+ * The table will be given the passed name. $csvFile should be a path to
+ * a valid csv file (based on the arguments passed to the constructor.)
+ *
+ * @param string $tableName
+ * @param string $csvFile
+ */
+ public function addTable($tableName, $csvFile)
+ {
+ if (!is_file($csvFile)) {
+ throw new InvalidArgumentException("Could not find csv file: {$csvFile}");
+ }
+
+ if (!is_readable($csvFile)) {
+ throw new InvalidArgumentException("Could not read csv file: {$csvFile}");
+ }
+
+ $fh = fopen($csvFile, 'r');
+ $columns = $this->getCsvRow($fh);
+
+ if ($columns === FALSE)
+ {
+ throw new InvalidArgumentException("Could not determine the headers from the given file {$csvFile}");
+ }
+
+ $metaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns);
+ $table = new PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData);
+
+ while (($row = $this->getCsvRow($fh)) !== FALSE)
+ {
+ $table->addRow(array_combine($columns, $row));
+ }
+
+ $this->tables[$tableName] = $table;
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse);
+ }
+
+ /**
+ * Returns a row from the csv file in an indexed array.
+ *
+ * @param resource $fh
+ * @return array
+ */
+ protected function getCsvRow($fh)
+ {
+ if (version_compare(PHP_VERSION, '5.3.0', '>')) {
+ return fgetcsv($fh, NULL, $this->delimiter, $this->enclosure, $this->escape);
+ } else {
+ return fgetcsv($fh, NULL, $this->delimiter, $this->enclosure);
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A dataset decorator that allows filtering out tables and table columns from
+ * results.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_DataSetFilter extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * The dataset being decorated.
+ * @var PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ protected $originalDataSet;
+
+ /**
+ * The tables to exclude from the data set.
+ * @var Array
+ */
+ protected $excludeTables = [];
+
+ /**
+ * The tables to exclude from the data set.
+ * @var Array
+ */
+ protected $includeTables = [];
+
+ /**
+ * The columns to exclude from the data set.
+ * @var Array
+ */
+ protected $excludeColumns = [];
+
+ /**
+ * The columns to exclude from the data set.
+ * @var Array
+ */
+ protected $includeColumns = [];
+
+ /**
+ * Creates a new filtered data set.
+ *
+ * The $exclude tables should be an associative array using table names as
+ * the key and an array of column names to exclude for the value. If you
+ * would like to exclude a full table set the value of the table's entry
+ * to the special string '*'.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $originalDataSet
+ * @param Array $excludeTables @deprecated use set* methods instead.
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_IDataSet $originalDataSet, array $excludeTables = [])
+ {
+ $this->originalDataSet = $originalDataSet;
+
+ $tables = [];
+ foreach ($excludeTables as $tableName => $values) {
+ if (is_array($values)) {
+ $this->setExcludeColumnsForTable($tableName, $values);
+ } elseif ($values == '*') {
+ $tables[] = $tableName;
+ } else {
+ $this->setExcludeColumnsForTable($tableName, (array) $values);
+ }
+ }
+
+ $this->addExcludeTables($tables);
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ $original_tables = $this->originalDataSet->getIterator($reverse);
+ $new_tables = [];
+
+ foreach ($original_tables as $table) {
+ /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */
+ $tableName = $table->getTableMetaData()->getTableName();
+
+ if ((!in_array($tableName, $this->includeTables) && !empty($this->includeTables)) ||
+ in_array($tableName, $this->excludeTables)
+ ) {
+ continue;
+ } elseif (!empty($this->excludeColumns[$tableName]) || !empty($this->includeColumns[$tableName])) {
+ $new_table = new PHPUnit_Extensions_Database_DataSet_TableFilter($table);
+
+ if (!empty($this->includeColumns[$tableName])) {
+ $new_table->addIncludeColumns($this->includeColumns[$tableName]);
+ }
+
+ if (!empty($this->excludeColumns[$tableName])) {
+ $new_table->addExcludeColumns($this->excludeColumns[$tableName]);
+ }
+
+ $new_tables[] = $new_table;
+ } else {
+ $new_tables[] = $table;
+ }
+ }
+
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($new_tables);
+ }
+
+ /**
+ * Adds tables to be included in the data set.
+ * @param array $tables
+ */
+ public function addIncludeTables(Array $tables)
+ {
+ $this->includeTables = array_unique(array_merge($this->includeTables, $tables));
+ }
+
+ /**
+ * Adds tables to be included in the data set.
+ * @param array $tables
+ */
+ public function addExcludeTables(Array $tables)
+ {
+ $this->excludeTables = array_unique(array_merge($this->excludeTables, $tables));
+ }
+
+ /**
+ * Adds columns to include in the data set for the given table.
+ * @param string $table
+ * @param Array $columns
+ */
+ public function setIncludeColumnsForTable($table, Array $columns)
+ {
+ $this->includeColumns[$table] = $columns;
+ }
+
+ /**
+ * Adds columns to include in the data set for the given table.
+ * @param string $table
+ * @param Array $columns
+ */
+ public function setExcludeColumnsForTable($table, Array $columns)
+ {
+ $this->excludeColumns[$table] = $columns;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default implementation of a data set.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_DefaultDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * An array of ITable objects.
+ *
+ * @var array
+ */
+ protected $tables;
+
+ /**
+ * Creates a new dataset using the given tables.
+ *
+ * @param array $tables
+ */
+ public function __construct(Array $tables = [])
+ {
+ $this->tables = $tables;
+ }
+
+ /**
+ * Adds a table to the dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ public function addTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ $this->tables[] = $table;
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides default table functionality.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_DefaultTable extends PHPUnit_Extensions_Database_DataSet_AbstractTable
+{
+ /**
+ * Creates a new table object using the given $tableMetaData
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData)
+ {
+ $this->setTableMetaData($tableMetaData);
+ $this->data = [];
+ }
+
+ /**
+ * Adds a row to the table with optional values.
+ *
+ * @param array $values
+ */
+ public function addRow($values = [])
+ {
+ $this->data[] = array_replace(
+ array_fill_keys($this->getTableMetaData()->getColumns(), NULL),
+ $values
+ );
+ }
+
+ /**
+ * Adds the rows in the passed table to the current table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ public function addTableRows(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ $tableColumns = $this->getTableMetaData()->getColumns();
+ $rowCount = $table->getRowCount();
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ $newRow = [];
+ foreach ($tableColumns as $columnName) {
+ $newRow[$columnName] = $table->getValue($i, $columnName);
+ }
+ $this->addRow($newRow);
+ }
+ }
+
+ /**
+ * Sets the specified column of the specied row to the specified value.
+ *
+ * @param int $row
+ * @param string $column
+ * @param mixed $value
+ */
+ public function setValue($row, $column, $value)
+ {
+ if (isset($this->data[$row])) {
+ $this->data[$row][$column] = $value;
+ } else {
+ throw new InvalidArgumentException('The row given does not exist.');
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default table iterator
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_DefaultTableIterator implements PHPUnit_Extensions_Database_DataSet_ITableIterator
+{
+ /**
+ * An array of tables in the iterator.
+ *
+ * @var Array
+ */
+ protected $tables;
+
+ /**
+ * If this property is true then the tables will be iterated in reverse
+ * order.
+ *
+ * @var bool
+ */
+ protected $reverse;
+
+ /**
+ * Creates a new default table iterator object.
+ *
+ * @param array $tables
+ * @param bool $reverse
+ */
+ public function __construct(Array $tables, $reverse = FALSE)
+ {
+ $this->tables = $tables;
+ $this->reverse = $reverse;
+
+ $this->rewind();
+ }
+
+ /**
+ * Returns the current table.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function getTable()
+ {
+ return $this->current();
+ }
+
+ /**
+ * Returns the current table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData()
+ {
+ return $this->current()->getTableMetaData();
+ }
+
+ /**
+ * Returns the current table.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function current()
+ {
+ return current($this->tables);
+ }
+
+ /**
+ * Returns the name of the current table.
+ *
+ * @return string
+ */
+ public function key()
+ {
+ return $this->current()->getTableMetaData()->getTableName();
+ }
+
+ /**
+ * advances to the next element.
+ */
+ public function next()
+ {
+ if ($this->reverse) {
+ prev($this->tables);
+ } else {
+ next($this->tables);
+ }
+ }
+
+ /**
+ * Rewinds to the first element
+ */
+ public function rewind()
+ {
+ if ($this->reverse) {
+ end($this->tables);
+ } else {
+ reset($this->tables);
+ }
+ }
+
+ /**
+ * Returns true if the current index is valid
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return ($this->current() !== FALSE);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default implementation of table meta data
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData extends PHPUnit_Extensions_Database_DataSet_AbstractTableMetaData
+{
+ /**
+ * Creates a new default table meta data object.
+ *
+ * @param string $tableName
+ * @param array $columns
+ * @param array $primaryKeys
+ */
+ public function __construct($tableName, Array $columns, Array $primaryKeys = [])
+ {
+ $this->tableName = $tableName;
+ $this->columns = $columns;
+ $this->primaryKeys = [];
+
+ foreach ($primaryKeys as $columnName) {
+ if (!in_array($columnName, $this->columns)) {
+ throw new InvalidArgumentException('Primary key column passed that is not in the column list.');
+ } else {
+ $this->primaryKeys[] = $columnName;
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default implementation of a data set.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet
+{
+ protected function getTableInfo(Array &$tableColumns, Array &$tableValues)
+ {
+ if ($this->xmlFileContents->getName() != 'dataset') {
+ throw new PHPUnit_Extensions_Database_Exception('The root element of a flat xml data set file must be called <dataset>');
+ }
+
+ foreach ($this->xmlFileContents->children() as $row) {
+ $tableName = $row->getName();
+
+ if (!isset($tableColumns[$tableName])) {
+ $tableColumns[$tableName] = [];
+ $tableValues[$tableName] = [];
+ }
+
+ $values = [];
+ foreach ($row->attributes() as $name => $value) {
+ if (!in_array($name, $tableColumns[$tableName])) {
+ $tableColumns[$tableName][] = $name;
+ }
+
+ $values[$name] = $value;
+ }
+
+ if (count($values)) {
+ $tableValues[$tableName][] = $values;
+ }
+ }
+ }
+
+ public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename)
+ {
+ $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_FlatXml();
+ $pers->setFileName($filename);
+
+ try {
+ $pers->write($dataset);
+ } catch (RuntimeException $e) {
+ throw new PHPUnit_Framework_Exception(__METHOD__ . ' called with an unwritable file.');
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface for creating and reading data from data sets.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DataSet_IDataSet extends IteratorAggregate
+{
+ /**
+ * Returns an array of table names contained in the dataset.
+ *
+ * @return array
+ */
+ public function getTableNames();
+
+ /**
+ * Returns a table meta data object for the given table.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData($tableName);
+
+ /**
+ * Returns a table object for the given table.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function getTable($tableName);
+
+ /**
+ * Returns a reverse iterator for all table objects in the given dataset.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ public function getReverseIterator();
+
+ /**
+ * Asserts that the given data set matches this data set.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_IDataSet $other);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An interface for persisting datasets
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DataSet_IPersistable
+{
+ /**
+ * Writes the given dataset
+ *
+ * The previous dataset will be overwritten.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ public function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides an interface for creating data sets from data set spec strings.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DataSet_ISpec
+{
+ /**
+ * Creates a data set from a data set spec string.
+ *
+ * @param string $dataSetSpec
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ public function getDataSet($dataSetSpec);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface for creating and reading data from data sets.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DataSet_ITable
+{
+ /**
+ * Returns the table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData();
+
+ /**
+ * Returns the number of rows in this table.
+ *
+ * @return int
+ */
+ public function getRowCount();
+
+ /**
+ * Returns the value for the given column on the given row.
+ *
+ * @param int $row
+ * @param int $column
+ */
+ public function getValue($row, $column);
+
+ /**
+ * Returns the an associative array keyed by columns for the given row.
+ *
+ * @param int $row
+ * @return array
+ */
+ public function getRow($row);
+
+ /**
+ * Asserts that the given table matches this table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface for creating and reading data from data sets.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DataSet_ITableIterator extends Iterator
+{
+ /**
+ * Returns the current table.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function getTable();
+
+ /**
+ * Returns the current table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData();
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface for returning table meta data.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DataSet_ITableMetaData
+{
+ /**
+ * Returns the names of the columns in the table.
+ *
+ * @return array
+ */
+ public function getColumns();
+
+ /**
+ * Returns the names of the primary key columns in the table.
+ *
+ * @return array
+ */
+ public function getPrimaryKeys();
+
+ /**
+ * Returns the name of the table.
+ *
+ * @return string
+ */
+ public function getTableName();
+
+ /**
+ * Asserts that the given tableMetaData matches this tableMetaData.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_ITableMetaData $other);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An interface for parsing YAML files.
+ *
+ * @since Class available since Release 1.3.1
+ */
+interface PHPUnit_Extensions_Database_DataSet_IYamlParser {
+ /**
+ * @param string $yamlFile
+ * @return array parsed YAML
+ */
+ public function parseYaml($yamlFile);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Data set implementation for the output of mysqldump --xml.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_MysqlXmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet
+{
+ protected function getTableInfo(array &$tableColumns, array &$tableValues)
+ {
+ if ($this->xmlFileContents->getName() != 'mysqldump') {
+ throw new PHPUnit_Extensions_Database_Exception('The root element of a MySQL XML data set file must be called <mysqldump>');
+ }
+
+ foreach ($this->xmlFileContents->xpath('./database/table_data') as $tableElement) {
+ if (empty($tableElement['name'])) {
+ throw new PHPUnit_Extensions_Database_Exception('<table_data> elements must include a name attribute');
+ }
+
+ $tableName = (string) $tableElement['name'];
+
+ if (!isset($tableColumns[$tableName])) {
+ $tableColumns[$tableName] = [];
+ }
+
+ if (!isset($tableValues[$tableName])) {
+ $tableValues[$tableName] = [];
+ }
+
+ foreach ($tableElement->xpath('./row') as $rowElement) {
+ $rowValues = [];
+
+ foreach ($rowElement->xpath('./field') as $columnElement) {
+ if (empty($columnElement['name'])) {
+ throw new PHPUnit_Extensions_Database_Exception('<field> element name attributes cannot be empty');
+ }
+
+ $columnName = (string) $columnElement['name'];
+
+ if (!in_array($columnName, $tableColumns[$tableName])) {
+ $tableColumns[$tableName][] = $columnName;
+ }
+ }
+
+ foreach ($tableColumns[$tableName] as $columnName) {
+ $fields = $rowElement->xpath('./field[@name="' . $columnName . '"]');
+ $column = $fields[0];
+ $attr = $column->attributes('http://www.w3.org/2001/XMLSchema-instance');
+
+ if (isset($attr['type']) && (string) $attr['type'] === 'xs:hexBinary') {
+ $columnValue = pack('H*',(string) $column);
+ } else {
+ $null = isset($column['nil']) || isset($attr[0]);
+ $columnValue = $null ? NULL : (string) $column;
+ }
+
+ $rowValues[$columnName] = $columnValue;
+
+ }
+
+ $tableValues[$tableName][] = $rowValues;
+ }
+ }
+
+ foreach ($this->xmlFileContents->xpath('./database/table_structure') as $tableElement) {
+ if (empty($tableElement['name'])) {
+ throw new PHPUnit_Extensions_Database_Exception('<table_structure> elements must include a name attribute');
+ }
+
+ $tableName = (string) $tableElement['name'];
+
+ foreach ($tableElement->xpath('./field') as $fieldElement) {
+ if (empty($fieldElement['Field']) && empty($fieldElement['field'])) {
+ throw new PHPUnit_Extensions_Database_Exception('<field> elements must include a Field attribute');
+ }
+
+ $columnName = (string) (empty($fieldElement['Field']) ? $fieldElement['field'] : $fieldElement['Field']);
+
+ if (!in_array($columnName, $tableColumns[$tableName])) {
+ $tableColumns[$tableName][] = $columnName;
+ }
+ }
+ }
+ }
+
+ public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename)
+ {
+ $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_MysqlXml;
+ $pers->setFileName($filename);
+
+ try {
+ $pers->write($dataset);
+ }
+
+ catch (RuntimeException $e) {
+ throw new PHPUnit_Framework_Exception(__METHOD__ . ' called with an unwritable file.');
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An abstract implementation of a dataset persistor.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_DataSet_Persistors_Abstract implements PHPUnit_Extensions_Database_DataSet_IPersistable
+{
+ public function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ $this->saveDataSet($dataset);
+ }
+
+ /**
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ protected function saveDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ $this->startDataSet($dataset);
+
+ foreach ($dataset as $table) {
+ $this->saveTable($table);
+ }
+
+ $this->endDataSet($dataset);
+ }
+
+ /**
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function saveTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ $rowCount = $table->getRowCount();
+ $this->startTable($table);
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ $this->row($table->getRow($i), $table);
+ }
+
+ $this->endTable($table);
+ }
+
+ /**
+ * Override to save the start of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ abstract protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset);
+
+ /**
+ * Override to save the end of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ abstract protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset);
+
+ /**
+ * Override to save the start of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ abstract protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table);
+
+ /**
+ * Override to save the end of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ abstract protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table);
+
+ /**
+ * Override to save a table row.
+ *
+ * @param array $row
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ abstract protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates the appropriate Persistor based on a given type and spec.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Persistors_Factory
+{
+ /**
+ * Returns the persistor.
+ *
+ * @param string $type
+ * @param string $spec
+ * @return PHPUnit_Extensions_Database_DataSet_IPersistable
+ */
+ public function getPersistorBySpec($type, $spec)
+ {
+ switch (strtolower($type)) {
+ case 'xml':
+ $xmlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_Xml();
+ $xmlPersistor->setFileName($spec);
+
+ return $xmlPersistor;
+
+ case 'flatxml':
+ $flatXmlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_FlatXml();
+ $flatXmlPersistor->setFileName($spec);
+
+ return $flatXmlPersistor;
+
+ case 'yaml':
+ $yamlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_Yaml();
+ $yamlPersistor->setFileName($spec);
+
+ return $yamlPersistor;
+
+ case 'mysqlxml':
+ $mysqlXmlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_MysqlXml();
+ $mysqlXmlPersistor->setFileName($spec);
+
+ return $mysqlXmlPersistor;
+
+ default:
+ throw new PHPUnit_Extensions_Database_Exception("I don't know what you want from me. PERSISTOR");
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A Flat XML dataset persistor.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Persistors_FlatXml extends PHPUnit_Extensions_Database_DataSet_Persistors_Abstract
+{
+ /**
+ * @var string
+ */
+ protected $filename;
+
+ /**
+ * @var resource
+ */
+ protected $fh;
+
+ /**
+ * Sets the filename that this persistor will save to.
+ *
+ * @param string $filename
+ */
+ public function setFileName($filename)
+ {
+ $this->filename = $filename;
+ }
+
+ /**
+ * Override to save the start of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ $this->fh = fopen($this->filename, 'w');
+
+ if ($this->fh === FALSE) {
+ throw new PHPUnit_Framework_Exception(
+ "Could not open {$this->filename} for writing see " . __CLASS__ . '::setFileName()'
+ );
+ }
+
+ fwrite($this->fh, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ fwrite($this->fh, "<dataset>\n");
+ }
+
+ /**
+ * Override to save the end of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ fwrite($this->fh, "</dataset>\n");
+ }
+
+ /**
+ * Override to save the start of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ if ($table->getRowCount() == 0) {
+ fwrite($this->fh, "\t<{$table->getTableMetaData()->getTableName()} />\n");
+ }
+ }
+
+ /**
+ * Override to save the end of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ //do nothing
+ }
+
+ /**
+ * Override to save a table row.
+ *
+ * @param array $row
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ fwrite($this->fh, "\t<{$table->getTableMetaData()->getTableName()}\n");
+
+ foreach ($table->getTableMetaData()->getColumns() as $columnName) {
+ if (isset($row[$columnName])) {
+ fwrite($this->fh, "\t\t{$columnName}=\"" . htmlspecialchars($row[$columnName]) . "\"\n");
+ }
+ }
+
+ fwrite($this->fh, "\t/>\n");
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A MySQL XML dataset persistor.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Persistors_MysqlXml extends PHPUnit_Extensions_Database_DataSet_Persistors_Abstract
+{
+ /**
+ * @var string
+ */
+ protected $filename;
+
+ /**
+ * @var string
+ */
+ protected $database;
+
+ /**
+ * @var resource
+ */
+ protected $fh;
+
+ /**
+ * Sets the filename that this persistor will save to.
+ *
+ * @param string $filename
+ */
+ public function setFileName($filename)
+ {
+ $this->filename = $filename;
+ }
+
+ /**
+ * Sets the name of the database.
+ *
+ * @param string $database
+ */
+ public function setDatabase($database)
+ {
+ $this->database = $database;
+ }
+
+ /**
+ * Override to save the start of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ $this->fh = fopen($this->filename, 'w');
+
+ if ($this->fh === FALSE) {
+ throw new PHPUnit_Framework_Exception(
+ "Could not open {$this->filename} for writing see " . __CLASS__ . '::setFileName()'
+ );
+ }
+
+ fwrite($this->fh, '<?xml version="1.0" encoding="UTF-8"?>' . "\n");
+ fwrite($this->fh, '<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' . "\n");
+ fwrite($this->fh, '<database name="' . $this->database . '">' . "\n");
+ }
+
+ /**
+ * Override to save the end of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ fwrite($this->fh, '</database>' . "\n");
+ fwrite($this->fh, '</mysqldump>' . "\n");
+ }
+
+ /**
+ * Override to save the start of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ fwrite($this->fh, "\t" . '<table_data name="' . $table->getTableMetaData()->getTableName() . '">' . "\n");
+ }
+
+ /**
+ * Override to save the end of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ fwrite($this->fh, "\t" . '</table_data>' . "\n");
+ }
+
+ /**
+ * Override to save a table row.
+ *
+ * @param array $row
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ fwrite($this->fh, "\t" . '<row>' . "\n");
+
+ foreach ($table->getTableMetaData()->getColumns() as $columnName) {
+ fwrite($this->fh, "\t\t" . '<field name="' . $columnName . '"');
+ if (isset($row[$columnName])) {
+ fwrite($this->fh, '>' . htmlspecialchars($row[$columnName]) . '</field>' . "\n");
+ } else {
+ fwrite($this->fh, ' xsi:nil="true" />' . "\n");
+ }
+ }
+
+ fwrite($this->fh, "\t" . '</row>' . "\n");
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A XML dataset persistor.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Persistors_Xml extends PHPUnit_Extensions_Database_DataSet_Persistors_Abstract
+{
+ /**
+ * @var string
+ */
+ protected $filename;
+
+ /**
+ * @var resource
+ */
+ protected $fh;
+
+ /**
+ * Sets the filename that this persistor will save to.
+ *
+ * @param string $filename
+ */
+ public function setFileName($filename)
+ {
+ $this->filename = $filename;
+ }
+
+ /**
+ * Override to save the start of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ $this->fh = fopen($this->filename, 'w');
+
+ if ($this->fh === FALSE) {
+ throw new PHPUnit_Framework_Exception(
+ "Could not open {$this->filename} for writing see " . __CLASS__ . '::setFileName()'
+ );
+ }
+
+ fwrite($this->fh, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ fwrite($this->fh, "<dataset>\n");
+ }
+
+ /**
+ * Override to save the end of a dataset.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ fwrite($this->fh, "</dataset>\n");
+ }
+
+ /**
+ * Override to save the start of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ fwrite($this->fh, "\t<table name=\"{$table->getTableMetaData()->getTableName()}\">\n");
+
+ foreach ($table->getTableMetaData()->getColumns() as $columnName) {
+ fwrite($this->fh, "\t\t<column>{$columnName}</column>\n");
+ }
+ }
+
+ /**
+ * Override to save the end of a table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ fwrite($this->fh, "\t</table>\n");
+ }
+
+ /**
+ * Override to save a table row.
+ *
+ * @param array $row
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ */
+ protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table)
+ {
+ fwrite($this->fh, "\t\t<row>\n");
+
+ foreach ($table->getTableMetaData()->getColumns() as $columnName) {
+ if (isset($row[$columnName])) {
+ fwrite($this->fh, "\t\t\t<value>" . htmlspecialchars($row[$columnName]) . "</value>\n");
+ } else {
+ fwrite($this->fh, "\t\t\t<null />\n");
+ }
+ }
+
+ fwrite($this->fh, "\t\t</row>\n");
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A yaml dataset persistor
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Persistors_Yaml implements PHPUnit_Extensions_Database_DataSet_IPersistable
+{
+ /**
+ * @var string
+ */
+ protected $filename;
+
+ /**
+ * Sets the filename that this persistor will save to.
+ *
+ * @param string $filename
+ */
+ public function setFileName($filename)
+ {
+ $this->filename = $filename;
+ }
+
+ /**
+ * Writes the dataset to a yaml file
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ */
+ public function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset)
+ {
+ $phpArr = [];
+ $emptyTables = [];
+
+ foreach ($dataset as $table) {
+ $tableName = $table->getTableMetaData()->getTableName();
+ $rowCount = $table->getRowCount();
+
+ if (!$rowCount) {
+ $emptyTables[] = $tableName;
+ continue;
+ }
+
+ $phpArr[$tableName] = [];
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ $phpArr[$tableName][] = $table->getRow($i);
+ }
+ }
+
+ $emptyTablesAsString = '';
+
+ if (count($emptyTables)) {
+ $emptyTablesAsString = implode(":\n", $emptyTables) . ":\n\n";
+ }
+
+ file_put_contents(
+ $this->filename,
+ Symfony\Component\Yaml\Yaml::dump($phpArr, 3) . $emptyTablesAsString
+ );
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides access to a database instance as a data set.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_QueryDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * An array of ITable objects.
+ *
+ * @var array
+ */
+ protected $tables = [];
+
+ /**
+ * The database connection this dataset is using.
+ *
+ * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ */
+ protected $databaseConnection;
+
+ /**
+ * Creates a new dataset using the given database connection.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection
+ */
+ public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection)
+ {
+ $this->databaseConnection = $databaseConnection;
+ }
+
+ public function addTable($tableName, $query = NULL)
+ {
+ if ($query === NULL) {
+ $query = 'SELECT * FROM ' . $tableName;
+ }
+
+ $this->tables[$tableName] = new PHPUnit_Extensions_Database_DataSet_QueryTable($tableName, $query, $this->databaseConnection);
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DB_TableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse);
+ }
+
+ /**
+ * Returns a table object for the given table.
+ *
+ * @param string $tableName
+ * @return PHPUnit_Extensions_Database_DB_Table
+ */
+ public function getTable($tableName)
+ {
+ if (!isset($this->tables[$tableName])) {
+ throw new InvalidArgumentException("$tableName is not a table in the current database.");
+ }
+
+ return $this->tables[$tableName];
+ }
+
+ /**
+ * Returns a list of table names for the database
+ *
+ * @return Array
+ */
+ public function getTableNames()
+ {
+ return array_keys($this->tables);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides the functionality to represent a database table.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_QueryTable extends PHPUnit_Extensions_Database_DataSet_AbstractTable
+{
+ /**
+ * @var string
+ */
+ protected $query;
+
+ /**
+ * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ */
+ protected $databaseConnection;
+
+ /**
+ * @var string
+ */
+ protected $tableName;
+
+ /**
+ * Creates a new database query table object.
+ *
+ * @param string $table_name
+ * @param string $query
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection
+ */
+ public function __construct($tableName, $query, PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection)
+ {
+ $this->query = $query;
+ $this->databaseConnection = $databaseConnection;
+ $this->tableName = $tableName;
+ }
+
+ /**
+ * Returns the table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData()
+ {
+ $this->createTableMetaData();
+
+ return parent::getTableMetaData();
+ }
+
+ /**
+ * Checks if a given row is in the table
+ *
+ * @param array $row
+ *
+ * @return bool
+ */
+ public function assertContainsRow(Array $row)
+ {
+ $this->loadData();
+
+ return parent::assertContainsRow($row);
+ }
+
+ /**
+ * Returns the number of rows in this table.
+ *
+ * @return int
+ */
+ public function getRowCount()
+ {
+ $this->loadData();
+
+ return parent::getRowCount();
+ }
+
+ /**
+ * Returns the value for the given column on the given row.
+ *
+ * @param int $row
+ * @param int $column
+ */
+ public function getValue($row, $column)
+ {
+ $this->loadData();
+
+ return parent::getValue($row, $column);
+ }
+
+ /**
+ * Returns the an associative array keyed by columns for the given row.
+ *
+ * @param int $row
+ * @return array
+ */
+ public function getRow($row)
+ {
+ $this->loadData();
+
+ return parent::getRow($row);
+ }
+
+ /**
+ * Asserts that the given table matches this table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other)
+ {
+ $this->loadData();
+
+ return parent::matches($other);
+ }
+
+ protected function loadData()
+ {
+ if ($this->data === NULL) {
+ $pdoStatement = $this->databaseConnection->getConnection()->query($this->query);
+ $this->data = $pdoStatement->fetchAll(PDO::FETCH_ASSOC);
+ }
+ }
+
+ protected function createTableMetaData()
+ {
+ if ($this->tableMetaData === NULL)
+ {
+ $this->loadData();
+
+ // if some rows are in the table
+ $columns = [];
+ if (isset($this->data[0]))
+ // get column names from data
+ $columns = array_keys($this->data[0]);
+ else {
+ // if no rows found, get column names from database
+ $pdoStatement = $this->databaseConnection->getConnection()->prepare('SELECT column_name FROM information_schema.COLUMNS WHERE table_schema=:schema AND table_name=:table');
+ $pdoStatement->execute([
+ 'table' => $this->tableName,
+ 'schema' => $this->databaseConnection->getSchema()
+ ]);
+
+ $columns = $pdoStatement->fetchAll(PDO::FETCH_COLUMN, 0);
+ }
+ // create metadata
+ $this->tableMetaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($this->tableName, $columns);
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Allows for replacing arbitrary values or portions of values with new data.
+ *
+ * A usage for this is replacing all values == '[NULL'] with a true NULL value
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_ReplacementDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ protected $dataSet;
+
+ /**
+ * @var array
+ */
+ protected $fullReplacements;
+
+ /**
+ * @var array
+ */
+ protected $subStrReplacements;
+
+ /**
+ * Creates a new replacement dataset
+ *
+ * You can pass in any data set that implements PHPUnit_Extensions_Database_DataSet_IDataSet
+ *
+ * @param string $delimiter
+ * @param string $enclosure
+ * @param string $escape
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet, Array $fullReplacements = [], Array $subStrReplacements = [])
+ {
+ $this->dataSet = $dataSet;
+ $this->fullReplacements = $fullReplacements;
+ $this->subStrReplacements = $subStrReplacements;
+ }
+
+ /**
+ * Adds a new full replacement
+ *
+ * Full replacements will only replace values if the FULL value is a match
+ *
+ * @param string $value
+ * @param string $replacement
+ */
+ public function addFullReplacement($value, $replacement)
+ {
+ $this->fullReplacements[$value] = $replacement;
+ }
+
+ /**
+ * Adds a new substr replacement
+ *
+ * Substr replacements will replace all occurances of the substr in every column
+ *
+ * @param string $value
+ * @param string $replacement
+ */
+ public function addSubStrReplacement($value, $replacement)
+ {
+ $this->subStrReplacements[$value] = $replacement;
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ $innerIterator = $reverse ? $this->dataSet->getReverseIterator() : $this->dataSet->getIterator();
+
+ return new PHPUnit_Extensions_Database_DataSet_ReplacementTableIterator($innerIterator, $this->fullReplacements, $this->subStrReplacements);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Allows for replacing arbitrary strings in your data sets with other values.
+ *
+ * @since Class available since Release 1.0.0
+ * @todo When setTableMetaData() is taken out of the AbstractTable this class should extend AbstractTable.
+ */
+class PHPUnit_Extensions_Database_DataSet_ReplacementTable implements PHPUnit_Extensions_Database_DataSet_ITable
+{
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ protected $table;
+
+ /**
+ * @var array
+ */
+ protected $fullReplacements;
+
+ /**
+ * @var array
+ */
+ protected $subStrReplacements;
+
+ /**
+ * Creates a new replacement table
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ * @param array $fullReplacements
+ * @param array $subStrReplacements
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_ITable $table, Array $fullReplacements = [], Array $subStrReplacements = [])
+ {
+ $this->table = $table;
+ $this->fullReplacements = $fullReplacements;
+ $this->subStrReplacements = $subStrReplacements;
+ }
+
+ /**
+ * Adds a new full replacement
+ *
+ * Full replacements will only replace values if the FULL value is a match
+ *
+ * @param string $value
+ * @param string $replacement
+ */
+ public function addFullReplacement($value, $replacement)
+ {
+ $this->fullReplacements[$value] = $replacement;
+ }
+
+ /**
+ * Adds a new substr replacement
+ *
+ * Substr replacements will replace all occurances of the substr in every column
+ *
+ * @param string $value
+ * @param string $replacement
+ */
+ public function addSubStrReplacement($value, $replacement)
+ {
+ $this->subStrReplacements[$value] = $replacement;
+ }
+
+ /**
+ * Returns the table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData()
+ {
+ return $this->table->getTableMetaData();
+ }
+
+ /**
+ * Returns the number of rows in this table.
+ *
+ * @return int
+ */
+ public function getRowCount()
+ {
+ return $this->table->getRowCount();
+ }
+
+ /**
+ * Returns the value for the given column on the given row.
+ *
+ * @param int $row
+ * @param int $column
+ */
+ public function getValue($row, $column)
+ {
+ return $this->getReplacedValue($this->table->getValue($row, $column));
+ }
+
+ /**
+ * Returns the an associative array keyed by columns for the given row.
+ *
+ * @param int $row
+ * @return array
+ */
+ public function getRow($row)
+ {
+ $row = $this->table->getRow($row);
+
+ return array_map([$this, 'getReplacedValue'], $row);
+ }
+
+ /**
+ * Asserts that the given table matches this table.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $other
+ */
+ public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other)
+ {
+ $thisMetaData = $this->getTableMetaData();
+ $otherMetaData = $other->getTableMetaData();
+
+ if (!$thisMetaData->matches($otherMetaData) ||
+ $this->getRowCount() != $other->getRowCount()) {
+ return FALSE;
+ }
+
+ $columns = $thisMetaData->getColumns();
+ $rowCount = $this->getRowCount();
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ foreach ($columns as $columnName) {
+ $thisValue = $this->getValue($i, $columnName);
+ $otherValue = $other->getValue($i, $columnName);
+ if (is_numeric($thisValue) && is_numeric($otherValue)) {
+ if ($thisValue != $otherValue) {
+ return FALSE;
+ }
+ } elseif ($thisValue !== $otherValue) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+ }
+
+ public function __toString()
+ {
+ $columns = $this->getTableMetaData()->getColumns();
+
+ $lineSeperator = str_repeat('+----------------------', count($columns)) . "+\n";
+ $lineLength = strlen($lineSeperator) - 1;
+
+ $tableString = $lineSeperator;
+ $tableString .= '| ' . str_pad($this->getTableMetaData()->getTableName(), $lineLength - 4, ' ', STR_PAD_RIGHT) . " |\n";
+ $tableString .= $lineSeperator;
+ $tableString .= $this->rowToString($columns);
+ $tableString .= $lineSeperator;
+
+ $rowCount = $this->getRowCount();
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ $values = [];
+
+ foreach ($columns as $columnName) {
+ $values[] = $this->getValue($i, $columnName);
+ }
+
+ $tableString .= $this->rowToString($values);
+ $tableString .= $lineSeperator;
+ }
+
+ return "\n" . $tableString . "\n";
+ }
+
+ protected function rowToString(Array $row)
+ {
+ $rowString = '';
+
+ foreach ($row as $value) {
+ if (is_null($value)) {
+ $value = 'NULL';
+ }
+
+ $rowString .= '| ' . str_pad(substr($value, 0, 20), 20, ' ', STR_PAD_BOTH) . ' ';
+ }
+
+ return $rowString . "|\n";
+ }
+
+ protected function getReplacedValue($value)
+ {
+ if (is_scalar($value) && array_key_exists((string) $value, $this->fullReplacements)) {
+ return $this->fullReplacements[$value];
+ }
+
+ else if (count($this->subStrReplacements) && isset($value)) {
+ return str_replace(array_keys($this->subStrReplacements), array_values($this->subStrReplacements), $value);
+ }
+
+ else {
+ return $value;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default table iterator
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_ReplacementTableIterator implements OuterIterator, PHPUnit_Extensions_Database_DataSet_ITableIterator
+{
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected $innerIterator;
+
+ /**
+ * @var array
+ */
+ protected $fullReplacements;
+
+ /**
+ * @var array
+ */
+ protected $subStrReplacements;
+
+ /**
+ * Creates a new replacement table iterator object.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableIterator $innerIterator
+ * @param array $fullReplacements
+ * @param array $subStrReplacements
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_ITableIterator $innerIterator, Array $fullReplacements = [], Array $subStrReplacements = [])
+ {
+ $this->innerIterator = $innerIterator;
+ $this->fullReplacements = $fullReplacements;
+ $this->subStrReplacements = $subStrReplacements;
+ }
+
+ /**
+ * Adds a new full replacement
+ *
+ * Full replacements will only replace values if the FULL value is a match
+ *
+ * @param string $value
+ * @param string $replacement
+ */
+ public function addFullReplacement($value, $replacement)
+ {
+ $this->fullReplacements[$value] = $replacement;
+ }
+
+ /**
+ * Adds a new substr replacement
+ *
+ * Substr replacements will replace all occurances of the substr in every column
+ *
+ * @param string $value
+ * @param string $replacement
+ */
+ public function addSubStrReplacement($value, $replacement)
+ {
+ $this->subStrReplacements[$value] = $replacement;
+ }
+
+ /**
+ * Returns the current table.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function getTable()
+ {
+ return $this->current();
+ }
+
+ /**
+ * Returns the current table's meta data.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ public function getTableMetaData()
+ {
+ $this->current()->getTableMetaData();
+ }
+
+ /**
+ * Returns the current table.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ public function current()
+ {
+ return new PHPUnit_Extensions_Database_DataSet_ReplacementTable($this->innerIterator->current(), $this->fullReplacements, $this->subStrReplacements);
+ }
+
+ /**
+ * Returns the name of the current table.
+ *
+ * @return string
+ */
+ public function key()
+ {
+ return $this->current()->getTableMetaData()->getTableName();
+ }
+
+ /**
+ * advances to the next element.
+ */
+ public function next()
+ {
+ $this->innerIterator->next();
+ }
+
+ /**
+ * Rewinds to the first element
+ */
+ public function rewind()
+ {
+ $this->innerIterator->rewind();
+ }
+
+ /**
+ * Returns true if the current index is valid
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return $this->innerIterator->valid();
+ }
+
+ public function getInnerIterator()
+ {
+ return $this->innerIterator;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates CsvDataSets based off of a spec string.
+ *
+ * The format of the spec string is as follows:
+ *
+ * <csv options>|table1:filename.csv,table2:filename2.csv
+ *
+ * The first portion of the spec including the pipe symbol '|' is optional.
+ * If the pipe option is included than it may be preceded by up to four
+ * characters specifying values for the following arguments in order:
+ * delimiter (defaults to ',',) enclosure (defaults to '"',) escape (defaults to '"',).
+ *
+ * Any additional characters in the csv options will be discarded.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Specs_Csv implements PHPUnit_Extensions_Database_DataSet_ISpec
+{
+ /**
+ * Creates CSV Data Set from a data set spec.
+ *
+ * @param string $dataSetSpec
+ * @return PHPUnit_Extensions_Database_DataSet_CsvDataSet
+ */
+ public function getDataSet($dataSetSpec)
+ {
+ $csvDataSetArgs = $this->getCsvOptions($dataSetSpec);
+ $csvDataSetRfl = new ReflectionClass('PHPUnit_Extensions_Database_DataSet_CsvDataSet');
+ $csvDataSet = $csvDataSetRfl->newInstanceArgs($csvDataSetArgs);
+
+ foreach ($this->getTableFileMap($dataSetSpec) as $tableName => $file) {
+ $csvDataSet->addTable($tableName, $file);
+ }
+
+ return $csvDataSet;
+ }
+
+ /**
+ * Returns CSV options.
+ *
+ * Returns an array containing the options that will be passed to the
+ * PHPUnit_Extensions_Database_DataSet_CsvDataSet constructor. The options
+ * are determined by the given $dataSetSpec.
+ *
+ * @param string $dataSetSpec
+ * @return array
+ */
+ protected function getCsvOptions($dataSetSpec)
+ {
+ list($csvOptStr) = explode('|', $dataSetSpec, 2);
+
+ return str_split($csvOptStr);
+ }
+
+ /**
+ * Returns map of tables to files.
+ *
+ * Returns an associative array containing a mapping of tables (the key)
+ * to files (the values.) The tables and files are determined by the given
+ * $dataSetSpec
+ *
+ * @param string $dataSetSpec
+ * @return array
+ */
+ protected function getTableFileMap($dataSetSpec)
+ {
+ $tables = [];
+
+ foreach (explode(',', $dataSetSpec) as $csvfile) {
+ list($tableName, $file) = explode(':', $csvfile, 2);
+ $tables[$tableName] = $file;
+ }
+
+ return $tables;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates DefaultDataSets based off of a spec string.
+ *
+ * This spec class requires a list of databases to be set to the object before
+ * it can return a list of databases.
+ *
+ * The format of the spec string is as follows:
+ *
+ * <db label>:<schema>:<table name>:<sql>
+ *
+ * The db label should be equal to one of the keys in the array of databases
+ * passed to setDatabases().
+ *
+ * The schema should be the primary schema you will be running the sql query
+ * against.
+ *
+ * The table name should be set to what you would like the table name in the
+ * dataset to be.
+ *
+ * The sql is the query you want to use to generate the table columns and data.
+ * The column names in the table will be identical to the column aliases in the
+ * query.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Specs_DbQuery implements PHPUnit_Extensions_Database_DataSet_ISpec, PHPUnit_Extensions_Database_IDatabaseListConsumer
+{
+ /**
+ * @var array
+ */
+ protected $databases = [];
+
+ /**
+ * Sets the database for the spec
+ *
+ * @param array $databases
+ */
+ public function setDatabases(array $databases)
+ {
+ $this->databases = $databases;
+ }
+
+ /**
+ * Creates a Default Data Set with a query table from a data set spec.
+ *
+ * @param string $dataSetSpec
+ * @return PHPUnit_Extensions_Database_DataSet_DefaultDataSet
+ */
+ public function getDataSet($dataSetSpec)
+ {
+ list($dbLabel, $schema, $table, $sql) = explode(':', $dataSetSpec, 4);
+ $databaseInfo = $this->databases[$dbLabel];
+
+ $pdoRflc = new ReflectionClass('PDO');
+ $pdo = $pdoRflc->newInstanceArgs(explode('|', $databaseInfo));
+ $dbConnection = new PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection($pdo, $schema);
+ $table = $dbConnection->createQueryTable($table, $sql);
+
+ return new PHPUnit_Extensions_Database_DataSet_DefaultDataSet([$table]);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates a database dataset based off of a spec string.
+ *
+ * This spec class requires a list of databases to be set to the object before
+ * it can return a list of databases.
+ *
+ * The format of the spec string is as follows:
+ *
+ * <db label>:<schema>:<tables>
+ *
+ * The db label should be equal to one of the keys in the array of databases
+ * passed to setDatabases().
+ *
+ * The schema should be the primary schema you will be choosing tables from.
+ *
+ * The tables should be a comma delimited list of all tables you would like to
+ * pull data from.
+ *
+ * The sql is the query you want to use to generate the table columns and data.
+ * The column names in the table will be identical to the column aliases in the
+ * query.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Specs_DbTable implements PHPUnit_Extensions_Database_DataSet_ISpec, PHPUnit_Extensions_Database_IDatabaseListConsumer
+{
+ /**
+ * @var array
+ */
+ protected $databases = [];
+
+ /**
+ * Sets the database for the spec
+ *
+ * @param array $databases
+ */
+ public function setDatabases(array $databases)
+ {
+ $this->databases = $databases;
+ }
+
+ /**
+ * Creates a DB Data Set from a data set spec.
+ *
+ * @param string $dataSetSpec
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ public function getDataSet($dataSetSpec)
+ {
+ list($dbLabel, $schema, $tables) = explode(':', $dataSetSpec, 3);
+ $databaseInfo = $this->databases[$dbLabel];
+
+ $pdoRflc = new ReflectionClass('PDO');
+ $pdo = $pdoRflc->newInstanceArgs(explode('|', $databaseInfo));
+ $dbConnection = new PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection($pdo, $schema);
+
+ return !empty($tables) ? $dbConnection->createDataSet(explode(',', $tables)) : $dbConnection->createDataSet();
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates the appropriate DataSet Spec based on a given type.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Specs_Factory implements PHPUnit_Extensions_Database_DataSet_Specs_IFactory
+{
+ /**
+ * Returns the data set
+ *
+ * @param string $type
+ * @return PHPUnit_Extensions_Database_DataSet_ISpec
+ */
+ public function getDataSetSpecByType($type)
+ {
+ switch ($type) {
+ case 'xml':
+ return new PHPUnit_Extensions_Database_DataSet_Specs_Xml();
+
+ case 'flatxml':
+ return new PHPUnit_Extensions_Database_DataSet_Specs_FlatXml();
+
+ case 'csv':
+ return new PHPUnit_Extensions_Database_DataSet_Specs_Csv();
+
+ case 'yaml':
+ return new PHPUnit_Extensions_Database_DataSet_Specs_Yaml();
+
+ case 'dbtable':
+ return new PHPUnit_Extensions_Database_DataSet_Specs_DbTable();
+
+ case 'dbquery':
+ return new PHPUnit_Extensions_Database_DataSet_Specs_DbQuery();
+
+ default:
+ throw new PHPUnit_Extensions_Database_Exception("I don't know what you want from me.");
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates a FlatXML dataset based off of a spec string.
+ *
+ * The format of the spec string is as follows:
+ *
+ * <filename>
+ *
+ * The filename should be the location of a flat xml file relative to the
+ * current working directory.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Specs_FlatXml implements PHPUnit_Extensions_Database_DataSet_ISpec
+{
+ /**
+ * Creates Flat XML Data Set from a data set spec.
+ *
+ * @param string $dataSetSpec
+ * @return PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet
+ */
+ public function getDataSet($dataSetSpec)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet($dataSetSpec);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An interface for data set spec factories.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_DataSet_Specs_IFactory
+{
+ /**
+ * Returns the data set
+ *
+ * @param string $type
+ * @return PHPUnit_Extensions_Database_DataSet_ISpec
+ */
+ public function getDataSetSpecByType($type);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates a XML dataset based off of a spec string.
+ *
+ * The format of the spec string is as follows:
+ *
+ * <filename>
+ *
+ * The filename should be the location of a xml file relative to the
+ * current working directory.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Specs_Xml implements PHPUnit_Extensions_Database_DataSet_ISpec
+{
+ /**
+ * Creates XML Data Set from a data set spec.
+ *
+ * @param string $dataSetSpec
+ * @return PHPUnit_Extensions_Database_DataSet_XmlDataSet
+ */
+ public function getDataSet($dataSetSpec)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_XmlDataSet($dataSetSpec);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates a YAML dataset based off of a spec string.
+ *
+ * The format of the spec string is as follows:
+ *
+ * <filename>
+ *
+ * The filename should be the location of a yaml file relative to the
+ * current working directory.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_Specs_Yaml implements PHPUnit_Extensions_Database_DataSet_ISpec
+{
+ /**
+ * Creates YAML Data Set from a data set spec.
+ *
+ * @param string $dataSetSpec
+ * @return PHPUnit_Extensions_Database_DataSet_YamlDataSet
+ */
+ public function getDataSet($dataSetSpec)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_YamlDataSet($dataSetSpec);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default YAML parser, using Symfony/Yaml.
+ *
+ * @since Class available since Release 1.3.1
+ */
+class PHPUnit_Extensions_Database_DataSet_SymfonyYamlParser implements PHPUnit_Extensions_Database_DataSet_IYamlParser {
+ public function parseYaml($yamlFile) {
+ return Symfony\Component\Yaml\Yaml::parse(file_get_contents($yamlFile));
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A table decorator that allows filtering out table columns from results.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_TableFilter extends PHPUnit_Extensions_Database_DataSet_AbstractTable
+{
+ /**
+ * The table meta data being decorated.
+ * @var PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ protected $originalTable;
+
+ /**
+ * Creates a new table filter using the original table
+ *
+ * @param $originalTable PHPUnit_Extensions_Database_DataSet_ITable
+ * @param $excludeColumns Array @deprecated, use the set* methods instead.
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_ITable $originalTable, Array $excludeColumns = [])
+ {
+ $this->originalTable = $originalTable;
+ $this->setTableMetaData(new PHPUnit_Extensions_Database_DataSet_TableMetaDataFilter($originalTable->getTableMetaData()));
+ $this->addExcludeColumns($excludeColumns);
+ }
+
+ /**
+ * Returns the number of rows in this table.
+ *
+ * @return int
+ */
+ public function getRowCount()
+ {
+ return $this->originalTable->getRowCount();
+ }
+
+ /**
+ * Returns the value for the given column on the given row.
+ *
+ * @param int $row
+ * @param int $column
+ */
+ public function getValue($row, $column)
+ {
+ if (in_array($column, $this->getTableMetaData()->getColumns())) {
+ return $this->originalTable->getValue($row, $column);
+ } else {
+ throw new InvalidArgumentException("The given row ({$row}) and column ({$column}) do not exist in table {$this->getTableMetaData()->getTableName()}");
+ }
+ }
+
+ /**
+ * Sets the columns to include in the table.
+ * @param Array $includeColumns
+ */
+ public function addIncludeColumns(Array $includeColumns)
+ {
+ $this->tableMetaData->addIncludeColumns($includeColumns);
+ }
+
+ /**
+ * Clears the included columns.
+ */
+ public function clearIncludeColumns()
+ {
+ $this->tableMetaData->clearIncludeColumns();
+ }
+
+ /**
+ * Sets the columns to exclude from the table.
+ * @param Array $excludeColumns
+ */
+ public function addExcludeColumns(Array $excludeColumns)
+ {
+ $this->tableMetaData->addExcludeColumns($excludeColumns);
+ }
+
+ /**
+ * Clears the included columns.
+ */
+ public function clearExcludeColumns()
+ {
+ $this->tableMetaData->clearExcludeColumns();
+ }
+
+ /**
+ * Checks if a given row is in the table
+ *
+ * @param array $row
+ *
+ * @return bool
+ */
+ public function assertContainsRow(Array $row)
+ {
+ $this->loadData();
+
+ return parent::assertContainsRow($row);
+ }
+
+ /**
+ * Loads data into local data table if it's not already loaded
+ */
+ protected function loadData()
+ {
+ if ($this->data === NULL) {
+ $data = [];
+ for($row = 0;$row < $this->originalTable->getRowCount();$row++) {
+ $tRow = [];
+ foreach($this->getTableMetaData()->getColumns() as $col) {
+ $tRow[$col] = $this->getValue($row, $col);
+ }
+ $data[$row] = $tRow;
+ }
+ $this->data = $data;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TableMetaData decorator that allows filtering columns from another
+ * metaData object.
+ *
+ * The if a whitelist (include) filter is specified, then only those columns
+ * will be included.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_TableMetaDataFilter extends PHPUnit_Extensions_Database_DataSet_AbstractTableMetaData
+{
+ /**
+ * The table meta data being decorated.
+ * @var PHPUnit_Extensions_Database_DataSet_ITableMetaData
+ */
+ protected $originalMetaData;
+
+ /**
+ * The columns to exclude from the meta data.
+ * @var Array
+ */
+ protected $excludeColumns = [];
+
+ /**
+ * The columns to include from the meta data.
+ * @var Array
+ */
+ protected $includeColumns = [];
+
+ /**
+ * Creates a new filtered table meta data object filtering out
+ * $excludeColumns.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $originalMetaData
+ * @param array $excludeColumns - Deprecated. Use the set* methods instead.
+ */
+ public function __construct(PHPUnit_Extensions_Database_DataSet_ITableMetaData $originalMetaData, Array $excludeColumns = [])
+ {
+ $this->originalMetaData = $originalMetaData;
+ $this->addExcludeColumns($excludeColumns);
+ }
+
+ /**
+ * Returns the names of the columns in the table.
+ *
+ * @return array
+ */
+ public function getColumns()
+ {
+ if (!empty($this->includeColumns)) {
+ return array_values(array_intersect($this->originalMetaData->getColumns(), $this->includeColumns));
+ }
+ elseif (!empty($this->excludeColumns)) {
+ return array_values(array_diff($this->originalMetaData->getColumns(), $this->excludeColumns));
+ }
+ else {
+ return $this->originalMetaData->getColumns();
+ }
+ }
+
+ /**
+ * Returns the names of the primary key columns in the table.
+ *
+ * @return array
+ */
+ public function getPrimaryKeys()
+ {
+ return $this->originalMetaData->getPrimaryKeys();
+ }
+
+ /**
+ * Returns the name of the table.
+ *
+ * @return string
+ */
+ public function getTableName()
+ {
+ return $this->originalMetaData->getTableName();
+ }
+
+ /**
+ * Sets the columns to include in the table.
+ * @param Array $includeColumns
+ */
+ public function addIncludeColumns(Array $includeColumns)
+ {
+ $this->includeColumns = array_unique(array_merge($this->includeColumns, $includeColumns));
+ }
+
+ /**
+ * Clears the included columns.
+ */
+ public function clearIncludeColumns()
+ {
+ $this->includeColumns = [];
+ }
+
+ /**
+ * Sets the columns to exclude from the table.
+ * @param Array $excludeColumns
+ */
+ public function addExcludeColumns(Array $excludeColumns)
+ {
+ $this->excludeColumns = array_unique(array_merge($this->excludeColumns, $excludeColumns));
+ }
+
+ /**
+ * Clears the excluded columns.
+ */
+ public function clearExcludeColumns()
+ {
+ $this->excludeColumns = [];
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default implementation of a data set.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_XmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet
+{
+ protected function getTableInfo(Array &$tableColumns, Array &$tableValues)
+ {
+ if ($this->xmlFileContents->getName() != 'dataset') {
+ throw new PHPUnit_Extensions_Database_Exception('The root element of an xml data set file must be called <dataset>');
+ }
+
+ foreach ($this->xmlFileContents->xpath('/dataset/table') as $tableElement) {
+ if (empty($tableElement['name'])) {
+ throw new PHPUnit_Extensions_Database_Exception('Table elements must include a name attribute specifying the table name.');
+ }
+
+ $tableName = (string) $tableElement['name'];
+
+ if (!isset($tableColumns[$tableName])) {
+ $tableColumns[$tableName] = [];
+ }
+
+ if (!isset($tableValues[$tableName])) {
+ $tableValues[$tableName] = [];
+ }
+
+ $tableInstanceColumns = [];
+
+ foreach ($tableElement->xpath('./column') as $columnElement) {
+ $columnName = (string) $columnElement;
+ if (empty($columnName)) {
+ throw new PHPUnit_Extensions_Database_Exception("Missing <column> elements for table $tableName. Add one or more <column> elements to the <table> element.");
+ }
+
+ if (!in_array($columnName, $tableColumns[$tableName])) {
+ $tableColumns[$tableName][] = $columnName;
+ }
+
+ $tableInstanceColumns[] = $columnName;
+ }
+
+ foreach ($tableElement->xpath('./row') as $rowElement) {
+ $rowValues = [];
+ $index = 0;
+ $numOfTableInstanceColumns = count($tableInstanceColumns);
+
+ foreach ($rowElement->children() as $columnValue) {
+
+ if ($index >= $numOfTableInstanceColumns) {
+ throw new PHPUnit_Extensions_Database_Exception("Row contains more values than the number of columns defined for table $tableName.");
+ }
+ switch ($columnValue->getName()) {
+ case 'value':
+ $rowValues[$tableInstanceColumns[$index]] = (string) $columnValue;
+ $index++;
+ break;
+ case 'null':
+ $rowValues[$tableInstanceColumns[$index]] = NULL;
+ $index++;
+ break;
+ default:
+ throw new PHPUnit_Extensions_Database_Exception('Unknown element ' . $columnValue->getName() . ' in a row element.');
+ }
+ }
+
+ $tableValues[$tableName][] = $rowValues;
+ }
+ }
+ }
+
+ public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename)
+ {
+ $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_Xml();
+ $pers->setFileName($filename);
+
+ try {
+ $pers->write($dataset);
+ }
+
+ catch (RuntimeException $e) {
+ throw new PHPUnit_Framework_Exception(__METHOD__ . ' called with an unwritable file.');
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates YamlDataSets.
+ *
+ * You can incrementally add YAML files as tables to your datasets
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DataSet_YamlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet
+{
+ /**
+ * @var array
+ */
+ protected $tables = [];
+
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_IYamlParser
+ */
+ protected $parser;
+
+ /**
+ * Creates a new YAML dataset
+ *
+ * @param string $yamlFile
+ * @param PHPUnit_Extensions_Database_DataSet_IYamlParser $parser
+ */
+ public function __construct($yamlFile, $parser = NULL)
+ {
+ if ($parser == NULL) {
+ $parser = new PHPUnit_Extensions_Database_DataSet_SymfonyYamlParser();
+ }
+ $this->parser = $parser;
+ $this->addYamlFile($yamlFile);
+ }
+
+ /**
+ * Adds a new yaml file to the dataset.
+ * @param string $yamlFile
+ */
+ public function addYamlFile($yamlFile)
+ {
+ $data = $this->parser->parseYaml($yamlFile);
+
+ foreach ($data as $tableName => $rows) {
+ if (!isset($rows)) {
+ $rows = [];
+ }
+
+ if (!is_array($rows)) {
+ continue;
+ }
+
+ if (!array_key_exists($tableName, $this->tables)) {
+ $columns = $this->getColumns($rows);
+
+ $tableMetaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData(
+ $tableName, $columns
+ );
+
+ $this->tables[$tableName] = new PHPUnit_Extensions_Database_DataSet_DefaultTable(
+ $tableMetaData
+ );
+ }
+
+ foreach ($rows as $row) {
+ $this->tables[$tableName]->addRow($row);
+ }
+ }
+ }
+
+ /**
+ * Creates a unique list of columns from all the rows in a table.
+ * If the table is defined another time in the Yaml, and if the Yaml
+ * parser could return the multiple occerrences, then this would be
+ * insufficient unless we grouped all the occurences of the table
+ * into onwe row set. sfYaml, however, does not provide multiple tables
+ * with the same name, it only supplies the last table.
+ *
+ * @params all the rows in a table.
+ */
+ private function getColumns($rows) {
+ $columns = [];
+
+ foreach ($rows as $row) {
+ $columns = array_merge($columns, array_keys($row));
+ }
+
+ return array_values(array_unique($columns));
+ }
+
+ /**
+ * Creates an iterator over the tables in the data set. If $reverse is
+ * true a reverse iterator will be returned.
+ *
+ * @param bool $reverse
+ * @return PHPUnit_Extensions_Database_DataSet_ITableIterator
+ */
+ protected function createIterator($reverse = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator(
+ $this->tables, $reverse
+ );
+ }
+
+ /**
+ * Saves a given $dataset to $filename in YAML format
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset
+ * @param string $filename
+ */
+ public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename)
+ {
+ $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_Yaml();
+ $pers->setFileName($filename);
+
+ try {
+ $pers->write($dataset);
+ }
+
+ catch (RuntimeException $e) {
+ throw new PHPUnit_Framework_Exception(
+ __METHOD__ . ' called with an unwritable file.'
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This is the default implementation of the database tester. It receives its
+ * connection object from the constructor.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_DefaultTester extends PHPUnit_Extensions_Database_AbstractTester
+{
+ /**
+ * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ */
+ protected $connection;
+
+ /**
+ * Creates a new default database tester using the given connection.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ */
+ public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ parent::__construct();
+
+ $this->connection = $connection;
+ }
+
+ /**
+ * Returns the test database connection.
+ *
+ * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ */
+ public function getConnection()
+ {
+ return $this->connection;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Thrown for exceptions encountered with database operations. Provides
+ * information regarding which operations failed and the query (if any) it
+ * failed on.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Exception extends Exception
+{
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An interface for classes that require a list of databases to operate.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_IDatabaseListConsumer
+{
+ /**
+ * Sets the database for the spec
+ *
+ * @param array $databases
+ */
+ public function setDatabases(array $databases);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This is the interface for DatabaseTester objects. These objects are used to
+ * add database testing to existing test cases using composition instead of
+ * extension.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_ITester
+{
+ /**
+ * Closes the specified connection.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ */
+ public function closeConnection(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection);
+
+ /**
+ * Returns the test database connection.
+ *
+ * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ */
+ public function getConnection();
+
+ /**
+ * Returns the test dataset.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ public function getDataSet();
+
+ /**
+ * TestCases must call this method inside setUp().
+ */
+ public function onSetUp();
+
+ /**
+ * TestCases must call this method inside tearDown().
+ */
+ public function onTearDown();
+
+ /**
+ * Sets the test dataset to use.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet
+ */
+ public function setDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet);
+
+ /**
+ * Sets the schema value.
+ *
+ * @param string $schema
+ */
+ public function setSchema($schema);
+
+ /**
+ * Sets the DatabaseOperation to call when starting the test.
+ *
+ * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $setUpOperation
+ */
+ public function setSetUpOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $setUpOperation);
+
+ /**
+ * Sets the DatabaseOperation to call when stopping the test.
+ *
+ * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $tearDownOperation
+ */
+ public function setTearDownOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $tearDownOperation);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This class facilitates combining database operations. To create a composite
+ * operation pass an array of classes that implement
+ * PHPUnit_Extensions_Database_Operation_IDatabaseOperation and they will be
+ * executed in that order against all data sets.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Composite implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+{
+ /**
+ * @var array
+ */
+ protected $operations = [];
+
+ /**
+ * Creates a composite operation.
+ *
+ * @param array $operations
+ */
+ public function __construct(Array $operations)
+ {
+ foreach ($operations as $operation) {
+ if ($operation instanceof PHPUnit_Extensions_Database_Operation_IDatabaseOperation) {
+ $this->operations[] = $operation;
+ } else {
+ throw new InvalidArgumentException('Only database operation instances can be passed to a composite database operation.');
+ }
+ }
+ }
+
+ public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ try {
+ foreach ($this->operations as $operation) {
+ /* @var $operation PHPUnit_Extensions_Database_Operation_IDatabaseOperation */
+ $operation->execute($connection, $dataSet);
+ }
+ } catch (PHPUnit_Extensions_Database_Operation_Exception $e) {
+ throw new PHPUnit_Extensions_Database_Operation_Exception("COMPOSITE[{$e->getOperation()}]", $e->getQuery(), $e->getArgs(), $e->getTable(), $e->getError());
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Deletes the rows in a given dataset using primary key columns.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Delete extends PHPUnit_Extensions_Database_Operation_RowBased
+{
+ protected $operationName = 'DELETE';
+
+ protected $iteratorDirection = self::ITERATOR_TYPE_REVERSE;
+
+ protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ $keys = $databaseTableMetaData->getPrimaryKeys();
+
+ $whereStatement = 'WHERE ' . implode(' AND ', $this->buildPreparedColumnArray($keys, $connection));
+
+ $query = "
+ DELETE FROM {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())}
+ {$whereStatement}
+ ";
+
+ return $query;
+ }
+
+ protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row)
+ {
+ $args = [];
+ foreach ($databaseTableMetaData->getPrimaryKeys() as $columnName) {
+ $args[] = $table->getValue($row, $columnName);
+ }
+
+ return $args;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Deletes all rows from all tables in a dataset.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_DeleteAll implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+{
+ public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ foreach ($dataSet->getReverseIterator() as $table) {
+ /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */
+
+ $query = "
+ DELETE FROM {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())}
+ ";
+
+ try {
+ $connection->getConnection()->query($query);
+ } catch (PDOException $e) {
+ throw new PHPUnit_Extensions_Database_Operation_Exception('DELETE_ALL', $query, [], $table, $e->getMessage());
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Thrown for exceptions encountered with database operations. Provides
+ * information regarding which operations failed and the query (if any) it
+ * failed on.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Exception extends RuntimeException
+{
+ /**
+ * @var string
+ */
+ protected $operation;
+
+ /**
+ * @var string
+ */
+ protected $preparedQuery;
+
+ /**
+ * @var array
+ */
+ protected $preparedArgs;
+
+ /**
+ * @var PHPUnit_Extensions_Database_DataSet_ITable
+ */
+ protected $table;
+
+ /**
+ * @var string
+ */
+ protected $error;
+
+ /**
+ * Creates a new dbunit operation exception
+ *
+ * @param string $operation
+ * @param string $current_query
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $current_table
+ * @param string $error
+ */
+ public function __construct($operation, $current_query, $current_args, $current_table, $error)
+ {
+ parent::__construct("{$operation} operation failed on query: {$current_query} using args: " . print_r($current_args, TRUE) . " [{$error}]");
+
+ $this->operation = $operation;
+ $this->preparedQuery = $current_query;
+ $this->preparedArgs = $current_args;
+ $this->table = $current_table;
+ $this->error = $error;
+ }
+
+ public function getOperation()
+ {
+ return $this->operation;
+ }
+
+ public function getQuery()
+ {
+ return $this->preparedQuery;
+ }
+
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ public function getArgs()
+ {
+ return $this->preparedArgs;
+ }
+
+ public function getError()
+ {
+ return $this->error;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A class factory to easily return database operations.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Factory
+{
+ /**
+ * Returns a null database operation
+ *
+ * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ public static function NONE()
+ {
+ return new PHPUnit_Extensions_Database_Operation_Null();
+ }
+
+ /**
+ * Returns a clean insert database operation. It will remove all contents
+ * from the table prior to re-inserting rows.
+ *
+ * @param bool $cascadeTruncates Set to true to force truncates to cascade on databases that support this.
+ * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ public static function CLEAN_INSERT($cascadeTruncates = FALSE)
+ {
+ return new PHPUnit_Extensions_Database_Operation_Composite([
+ self::TRUNCATE($cascadeTruncates),
+ self::INSERT()
+ ]);
+ }
+
+ /**
+ * Returns an insert database operation.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ public static function INSERT()
+ {
+ return new PHPUnit_Extensions_Database_Operation_Insert();
+ }
+
+ /**
+ * Returns a truncate database operation.
+ *
+ * @param bool $cascadeTruncates Set to true to force truncates to cascade on databases that support this.
+ * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ public static function TRUNCATE($cascadeTruncates = FALSE)
+ {
+ $truncate = new PHPUnit_Extensions_Database_Operation_Truncate();
+ $truncate->setCascade($cascadeTruncates);
+
+ return $truncate;
+ }
+
+ /**
+ * Returns a delete database operation.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ public static function DELETE()
+ {
+ return new PHPUnit_Extensions_Database_Operation_Delete();
+ }
+
+ /**
+ * Returns a delete_all database operation.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ public static function DELETE_ALL()
+ {
+ return new PHPUnit_Extensions_Database_Operation_DeleteAll();
+ }
+
+ /**
+ * Returns an update database operation.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+ */
+ public static function UPDATE()
+ {
+ return new PHPUnit_Extensions_Database_Operation_Update();
+ }
+
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides a basic interface and functionality for executing database
+ * operations against a connection using a specific dataSet.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+{
+ /**
+ * Executes the database operation against the given $connection for the
+ * given $dataSet.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet
+ * @throws PHPUnit_Extensions_Database_Operation_Exception
+ */
+ public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet);
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This class provides functionality for inserting rows from a dataset into a database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Insert extends PHPUnit_Extensions_Database_Operation_RowBased
+{
+ protected $operationName = 'INSERT';
+
+ protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ $columnCount = count($table->getTableMetaData()->getColumns());
+
+ if ($columnCount > 0) {
+ $placeHolders = implode(', ', array_fill(0, $columnCount, '?'));
+
+ $columns = '';
+ foreach ($table->getTableMetaData()->getColumns() as $column) {
+ $columns .= $connection->quoteSchemaObject($column) . ', ';
+ }
+
+ $columns = substr($columns, 0, -2);
+
+ $query = "
+ INSERT INTO {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())}
+ ({$columns})
+ VALUES
+ ({$placeHolders})
+ ";
+
+ return $query;
+ } else {
+ return FALSE;
+ }
+ }
+
+ protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row)
+ {
+ $args = [];
+ foreach ($table->getTableMetaData()->getColumns() as $columnName) {
+ $args[] = $table->getValue($row, $columnName);
+ }
+
+ return $args;
+ }
+
+ protected function disablePrimaryKeys(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ if (count($databaseTableMetaData->getPrimaryKeys())) {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This class represents a null database operation.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Null implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+{
+ public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ /* do nothing */
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Updates the rows in a given dataset using primary key columns.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Replace extends PHPUnit_Extensions_Database_Operation_RowBased
+{
+ protected $operationName = 'REPLACE';
+
+ protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ $keys = $databaseTableMetaData->getPrimaryKeys();
+
+ $whereStatement = 'WHERE ' . implode(' AND ', $this->buildPreparedColumnArray($keys, $connection));
+
+ $query = "
+ SELECT COUNT(*)
+ FROM {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())}
+ {$whereStatement}
+ ";
+
+ return $query;
+ }
+
+ protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row)
+ {
+ $args = [];
+
+ foreach ($databaseTableMetaData->getPrimaryKeys() as $columnName) {
+ $args[] = $table->getValue($row, $columnName);
+ }
+
+ return $args;
+ }
+
+ /**
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet
+ */
+ public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ $insertOperation = new PHPUnit_Extensions_Database_Operation_Insert;
+ $updateOperation = new PHPUnit_Extensions_Database_Operation_Update;
+ $databaseDataSet = $connection->createDataSet();
+
+ foreach ($dataSet as $table) {
+ /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */
+ $databaseTableMetaData = $databaseDataSet->getTableMetaData($table->getTableMetaData()->getTableName());
+
+ $insertQuery = $insertOperation->buildOperationQuery($databaseTableMetaData, $table, $connection);
+ $updateQuery = $updateOperation->buildOperationQuery($databaseTableMetaData, $table, $connection);
+ $selectQuery = $this->buildOperationQuery($databaseTableMetaData, $table, $connection);
+
+ $insertStatement = $connection->getConnection()->prepare($insertQuery);
+ $updateStatement = $connection->getConnection()->prepare($updateQuery);
+ $selectStatement = $connection->getConnection()->prepare($selectQuery);
+
+ $rowCount = $table->getRowCount();
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ $selectArgs = $this->buildOperationArguments($databaseTableMetaData, $table, $i);
+ $query = $selectQuery;
+ $args = $selectArgs;
+
+ try {
+ $selectStatement->execute($selectArgs);
+
+ if ($selectStatement->fetchColumn(0) > 0) {
+ $updateArgs = $updateOperation->buildOperationArguments($databaseTableMetaData, $table, $i);
+ $query = $updateQuery;
+ $args = $updateArgs;
+
+ $updateStatement->execute($updateArgs);
+ } else {
+ $insertArgs = $insertOperation->buildOperationArguments($databaseTableMetaData, $table, $i);
+ $query = $insertQuery;
+ $args = $insertArgs;
+
+ $insertStatement->execute($insertArgs);
+ }
+ }
+
+ catch (Exception $e) {
+ throw new PHPUnit_Extensions_Database_Operation_Exception(
+ $this->operationName, $query, $args, $table, $e->getMessage()
+ );
+ }
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides basic functionality for row based operations.
+ *
+ * To create a row based operation you must create two functions. The first
+ * one, buildOperationQuery(), must return a query that will be used to create
+ * a prepared statement. The second one, buildOperationArguments(), should
+ * return an array containing arguments for each row.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_Operation_RowBased implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+{
+ const ITERATOR_TYPE_FORWARD = 0;
+ const ITERATOR_TYPE_REVERSE = 1;
+
+ protected $operationName;
+
+ protected $iteratorDirection = self::ITERATOR_TYPE_FORWARD;
+
+ /**
+ * @return string|bool String containing the query or FALSE if a valid query cannot be constructed
+ */
+ protected abstract function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection);
+
+ protected abstract function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row);
+
+ /**
+ * Allows an operation to disable primary keys if necessary.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ */
+ protected function disablePrimaryKeys(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ return FALSE;
+ }
+
+ /**
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet
+ */
+ public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ $databaseDataSet = $connection->createDataSet();
+
+ $dsIterator = $this->iteratorDirection == self::ITERATOR_TYPE_REVERSE ? $dataSet->getReverseIterator() : $dataSet->getIterator();
+
+ foreach ($dsIterator as $table) {
+ $rowCount = $table->getRowCount();
+
+ if($rowCount == 0) continue;
+
+ /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */
+ $databaseTableMetaData = $databaseDataSet->getTableMetaData($table->getTableMetaData()->getTableName());
+ $query = $this->buildOperationQuery($databaseTableMetaData, $table, $connection);
+ $disablePrimaryKeys = $this->disablePrimaryKeys($databaseTableMetaData, $table, $connection);
+
+ if ($query === FALSE) {
+ if ($table->getRowCount() > 0) {
+ throw new PHPUnit_Extensions_Database_Operation_Exception($this->operationName, '', [], $table, 'Rows requested for insert, but no columns provided!');
+ }
+ continue;
+ }
+
+ if ($disablePrimaryKeys) {
+ $connection->disablePrimaryKeys($databaseTableMetaData->getTableName());
+ }
+
+ $statement = $connection->getConnection()->prepare($query);
+
+ for ($i = 0; $i < $rowCount; $i++) {
+ $args = $this->buildOperationArguments($databaseTableMetaData, $table, $i);
+
+ try {
+ $statement->execute($args);
+ }
+
+ catch (Exception $e) {
+ throw new PHPUnit_Extensions_Database_Operation_Exception(
+ $this->operationName, $query, $args, $table, $e->getMessage()
+ );
+ }
+ }
+
+ if ($disablePrimaryKeys) {
+ $connection->enablePrimaryKeys($databaseTableMetaData->getTableName());
+ }
+ }
+ }
+
+ protected function buildPreparedColumnArray($columns, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ $columnArray = [];
+
+ foreach ($columns as $columnName) {
+ $columnArray[] = "{$connection->quoteSchemaObject($columnName)} = ?";
+ }
+
+ return $columnArray;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Executes a truncate against all tables in a dataset.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Truncate implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation
+{
+ protected $useCascade = FALSE;
+
+ public function setCascade($cascade = TRUE)
+ {
+ $this->useCascade = $cascade;
+ }
+
+ public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet)
+ {
+ foreach ($dataSet->getReverseIterator() as $table) {
+ /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */
+ $query = "
+ {$connection->getTruncateCommand()} {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())}
+ ";
+
+ if ($this->useCascade && $connection->allowsCascading()) {
+ $query .= ' CASCADE';
+ }
+
+ try {
+ $this->disableForeignKeyChecksForMysql($connection);
+ $connection->getConnection()->query($query);
+ $this->enableForeignKeyChecksForMysql($connection);
+ } catch (\Exception $e) {
+ $this->enableForeignKeyChecksForMysql($connection);
+
+ if ($e instanceof PDOException) {
+ throw new PHPUnit_Extensions_Database_Operation_Exception('TRUNCATE', $query, [], $table, $e->getMessage());
+ }
+
+ throw $e;
+ }
+ }
+ }
+
+ private function disableForeignKeyChecksForMysql(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ if ($this->isMysql($connection)) {
+ $connection->getConnection()->query('SET FOREIGN_KEY_CHECKS = 0');
+ }
+ }
+
+ private function enableForeignKeyChecksForMysql(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ if ($this->isMysql($connection)) {
+ $connection->getConnection()->query('SET FOREIGN_KEY_CHECKS = 1');
+ }
+ }
+
+ private function isMysql(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ return $connection->getConnection()->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql';
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Updates the rows in a given dataset using primary key columns.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_Operation_Update extends PHPUnit_Extensions_Database_Operation_RowBased
+{
+ protected $operationName = 'UPDATE';
+
+ protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ $keys = $databaseTableMetaData->getPrimaryKeys();
+ $columns = $table->getTableMetaData()->getColumns();
+ $whereStatement = 'WHERE ' . implode(' AND ', $this->buildPreparedColumnArray($keys, $connection));
+ $setStatement = 'SET ' . implode(', ', $this->buildPreparedColumnArray($columns, $connection));
+
+ $query = "
+ UPDATE {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())}
+ {$setStatement}
+ {$whereStatement}
+ ";
+
+ return $query;
+ }
+
+ protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row)
+ {
+ $args = [];
+ foreach ($table->getTableMetaData()->getColumns() as $columnName) {
+ $args[] = $table->getValue($row, $columnName);
+ }
+
+ foreach ($databaseTableMetaData->getPrimaryKeys() as $columnName) {
+ $args[] = $table->getValue($row, $columnName);
+ }
+
+ return $args;
+ }
+
+ protected function disablePrimaryKeys(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ if (count($databaseTableMetaData->getPrimaryKeys())) {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TestCase extension that provides functionality for testing and asserting
+ * against a real database.
+ *
+ * @since Class available since Release 1.0.0
+ */
+abstract class PHPUnit_Extensions_Database_TestCase extends PHPUnit_Framework_TestCase
+{
+ use PHPUnit_Extensions_Database_TestCase_Trait;
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+trait PHPUnit_Extensions_Database_TestCase_Trait
+{
+ /**
+ * @var PHPUnit_Extensions_Database_ITester
+ */
+ protected $databaseTester;
+
+ /**
+ * Closes the specified connection.
+ *
+ * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection
+ */
+ protected function closeConnection(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection)
+ {
+ $this->getDatabaseTester()->closeConnection($connection);
+ }
+
+ /**
+ * Returns the test database connection.
+ *
+ * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ */
+ protected abstract function getConnection();
+
+ /**
+ * Gets the IDatabaseTester for this testCase. If the IDatabaseTester is
+ * not set yet, this method calls newDatabaseTester() to obtain a new
+ * instance.
+ *
+ * @return PHPUnit_Extensions_Database_ITester
+ */
+ protected function getDatabaseTester()
+ {
+ if (empty($this->databaseTester)) {
+ $this->databaseTester = $this->newDatabaseTester();
+ }
+
+ return $this->databaseTester;
+ }
+
+ /**
+ * Returns the test dataset.
+ *
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ protected abstract function getDataSet();
+
+ /**
+ * Returns the database operation executed in test setup.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation
+ */
+ protected function getSetUpOperation()
+ {
+ return PHPUnit_Extensions_Database_Operation_Factory::CLEAN_INSERT();
+ }
+
+ /**
+ * Returns the database operation executed in test cleanup.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation
+ */
+ protected function getTearDownOperation()
+ {
+ return PHPUnit_Extensions_Database_Operation_Factory::NONE();
+ }
+
+ /**
+ * Creates a IDatabaseTester for this testCase.
+ *
+ * @return PHPUnit_Extensions_Database_ITester
+ */
+ protected function newDatabaseTester()
+ {
+ return new PHPUnit_Extensions_Database_DefaultTester($this->getConnection());
+ }
+
+ /**
+ * Creates a new DefaultDatabaseConnection using the given PDO connection
+ * and database schema name.
+ *
+ * @param PDO $connection
+ * @param string $schema
+ * @return PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection
+ */
+ protected function createDefaultDBConnection(PDO $connection, $schema = '')
+ {
+ return new PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection($connection, $schema);
+ }
+
+ /**
+ * Creates a new ArrayDataSet with the given array.
+ * The array parameter is an associative array of tables where the key is
+ * the table name and the value an array of rows. Each row is an associative
+ * array by itself with keys representing the field names and the values the
+ * actual data.
+ * For example:
+ * array(
+ * "addressbook" => array(
+ * array("id" => 1, "name" => "...", "address" => "..."),
+ * array("id" => 2, "name" => "...", "address" => "...")
+ * )
+ * )
+ *
+ * @param array $data
+ * @return PHPUnit_Extensions_Database_DataSet_ArrayDataSet
+ */
+ protected function createArrayDataSet(array $data)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_ArrayDataSet($data);
+ }
+
+ /**
+ * Creates a new FlatXmlDataSet with the given $xmlFile. (absolute path.)
+ *
+ * @param string $xmlFile
+ * @return PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet
+ */
+ protected function createFlatXMLDataSet($xmlFile)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet($xmlFile);
+ }
+
+ /**
+ * Creates a new XMLDataSet with the given $xmlFile. (absolute path.)
+ *
+ * @param string $xmlFile
+ * @return PHPUnit_Extensions_Database_DataSet_XmlDataSet
+ */
+ protected function createXMLDataSet($xmlFile)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_XmlDataSet($xmlFile);
+ }
+
+ /**
+ * Create a a new MysqlXmlDataSet with the given $xmlFile. (absolute path.)
+ *
+ * @param string $xmlFile
+ * @return PHPUnit_Extensions_Database_DataSet_MysqlXmlDataSet
+ * @since Method available since Release 1.0.0
+ */
+ protected function createMySQLXMLDataSet($xmlFile)
+ {
+ return new PHPUnit_Extensions_Database_DataSet_MysqlXmlDataSet($xmlFile);
+ }
+
+ /**
+ * Returns an operation factory instance that can be used to instantiate
+ * new operations.
+ *
+ * @return PHPUnit_Extensions_Database_Operation_Factory
+ */
+ protected function getOperations()
+ {
+ return new PHPUnit_Extensions_Database_Operation_Factory();
+ }
+
+ /**
+ * Performs operation returned by getSetUpOperation().
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->databaseTester = NULL;
+
+ $this->getDatabaseTester()->setSetUpOperation($this->getSetUpOperation());
+ $this->getDatabaseTester()->setDataSet($this->getDataSet());
+ $this->getDatabaseTester()->onSetUp();
+ }
+
+ /**
+ * Performs operation returned by getTearDownOperation().
+ */
+ protected function tearDown()
+ {
+ $this->getDatabaseTester()->setTearDownOperation($this->getTearDownOperation());
+ $this->getDatabaseTester()->setDataSet($this->getDataSet());
+ $this->getDatabaseTester()->onTearDown();
+
+ /*
+ * Destroy the tester after the test is run to keep DB connections
+ * from piling up.
+ */
+ $this->databaseTester = NULL;
+ }
+
+ /**
+ * Asserts that two given tables are equal.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $expected
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $actual
+ * @param string $message
+ */
+ public static function assertTablesEqual(PHPUnit_Extensions_Database_DataSet_ITable $expected, PHPUnit_Extensions_Database_DataSet_ITable $actual, $message = '')
+ {
+ $constraint = new PHPUnit_Extensions_Database_Constraint_TableIsEqual($expected);
+
+ self::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two given datasets are equal.
+ *
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $expected
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $actual
+ * @param string $message
+ */
+ public static function assertDataSetsEqual(PHPUnit_Extensions_Database_DataSet_IDataSet $expected, PHPUnit_Extensions_Database_DataSet_IDataSet $actual, $message = '')
+ {
+ $constraint = new PHPUnit_Extensions_Database_Constraint_DataSetIsEqual($expected);
+
+ self::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Assert that a given table has a given amount of rows
+ *
+ * @param string $tableName Name of the table
+ * @param int $expected Expected amount of rows in the table
+ * @param string $message Optional message
+ */
+ public function assertTableRowCount($tableName, $expected, $message = '')
+ {
+ $constraint = new PHPUnit_Extensions_Database_Constraint_TableRowCount($tableName, $expected);
+ $actual = $this->getConnection()->getRowCount($tableName);
+
+ self::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a given table contains a given row
+ *
+ * @param array $expectedRow Row expected to find
+ * @param PHPUnit_Extensions_Database_DataSet_ITable $table Table to look into
+ * @param string $message Optional message
+ */
+ public function assertTableContains(array $expectedRow, PHPUnit_Extensions_Database_DataSet_ITable $table, $message = '')
+ {
+ self::assertThat($table->assertContainsRow($expectedRow), self::isTrue(), $message);
+ }
+}
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Delegates database extension commands to the appropriate mode classes.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_UI_Command
+{
+ /**
+ * @var PHPUnit_Extensions_Database_UI_IModeFactory
+ */
+ protected $modeFactory;
+
+ /**
+ * @param PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory
+ */
+ public function __construct(PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory)
+ {
+ $this->modeFactory = $modeFactory;
+ }
+
+ /**
+ * Executes the database extension ui.
+ *
+ * @param PHPUnit_Extensions_Database_UI_IMedium $medium
+ * @param PHPUnit_Extensions_Database_UI_Context $context
+ */
+ public function main(PHPUnit_Extensions_Database_UI_IMedium $medium, PHPUnit_Extensions_Database_UI_Context $context)
+ {
+ try {
+ $medium->buildContext($context);
+ $mode = $this->modeFactory->getMode($context->getMode());
+ $mode->execute($context->getModeArguments(), $medium);
+
+ } catch (Exception $e) {
+ $medium->handleException($e);
+ }
+ }
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Holds the context of a particular database extension ui call.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_UI_Context
+{
+ /**
+ * @var string
+ */
+ protected $mode;
+
+ /**
+ * @var array
+ */
+ protected $modeArguments;
+
+ /**
+ * @param string $mode
+ */
+ public function setMode($mode)
+ {
+ $this->mode = $mode;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMode()
+ {
+ return $this->mode;
+ }
+
+ /**
+ * @param array $arguments
+ */
+ public function setModeArguments(array $arguments)
+ {
+ $this->mode_arguments = $arguments;
+ }
+
+ /**
+ * @return array
+ */
+ public function getModeArguments()
+ {
+ return $this->mode_arguments;
+ }
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Defines the interface necessary to create new mediums.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_UI_IMedium extends PHPUnit_Extensions_Database_UI_IMediumPrinter
+{
+ /**
+ * Builds the context for the application.
+ *
+ * @param PHPUnit_Extensions_Database_UI_Context $context
+ */
+ public function buildContext(PHPUnit_Extensions_Database_UI_Context $context);
+
+ /**
+ * Handles the displaying of exceptions received from the application.
+ *
+ * @param Exception $e
+ */
+ public function handleException(Exception $e);
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Defines the interface necessary to create new medium printers.
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_UI_IMediumPrinter
+{
+ /**
+ * Prints standard output messages.
+ *
+ * @param string $message
+ */
+ public function output($message);
+
+ /**
+ * Prints standard error messages.
+ *
+ * @param string $message
+ */
+ public function error($message);
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Defines the interface necessary to create new modes
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_UI_IMode
+{
+ /**
+ * Executes the mode using the given arguments and medium.
+ *
+ * @param array $modeArguments
+ * @param PHPUnit_Extensions_Database_UI_IMediumPrinter $medium
+ */
+ public function execute(array $modeArguments, PHPUnit_Extensions_Database_UI_IMediumPrinter $medium);
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Defines the interface necessary to create new mode factories
+ *
+ * @since Class available since Release 1.0.0
+ */
+interface PHPUnit_Extensions_Database_UI_IModeFactory
+{
+ /**
+ * Generates a new mode based on a given name.
+ *
+ * @param string $mode
+ * @return PHPUnit_Extensions_Database_UI_IMode
+ */
+ public function getMode($mode);
+
+ /**
+ * Returns the names of valid modes this factory can create.
+ *
+ * @return array
+ */
+ public function getModeList();
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An exception thrown when an invalid mode is requested from a mode factory.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_UI_InvalidModeException extends LogicException
+{
+ /**
+ * @var string
+ */
+ protected $mode;
+
+ /**
+ * @var PHPUnit_Extensions_Database_UI_IModeFactory
+ */
+ protected $modeFactory;
+
+ /**
+ * @param string $mode
+ * @param string $msg
+ * @param PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory
+ */
+ public function __construct($mode, $msg, PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory)
+ {
+ $this->mode = $mode;
+ $this->modeFactory = $modeFactory;
+ parent::__construct($msg);
+ }
+
+ /**
+ * @return string
+ */
+ public function getMode()
+ {
+ return $this->mode;
+ }
+
+ /**
+ * @return array
+ */
+ public function getValidModes()
+ {
+ return $this->modeFactory->getModeList();
+ }
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A text medium for the database extension tool.
+ *
+ * This class builds the call context based on command line parameters and
+ * prints output to stdout and stderr as appropriate.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_UI_Mediums_Text implements PHPUnit_Extensions_Database_UI_IMedium
+{
+ /**
+ * @var array
+ */
+ protected $arguments;
+
+ /**
+ * @var string
+ */
+ protected $command;
+
+ /**
+ * @param array $arguments
+ */
+ public function __construct(Array $arguments)
+ {
+ $this->arguments = $arguments;
+ }
+
+ /**
+ * Builds the context for the application.
+ *
+ * @param PHPUnit_Extensions_Database_UI_Context $context
+ */
+ public function buildContext(PHPUnit_Extensions_Database_UI_Context $context)
+ {
+ $arguments = $this->arguments;
+ $this->command = array_shift($arguments);
+
+ $context->setMode(array_shift($arguments));
+ $context->setModeArguments($arguments);
+ }
+
+ /**
+ * Handles the displaying of exceptions received from the application.
+ *
+ * @param Exception $e
+ */
+ public function handleException(Exception $e)
+ {
+ try {
+ throw $e;
+ } catch (PHPUnit_Extensions_Database_UI_InvalidModeException $invalidMode) {
+ if ($invalidMode->getMode() == '') {
+ $this->error('Please Specify a Command!' . PHP_EOL);
+ } else {
+ $this->error('Command Does Not Exist: ' . $invalidMode->getMode() . PHP_EOL);
+ }
+ $this->error('Valid Commands:' . PHP_EOL);
+
+ foreach ($invalidMode->getValidModes() as $mode) {
+ $this->error(' ' . $mode . PHP_EOL);
+ }
+ } catch (Exception $e) {
+ $this->error('Unknown Error: ' . $e->getMessage() . PHP_EOL);
+ }
+ }
+
+ /**
+ * Prints the message to stdout.
+ *
+ * @param string $message
+ */
+ public function output($message)
+ {
+ echo $message;
+ }
+
+ /**
+ * Prints the message to stderr
+ *
+ * @param string $message
+ */
+ public function error($message)
+ {
+ fputs(STDERR, $message);
+ }
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The default factory for db extension modes.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_UI_ModeFactory implements PHPUnit_Extensions_Database_UI_IModeFactory
+{
+ /**
+ * Generates a new mode based on a given name.
+ *
+ * @param string $mode
+ * @return PHPUnit_Extensions_Database_UI_IMode
+ */
+ public function getMode($mode)
+ {
+ if ($mode == '') {
+ throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'A mode was not provided.', $this);
+ }
+
+ $modeMap = $this->getModeMap();
+ if (isset($modeMap[$mode])) {
+ $modeClass = $this->getModeClass($mode, $modeMap[$mode]);
+
+ return new $modeClass();
+ } else {
+ throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'The mode does not exist. Attempting to load mode ' . $mode, $this);
+ }
+ }
+
+ /**
+ * Returns the names of valid modes this factory can create.
+ *
+ * @return array
+ */
+ public function getModeList()
+ {
+ return array_keys($this->getModeMap());
+ }
+
+ /**
+ * Returns a map of modes to class name parts
+ *
+ * @return array
+ */
+ protected function getModeMap()
+ {
+ return ['export-dataset' => 'ExportDataSet'];
+ }
+
+ /**
+ * Given a $mode label and a $mode_name class part attempts to return the
+ * class name necessary to instantiate the mode.
+ *
+ * @param string $mode
+ * @param string $mode_name
+ * @return string
+ */
+ protected function getModeClass($mode, $mode_name)
+ {
+ $modeClass = 'PHPUnit_Extensions_Database_UI_Modes_' . $mode_name;
+ $modeFile = dirname(__FILE__) . '/Modes/' . $mode_name . '.php';
+
+ if (class_exists($modeClass)) {
+ return $modeClass;
+ }
+
+ if (!is_readable($modeFile)) {
+ throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'The mode\'s file could not be loaded. Trying file ' . $modeFile, $this);
+ }
+
+ require_once ($modeFile);
+
+ if (!class_exists($modeClass)) {
+ throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'The mode class was not found in the file. Expecting class name ' . $modeClass, $this);
+ }
+
+ return $modeClass;
+ }
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The class for the export-dataset command.
+ *
+ * This command is used to convert existing data sets or data in the database
+ * into a valid data set format.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_UI_Modes_ExportDataSet implements PHPUnit_Extensions_Database_UI_IMode
+{
+ /**
+ * Executes the export dataset command.
+ *
+ * @param array $modeArguments
+ * @param PHPUnit_Extensions_Database_UI_IMediumPrinter $medium
+ */
+ public function execute(array $modeArguments, PHPUnit_Extensions_Database_UI_IMediumPrinter $medium)
+ {
+ $arguments = new PHPUnit_Extensions_Database_UI_Modes_ExportDataSet_Arguments($modeArguments);
+
+ if (FALSE && !$arguments->areValid()) {
+ throw new InvalidArgumentException('The arguments for this command are incorrect.');
+ }
+
+ $datasets = [];
+ foreach ($arguments->getArgumentArray('dataset') as $argString) {
+ $datasets[] = $this->getDataSetFromArgument($argString, $arguments->getDatabases());
+ }
+
+ $finalDataset = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet($datasets);
+
+ $outputDataset = $this->getPersistorFromArgument($arguments->getSingleArgument('output'));
+ $outputDataset->write($finalDataset);
+ }
+
+ /**
+ * Returns the correct dataset given an argument containing a dataset spec.
+ *
+ * @param string $argString
+ * @param array $databaseList
+ * @return PHPUnit_Extensions_Database_DataSet_IDataSet
+ */
+ protected function getDataSetFromArgument($argString, $databaseList)
+ {
+ $dataSetSpecFactory = new PHPUnit_Extensions_Database_DataSet_Specs_Factory();
+ list($type, $dataSetSpecStr) = explode(':', $argString, 2);
+ $dataSetSpec = $dataSetSpecFactory->getDataSetSpecByType($type);
+
+ if ($dataSetSpec instanceof PHPUnit_Extensions_Database_IDatabaseListConsumer) {
+ $dataSetSpec->setDatabases($databaseList);
+ }
+
+ return $dataSetSpec->getDataSet($dataSetSpecStr);
+ }
+
+ /**
+ * Returns the correct persistor given an argument containing a persistor spec.
+ *
+ * @param string $argString
+ * @return PHPUnit_Extensions_Database_DataSet_IPersistable
+ */
+ protected function getPersistorFromArgument($argString)
+ {
+ $persistorFactory = new PHPUnit_Extensions_Database_DataSet_Persistors_Factory();
+ list($type, $spec) = explode(':', $argString, 2);
+
+ return $persistorFactory->getPersistorBySpec($type, $spec);
+ }
+}
+
+<?php
+/*
+ * This file is part of DBUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Represents arguments received from a medium.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Extensions_Database_UI_Modes_ExportDataSet_Arguments
+{
+ /**
+ * @var array
+ */
+ protected $arguments = [];
+
+ /**
+ * @param array $arguments
+ */
+ public function __construct(array $arguments)
+ {
+ foreach ($arguments as $argument) {
+ list($argName, $argValue) = explode('=', $argument, 2);
+
+ $argName = trim($argName, '-');
+
+ if (!isset($this->arguments[$argName])) {
+ $this->arguments[$argName] = [];
+ }
+
+ $this->arguments[$argName][] = $argValue;
+ }
+ }
+
+ /**
+ * Returns an array of arguments matching the given $argName
+ *
+ * @param string $argName
+ * @return array
+ */
+ public function getArgumentArray($argName)
+ {
+ if ($this->argumentIsSet($argName)) {
+ return $this->arguments[$argName];
+ } else {
+ return NULL;
+ }
+ }
+
+ /**
+ * Returns a single argument value.
+ *
+ * If $argName points to an array the first argument will be returned.
+ *
+ * @param string $argName
+ * @return mixed
+ */
+ public function getSingleArgument($argName)
+ {
+ if ($this->argumentIsSet($argName)) {
+ return reset($this->arguments[$argName]);
+ } else {
+ return NULL;
+ }
+ }
+
+ /**
+ * Returns whether an argument is set.
+ *
+ * @param string $argName
+ * @return bool
+ */
+ public function argumentIsSet($argName)
+ {
+ return array_key_exists($argName, $this->arguments);
+ }
+
+ /**
+ * Returns an array containing the names of all arguments provided.
+ *
+ * @return array
+ */
+ public function getArgumentNames()
+ {
+ return array_keys($this->arguments);
+ }
+
+ /**
+ * Returns an array of database arguments keyed by name.
+ *
+ * @todo this should be moved.
+ * @return array
+ */
+ public function getDatabases()
+ {
+ $databases = $this->getArgumentArray('database');
+
+ $retDb = [];
+ foreach ($databases as $db) {
+ list($name, $arg) = explode(':', $db, 2);
+ $retDb[$name] = $arg;
+ }
+
+ return $retDb;
+ }
+}
+
+<?php
+/*
+ * This file is part of the PHP_Invoker package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(ticks = 1);
+
+/**
+ * Utility class for invoking callables with a timeout.
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHP_Invoker
+{
+ /**
+ * @var int
+ */
+ protected $timeout;
+
+ /**
+ * Invokes a callable and raises an exception when the execution does not
+ * finish before the specified timeout.
+ *
+ * @param callable $callable
+ * @param array $arguments
+ * @param int $timeout in seconds
+ * @return mixed
+ * @throws InvalidArgumentException
+ */
+ public function invoke($callable, array $arguments, $timeout)
+ {
+ if (!is_callable($callable)) {
+ throw new InvalidArgumentException;
+ }
+
+ if (!is_integer($timeout)) {
+ throw new InvalidArgumentException;
+ }
+
+ pcntl_signal(SIGALRM, array($this, 'callback'), TRUE);
+ pcntl_alarm($timeout);
+
+ $this->timeout = $timeout;
+
+ try {
+ $result = call_user_func_array($callable, $arguments);
+ }
+
+ catch (Exception $e) {
+ pcntl_alarm(0);
+ throw $e;
+ }
+
+ pcntl_alarm(0);
+
+ return $result;
+ }
+
+ /**
+ * Invoked by pcntl_signal() when a SIGALRM occurs.
+ */
+ public function callback()
+ {
+ throw new PHP_Invoker_TimeoutException(
+ sprintf(
+ 'Execution aborted after %s',
+ PHP_Timer::secondsToTimeString($this->timeout)
+ )
+ );
+ }
+}
+<?php
+/*
+ * This file is part of the PHP_Invoker package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * @since Class available since Release 1.0.0
+ */
+class PHP_Invoker_TimeoutException extends RuntimeException
+{
+}
+The MIT License (MIT)
+
+Copyright (c) 2015 phpDocumentor
+
+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.
+
+<?php
+/**
+ * phpDocumentor
+ *
+ * PHP Version 5.5
+ *
+ * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+/**
+ * Interface for Api Elements
+ */
+interface Element
+{
+ /**
+ * Returns the Fqsen of the element.
+ *
+ * @return Fqsen
+ */
+ public function getFqsen();
+
+ /**
+ * Returns the name of the element.
+ *
+ * @return string
+ */
+ public function getName();
+}<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+/**
+ * Interface for files processed by the ProjectFactory
+ */
+interface File
+{
+ /**
+ * Returns the content of the file as a string.
+ *
+ * @return string
+ */
+ public function getContents();
+
+ /**
+ * Returns md5 hash of the file.
+ *
+ * @return string
+ */
+ public function md5();
+
+ /**
+ * Returns an relative path to the file.
+ *
+ * @return string
+ */
+ public function path();
+}
+<?php
+/**
+ * phpDocumentor
+ *
+ * PHP Version 5.5
+ *
+ * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+/**
+ * Value Object for Fqsen.
+ *
+ * @link https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc-meta.md
+ */
+final class Fqsen
+{
+ /**
+ * @var string full quallified class name
+ */
+ private $fqsen;
+
+ /**
+ * @var string name of the element without path.
+ */
+ private $name;
+
+ /**
+ * Initializes the object.
+ *
+ * @param string $fqsen
+ *
+ * @throws \InvalidArgumentException when $fqsen is not matching the format.
+ */
+ public function __construct($fqsen)
+ {
+ $matches = array();
+ $result = preg_match('/^\\\\([\\w_\\\\]*)(?:[:]{2}\\$?([\\w_]+))?(?:\\(\\))?$/', $fqsen, $matches);
+
+ if ($result === 0) {
+ throw new \InvalidArgumentException(
+ sprintf('"%s" is not a valid Fqsen.', $fqsen)
+ );
+ }
+
+ $this->fqsen = $fqsen;
+
+ if (isset($matches[2])) {
+ $this->name = $matches[2];
+ } else {
+ $matches = explode('\\', $fqsen);
+ $this->name = trim(end($matches), '()');
+ }
+ }
+
+ /**
+ * converts this class to string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element without path.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+/**
+ * The location where an element occurs within a file.
+ */
+final class Location
+{
+ /** @var int */
+ private $lineNumber = 0;
+
+ /** @var int */
+ private $columnNumber = 0;
+
+ /**
+ * Initializes the location for an element using its line number in the file and optionally the column number.
+ *
+ * @param int $lineNumber
+ * @param int $columnNumber
+ */
+ public function __construct($lineNumber, $columnNumber = 0)
+ {
+ $this->lineNumber = $lineNumber;
+ $this->columnNumber = $columnNumber;
+ }
+
+ /**
+ * Returns the line number that is covered by this location.
+ *
+ * @return integer
+ */
+ public function getLineNumber()
+ {
+ return $this->lineNumber;
+ }
+
+ /**
+ * Returns the column number (character position on a line) for this location object.
+ *
+ * @return integer
+ */
+ public function getColumnNumber()
+ {
+ return $this->columnNumber;
+ }
+}
+<?php
+/**
+ * phpDocumentor
+ *
+ * PHP Version 5.5
+ *
+ * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+/**
+ * Interface for project. Since the definition of a project can be different per factory this interface will be small.
+ */
+interface Project
+{
+ /**
+ * Returns the name of the project.
+ *
+ * @return string
+ */
+ public function getName();
+}
+<?php
+/**
+ * phpDocumentor
+ *
+ * PHP Version 5.5
+ *
+ * @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+namespace phpDocumentor\Reflection;
+
+/**
+ * Interface for project factories. A project factory shall convert a set of files
+ * into an object implementing the Project interface.
+ */
+interface ProjectFactory
+{
+ /**
+ * Creates a project from the set of files.
+ *
+ * @param string $name
+ * @param File[] $files
+ * @return Project
+ */
+ public function create($name, array $files);
+}
+The MIT License (MIT)
+
+Copyright (c) 2010 Mike van Riel
+
+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.
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+use phpDocumentor\Reflection\DocBlock\Tag;
+use Webmozart\Assert\Assert;
+
+final class DocBlock
+{
+ /** @var string The opening line for this docblock. */
+ private $summary = '';
+
+ /** @var DocBlock\Description The actual description for this docblock. */
+ private $description = null;
+
+ /** @var Tag[] An array containing all the tags in this docblock; except inline. */
+ private $tags = array();
+
+ /** @var Types\Context Information about the context of this DocBlock. */
+ private $context = null;
+
+ /** @var Location Information about the location of this DocBlock. */
+ private $location = null;
+
+ /** @var bool Is this DocBlock (the start of) a template? */
+ private $isTemplateStart = false;
+
+ /** @var bool Does this DocBlock signify the end of a DocBlock template? */
+ private $isTemplateEnd = false;
+
+ /**
+ * @param string $summary
+ * @param DocBlock\Description $description
+ * @param DocBlock\Tag[] $tags
+ * @param Types\Context $context The context in which the DocBlock occurs.
+ * @param Location $location The location within the file that this DocBlock occurs in.
+ * @param bool $isTemplateStart
+ * @param bool $isTemplateEnd
+ */
+ public function __construct(
+ $summary = '',
+ DocBlock\Description $description = null,
+ array $tags = [],
+ Types\Context $context = null,
+ Location $location = null,
+ $isTemplateStart = false,
+ $isTemplateEnd = false
+ )
+ {
+ Assert::string($summary);
+ Assert::boolean($isTemplateStart);
+ Assert::boolean($isTemplateEnd);
+ Assert::allIsInstanceOf($tags, Tag::class);
+
+ $this->summary = $summary;
+ $this->description = $description ?: new DocBlock\Description('');
+ foreach ($tags as $tag) {
+ $this->addTag($tag);
+ }
+
+ $this->context = $context;
+ $this->location = $location;
+
+ $this->isTemplateEnd = $isTemplateEnd;
+ $this->isTemplateStart = $isTemplateStart;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSummary()
+ {
+ return $this->summary;
+ }
+
+ /**
+ * @return DocBlock\Description
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Returns the current context.
+ *
+ * @return Types\Context
+ */
+ public function getContext()
+ {
+ return $this->context;
+ }
+
+ /**
+ * Returns the current location.
+ *
+ * @return Location
+ */
+ public function getLocation()
+ {
+ return $this->location;
+ }
+
+ /**
+ * Returns whether this DocBlock is the start of a Template section.
+ *
+ * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
+ * (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
+ *
+ * An example of such an opening is:
+ *
+ * ```
+ * /**#@+
+ * * My DocBlock
+ * * /
+ * ```
+ *
+ * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
+ * elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
+ *
+ * @see self::isTemplateEnd() for the check whether a closing marker was provided.
+ *
+ * @return boolean
+ */
+ public function isTemplateStart()
+ {
+ return $this->isTemplateStart;
+ }
+
+ /**
+ * Returns whether this DocBlock is the end of a Template section.
+ *
+ * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
+ *
+ * @return boolean
+ */
+ public function isTemplateEnd()
+ {
+ return $this->isTemplateEnd;
+ }
+
+ /**
+ * Returns the tags for this DocBlock.
+ *
+ * @return Tag[]
+ */
+ public function getTags()
+ {
+ return $this->tags;
+ }
+
+ /**
+ * Returns an array of tags matching the given name. If no tags are found
+ * an empty array is returned.
+ *
+ * @param string $name String to search by.
+ *
+ * @return Tag[]
+ */
+ public function getTagsByName($name)
+ {
+ Assert::string($name);
+
+ $result = array();
+
+ /** @var Tag $tag */
+ foreach ($this->getTags() as $tag) {
+ if ($tag->getName() != $name) {
+ continue;
+ }
+
+ $result[] = $tag;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Checks if a tag of a certain type is present in this DocBlock.
+ *
+ * @param string $name Tag name to check for.
+ *
+ * @return bool
+ */
+ public function hasTag($name)
+ {
+ Assert::string($name);
+
+ /** @var Tag $tag */
+ foreach ($this->getTags() as $tag) {
+ if ($tag->getName() == $name) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds a tag to this DocBlock.
+ *
+ * @param Tag $tag The tag to add.
+ *
+ * @return void
+ */
+ private function addTag(Tag $tag)
+ {
+ $this->tags[] = $tag;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock;
+
+use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
+use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter;
+use Webmozart\Assert\Assert;
+
+/**
+ * Object representing to description for a DocBlock.
+ *
+ * A Description object can consist of plain text but can also include tags. A Description Formatter can then combine
+ * a body template with sprintf-style placeholders together with formatted tags in order to reconstitute a complete
+ * description text using the format that you would prefer.
+ *
+ * Because parsing a Description text can be a verbose process this is handled by the {@see DescriptionFactory}. It is
+ * thus recommended to use that to create a Description object, like this:
+ *
+ * $description = $descriptionFactory->create('This is a {@see Description}', $context);
+ *
+ * The description factory will interpret the given body and create a body template and list of tags from them, and pass
+ * that onto the constructor if this class.
+ *
+ * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace
+ * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial
+ * > type names and FQSENs.
+ *
+ * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this:
+ *
+ * $description = new Description(
+ * 'This is a %1$s',
+ * [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ]
+ * );
+ *
+ * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object
+ * is mainly responsible for rendering.
+ *
+ * @see DescriptionFactory to create a new Description.
+ * @see Description\Formatter for the formatting of the body and tags.
+ */
+class Description
+{
+ /** @var string */
+ private $bodyTemplate;
+
+ /** @var Tag[] */
+ private $tags;
+
+ /**
+ * Initializes a Description with its body (template) and a listing of the tags used in the body template.
+ *
+ * @param string $bodyTemplate
+ * @param Tag[] $tags
+ */
+ public function __construct($bodyTemplate, array $tags = [])
+ {
+ Assert::string($bodyTemplate);
+
+ $this->bodyTemplate = $bodyTemplate;
+ $this->tags = $tags;
+ }
+
+ /**
+ * Renders this description as a string where the provided formatter will format the tags in the expected string
+ * format.
+ *
+ * @param Formatter|null $formatter
+ *
+ * @return string
+ */
+ public function render(Formatter $formatter = null)
+ {
+ if ($formatter === null) {
+ $formatter = new PassthroughFormatter();
+ }
+
+ $tags = [];
+ foreach ($this->tags as $tag) {
+ $tags[] = '{' . $formatter->format($tag) . '}';
+ }
+ return vsprintf($this->bodyTemplate, $tags);
+ }
+
+ /**
+ * Returns a plain string representation of this description.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->render();
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock;
+
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+
+/**
+ * Creates a new Description object given a body of text.
+ *
+ * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their
+ * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the
+ * Description object's `render` method.
+ *
+ * In addition to the above does a Description support two types of escape sequences:
+ *
+ * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}`
+ * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description
+ * of an inline tag.
+ *
+ * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning
+ * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping
+ * over unexpected spaces as can be observed with tag descriptions.
+ */
+class DescriptionFactory
+{
+ /** @var TagFactory */
+ private $tagFactory;
+
+ /**
+ * Initializes this factory with the means to construct (inline) tags.
+ *
+ * @param TagFactory $tagFactory
+ */
+ public function __construct(TagFactory $tagFactory)
+ {
+ $this->tagFactory = $tagFactory;
+ }
+
+ /**
+ * Returns the parsed text of this description.
+ *
+ * @param string $contents
+ * @param TypeContext $context
+ *
+ * @return Description
+ */
+ public function create($contents, TypeContext $context = null)
+ {
+ list($text, $tags) = $this->parse($this->lex($contents), $context);
+
+ return new Description($text, $tags);
+ }
+
+ /**
+ * Strips the contents from superfluous whitespace and splits the description into a series of tokens.
+ *
+ * @param string $contents
+ *
+ * @return string[] A series of tokens of which the description text is composed.
+ */
+ private function lex($contents)
+ {
+ $contents = $this->removeSuperfluousStartingWhitespace($contents);
+
+ // performance optimalization; if there is no inline tag, don't bother splitting it up.
+ if (strpos($contents, '{@') === false) {
+ return [$contents];
+ }
+
+ return preg_split(
+ '/\{
+ # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally.
+ (?!@\})
+ # We want to capture the whole tag line, but without the inline tag delimiters.
+ (\@
+ # Match everything up to the next delimiter.
+ [^{}]*
+ # Nested inline tag content should not be captured, or it will appear in the result separately.
+ (?:
+ # Match nested inline tags.
+ (?:
+ # Because we did not catch the tag delimiters earlier, we must be explicit with them here.
+ # Notice that this also matches "{}", as a way to later introduce it as an escape sequence.
+ \{(?1)?\}
+ |
+ # Make sure we match hanging "{".
+ \{
+ )
+ # Match content after the nested inline tag.
+ [^{}]*
+ )* # If there are more inline tags, match them as well. We use "*" since there may not be any
+ # nested inline tags.
+ )
+ \}/Sux',
+ $contents,
+ null,
+ PREG_SPLIT_DELIM_CAPTURE
+ );
+ }
+
+ /**
+ * Parses the stream of tokens in to a new set of tokens containing Tags.
+ *
+ * @param string[] $tokens
+ * @param TypeContext $context
+ *
+ * @return string[]|Tag[]
+ */
+ private function parse($tokens, TypeContext $context)
+ {
+ $count = count($tokens);
+ $tagCount = 0;
+ $tags = [];
+
+ for ($i = 1; $i < $count; $i += 2) {
+ $tags[] = $this->tagFactory->create($tokens[$i], $context);
+ $tokens[$i] = '%' . ++$tagCount . '$s';
+ }
+
+ //In order to allow "literal" inline tags, the otherwise invalid
+ //sequence "{@}" is changed to "@", and "{}" is changed to "}".
+ //"%" is escaped to "%%" because of vsprintf.
+ //See unit tests for examples.
+ for ($i = 0; $i < $count; $i += 2) {
+ $tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]);
+ }
+
+ return [implode('', $tokens), $tags];
+ }
+
+ /**
+ * Removes the superfluous from a multi-line description.
+ *
+ * When a description has more than one line then it can happen that the second and subsequent lines have an
+ * additional indentation. This is commonly in use with tags like this:
+ *
+ * {@}since 1.1.0 This is an example
+ * description where we have an
+ * indentation in the second and
+ * subsequent lines.
+ *
+ * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent
+ * lines and this may cause rendering issues when, for example, using a Markdown converter.
+ *
+ * @param string $contents
+ *
+ * @return string
+ */
+ private function removeSuperfluousStartingWhitespace($contents)
+ {
+ $lines = explode("\n", $contents);
+
+ // if there is only one line then we don't have lines with superfluous whitespace and
+ // can use the contents as-is
+ if (count($lines) <= 1) {
+ return $contents;
+ }
+
+ // determine how many whitespace characters need to be stripped
+ $startingSpaceCount = 9999999;
+ for ($i = 1; $i < count($lines); $i++) {
+ // lines with a no length do not count as they are not indented at all
+ if (strlen(trim($lines[$i])) === 0) {
+ continue;
+ }
+
+ // determine the number of prefixing spaces by checking the difference in line length before and after
+ // an ltrim
+ $startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i])));
+ }
+
+ // strip the number of spaces from each line
+ if ($startingSpaceCount > 0) {
+ for ($i = 1; $i < count($lines); $i++) {
+ $lines[$i] = substr($lines[$i], $startingSpaceCount);
+ }
+ }
+
+ return implode("\n", $lines);
+ }
+
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+use phpDocumentor\Reflection\DocBlock\Tags\Example;
+
+/**
+ * Class used to find an example file's location based on a given ExampleDescriptor.
+ */
+class ExampleFinder
+{
+ /** @var string */
+ private $sourceDirectory = '';
+
+ /** @var string[] */
+ private $exampleDirectories = array();
+
+ /**
+ * Attempts to find the example contents for the given descriptor.
+ *
+ * @param Example $example
+ *
+ * @return string
+ */
+ public function find(Example $example)
+ {
+ $filename = $example->getFilePath();
+
+ $file = $this->getExampleFileContents($filename);
+ if (!$file) {
+ return "** File not found : {$filename} **";
+ }
+
+ return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount()));
+ }
+
+ /**
+ * Registers the project's root directory where an 'examples' folder can be expected.
+ *
+ * @param string $directory
+ *
+ * @return void
+ */
+ public function setSourceDirectory($directory = '')
+ {
+ $this->sourceDirectory = $directory;
+ }
+
+ /**
+ * Returns the project's root directory where an 'examples' folder can be expected.
+ *
+ * @return string
+ */
+ public function getSourceDirectory()
+ {
+ return $this->sourceDirectory;
+ }
+
+ /**
+ * Registers a series of directories that may contain examples.
+ *
+ * @param string[] $directories
+ */
+ public function setExampleDirectories(array $directories)
+ {
+ $this->exampleDirectories = $directories;
+ }
+
+ /**
+ * Returns a series of directories that may contain examples.
+ *
+ * @return string[]
+ */
+ public function getExampleDirectories()
+ {
+ return $this->exampleDirectories;
+ }
+
+ /**
+ * Attempts to find the requested example file and returns its contents or null if no file was found.
+ *
+ * This method will try several methods in search of the given example file, the first one it encounters is
+ * returned:
+ *
+ * 1. Iterates through all examples folders for the given filename
+ * 2. Checks the source folder for the given filename
+ * 3. Checks the 'examples' folder in the current working directory for examples
+ * 4. Checks the path relative to the current working directory for the given filename
+ *
+ * @param string $filename
+ *
+ * @return string|null
+ */
+ private function getExampleFileContents($filename)
+ {
+ $normalizedPath = null;
+
+ foreach ($this->exampleDirectories as $directory) {
+ $exampleFileFromConfig = $this->constructExamplePath($directory, $filename);
+ if (is_readable($exampleFileFromConfig)) {
+ $normalizedPath = $exampleFileFromConfig;
+ break;
+ }
+ }
+
+ if (!$normalizedPath) {
+ if (is_readable($this->getExamplePathFromSource($filename))) {
+ $normalizedPath = $this->getExamplePathFromSource($filename);
+ } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) {
+ $normalizedPath = $this->getExamplePathFromExampleDirectory($filename);
+ } elseif (is_readable($filename)) {
+ $normalizedPath = $filename;
+ }
+ }
+
+ return $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : null;
+ }
+
+ /**
+ * Get example filepath based on the example directory inside your project.
+ *
+ * @param string $file
+ *
+ * @return string
+ */
+ private function getExamplePathFromExampleDirectory($file)
+ {
+ return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file;
+ }
+
+ /**
+ * Returns a path to the example file in the given directory..
+ *
+ * @param string $directory
+ * @param string $file
+ *
+ * @return string
+ */
+ private function constructExamplePath($directory, $file)
+ {
+ return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file;
+ }
+
+ /**
+ * Get example filepath based on sourcecode.
+ *
+ * @param string $file
+ *
+ * @return string
+ */
+ private function getExamplePathFromSource($file)
+ {
+ return sprintf(
+ '%s%s%s',
+ trim($this->getSourceDirectory(), '\\/'),
+ DIRECTORY_SEPARATOR,
+ trim($file, '"')
+ );
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock;
+
+use phpDocumentor\Reflection\DocBlock;
+use Webmozart\Assert\Assert;
+
+/**
+ * Converts a DocBlock back from an object to a complete DocComment including Asterisks.
+ */
+class Serializer
+{
+ /** @var string The string to indent the comment with. */
+ protected $indentString = ' ';
+
+ /** @var int The number of times the indent string is repeated. */
+ protected $indent = 0;
+
+ /** @var bool Whether to indent the first line with the given indent amount and string. */
+ protected $isFirstLineIndented = true;
+
+ /** @var int|null The max length of a line. */
+ protected $lineLength = null;
+
+ /**
+ * Create a Serializer instance.
+ *
+ * @param int $indent The number of times the indent string is repeated.
+ * @param string $indentString The string to indent the comment with.
+ * @param bool $indentFirstLine Whether to indent the first line.
+ * @param int|null $lineLength The max length of a line or NULL to disable line wrapping.
+ */
+ public function __construct($indent = 0, $indentString = ' ', $indentFirstLine = true, $lineLength = null)
+ {
+ Assert::integer($indent);
+ Assert::string($indentString);
+ Assert::boolean($indentFirstLine);
+ Assert::nullOrInteger($lineLength);
+
+ $this->indent = $indent;
+ $this->indentString = $indentString;
+ $this->isFirstLineIndented = $indentFirstLine;
+ $this->lineLength = $lineLength;
+ }
+
+ /**
+ * Generate a DocBlock comment.
+ *
+ * @param DocBlock $docblock The DocBlock to serialize.
+ *
+ * @return string The serialized doc block.
+ */
+ public function getDocComment(DocBlock $docblock)
+ {
+ $indent = str_repeat($this->indentString, $this->indent);
+ $firstIndent = $this->isFirstLineIndented ? $indent : '';
+ // 3 === strlen(' * ')
+ $wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null;
+
+ $text = $this->removeTrailingSpaces(
+ $indent,
+ $this->addAsterisksForEachLine(
+ $indent,
+ $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength)
+ )
+ );
+
+ $comment = "{$firstIndent}/**\n{$indent} * {$text}\n{$indent} *\n";
+ $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment);
+ $comment .= $indent . ' */';
+
+ return $comment;
+ }
+
+ /**
+ * @param $indent
+ * @param $text
+ * @return mixed
+ */
+ private function removeTrailingSpaces($indent, $text)
+ {
+ return str_replace("\n{$indent} * \n", "\n{$indent} *\n", $text);
+ }
+
+ /**
+ * @param $indent
+ * @param $text
+ * @return mixed
+ */
+ private function addAsterisksForEachLine($indent, $text)
+ {
+ return str_replace("\n", "\n{$indent} * ", $text);
+ }
+
+ /**
+ * @param DocBlock $docblock
+ * @param $wrapLength
+ * @return string
+ */
+ private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength)
+ {
+ $text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription()
+ : '');
+ if ($wrapLength !== null) {
+ $text = wordwrap($text, $wrapLength);
+ return $text;
+ }
+ return $text;
+ }
+
+ /**
+ * @param DocBlock $docblock
+ * @param $wrapLength
+ * @param $indent
+ * @param $comment
+ * @return string
+ */
+ private function addTagBlock(DocBlock $docblock, $wrapLength, $indent, $comment)
+ {
+ foreach ($docblock->getTags() as $tag) {
+ $formatter = new DocBlock\Tags\Formatter\PassthroughFormatter();
+ $tagText = $formatter->format($tag);
+ if ($wrapLength !== null) {
+ $tagText = wordwrap($tagText, $wrapLength);
+ }
+ $tagText = str_replace("\n", "\n{$indent} * ", $tagText);
+
+ $comment .= "{$indent} * {$tagText}\n";
+ }
+
+ return $comment;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock;
+
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
+use phpDocumentor\Reflection\DocBlock\Tags\Generic;
+use phpDocumentor\Reflection\FqsenResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Creates a Tag object given the contents of a tag.
+ *
+ * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create`
+ * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can
+ * pass the dependencies that you need to construct a tag object.
+ *
+ * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise
+ * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to
+ * > verify that a dependency is actually passed.
+ *
+ * This Factory also features a Service Locator component that is used to pass the right dependencies to the
+ * `create` method of a tag; each dependency should be registered as a service or as a parameter.
+ *
+ * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass
+ * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface.
+ */
+final class StandardTagFactory implements TagFactory
+{
+ /** PCRE regular expression matching a tag name. */
+ const REGEX_TAGNAME = '[\w\-\_\\\\]+';
+
+ /**
+ * @var string[] An array with a tag as a key, and an FQCN to a class that handles it as an array value.
+ */
+ private $tagHandlerMappings = [
+ 'author' => '\phpDocumentor\Reflection\DocBlock\Tags\Author',
+ 'covers' => '\phpDocumentor\Reflection\DocBlock\Tags\Covers',
+ 'deprecated' => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated',
+ // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example',
+ 'link' => '\phpDocumentor\Reflection\DocBlock\Tags\Link',
+ 'method' => '\phpDocumentor\Reflection\DocBlock\Tags\Method',
+ 'param' => '\phpDocumentor\Reflection\DocBlock\Tags\Param',
+ 'property-read' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead',
+ 'property' => '\phpDocumentor\Reflection\DocBlock\Tags\Property',
+ 'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite',
+ 'return' => '\phpDocumentor\Reflection\DocBlock\Tags\Return_',
+ 'see' => '\phpDocumentor\Reflection\DocBlock\Tags\See',
+ 'since' => '\phpDocumentor\Reflection\DocBlock\Tags\Since',
+ 'source' => '\phpDocumentor\Reflection\DocBlock\Tags\Source',
+ 'throw' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
+ 'throws' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
+ 'uses' => '\phpDocumentor\Reflection\DocBlock\Tags\Uses',
+ 'var' => '\phpDocumentor\Reflection\DocBlock\Tags\Var_',
+ 'version' => '\phpDocumentor\Reflection\DocBlock\Tags\Version'
+ ];
+
+ /**
+ * @var \ReflectionParameter[][] a lazy-loading cache containing parameters for each tagHandler that has been used.
+ */
+ private $tagHandlerParameterCache = [];
+
+ /**
+ * @var FqsenResolver
+ */
+ private $fqsenResolver;
+
+ /**
+ * @var mixed[] an array representing a simple Service Locator where we can store parameters and
+ * services that can be inserted into the Factory Methods of Tag Handlers.
+ */
+ private $serviceLocator = [];
+
+ /**
+ * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers.
+ *
+ * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property
+ * is used.
+ *
+ * @param FqsenResolver $fqsenResolver
+ * @param string[] $tagHandlers
+ *
+ * @see self::registerTagHandler() to add a new tag handler to the existing default list.
+ */
+ public function __construct(FqsenResolver $fqsenResolver, array $tagHandlers = null)
+ {
+ $this->fqsenResolver = $fqsenResolver;
+ if ($tagHandlers !== null) {
+ $this->tagHandlerMappings = $tagHandlers;
+ }
+
+ $this->addService($fqsenResolver, FqsenResolver::class);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function create($tagLine, TypeContext $context = null)
+ {
+ if (! $context) {
+ $context = new TypeContext('');
+ }
+
+ list($tagName, $tagBody) = $this->extractTagParts($tagLine);
+
+ return $this->createTag($tagBody, $tagName, $context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function addParameter($name, $value)
+ {
+ $this->serviceLocator[$name] = $value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function addService($service, $alias = null)
+ {
+ $this->serviceLocator[$alias ?: get_class($service)] = $service;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function registerTagHandler($tagName, $handler)
+ {
+ Assert::stringNotEmpty($tagName);
+ Assert::stringNotEmpty($handler);
+ Assert::classExists($handler);
+ Assert::implementsInterface($handler, StaticMethod::class);
+
+ if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
+ throw new \InvalidArgumentException(
+ 'A namespaced tag must have a leading backslash as it must be fully qualified'
+ );
+ }
+
+ $this->tagHandlerMappings[$tagName] = $handler;
+ }
+
+ /**
+ * Extracts all components for a tag.
+ *
+ * @param string $tagLine
+ *
+ * @return string[]
+ */
+ private function extractTagParts($tagLine)
+ {
+ $matches = array();
+ if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us', $tagLine, $matches)) {
+ throw new \InvalidArgumentException(
+ 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
+ );
+ }
+
+ if (count($matches) < 3) {
+ $matches[] = '';
+ }
+
+ return array_slice($matches, 1);
+ }
+
+ /**
+ * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the
+ * body was invalid.
+ *
+ * @param string $body
+ * @param string $name
+ * @param TypeContext $context
+ *
+ * @return Tag|null
+ */
+ private function createTag($body, $name, TypeContext $context)
+ {
+ $handlerClassName = $this->findHandlerClassName($name, $context);
+ $arguments = $this->getArgumentsForParametersFromWiring(
+ $this->fetchParametersForHandlerFactoryMethod($handlerClassName),
+ $this->getServiceLocatorWithDynamicParameters($context, $name, $body)
+ )
+ ;
+
+ return call_user_func_array([$handlerClassName, 'create'], $arguments);
+ }
+
+ /**
+ * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
+ *
+ * @param string $tagName
+ * @param TypeContext $context
+ *
+ * @return string
+ */
+ private function findHandlerClassName($tagName, TypeContext $context)
+ {
+ $handlerClassName = Generic::class;
+ if (isset($this->tagHandlerMappings[$tagName])) {
+ $handlerClassName = $this->tagHandlerMappings[$tagName];
+ } elseif ($this->isAnnotation($tagName)) {
+ // TODO: Annotation support is planned for a later stage and as such is disabled for now
+ // $tagName = (string)$this->fqsenResolver->resolve($tagName, $context);
+ // if (isset($this->annotationMappings[$tagName])) {
+ // $handlerClassName = $this->annotationMappings[$tagName];
+ // }
+ }
+
+ return $handlerClassName;
+ }
+
+ /**
+ * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters.
+ *
+ * @param \ReflectionParameter[] $parameters
+ * @param mixed[] $locator
+ *
+ * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters
+ * is provided with this method.
+ */
+ private function getArgumentsForParametersFromWiring($parameters, $locator)
+ {
+ $arguments = [];
+ foreach ($parameters as $index => $parameter) {
+ $typeHint = $parameter->getClass() ? $parameter->getClass()->getName() : null;
+ if (isset($locator[$typeHint])) {
+ $arguments[] = $locator[$typeHint];
+ continue;
+ }
+
+ $parameterName = $parameter->getName();
+ if (isset($locator[$parameterName])) {
+ $arguments[] = $locator[$parameterName];
+ continue;
+ }
+
+ $arguments[] = null;
+ }
+
+ return $arguments;
+ }
+
+ /**
+ * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
+ * tag handler class name.
+ *
+ * @param string $handlerClassName
+ *
+ * @return \ReflectionParameter[]
+ */
+ private function fetchParametersForHandlerFactoryMethod($handlerClassName)
+ {
+ if (! isset($this->tagHandlerParameterCache[$handlerClassName])) {
+ $methodReflection = new \ReflectionMethod($handlerClassName, 'create');
+ $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
+ }
+
+ return $this->tagHandlerParameterCache[$handlerClassName];
+ }
+
+ /**
+ * Returns a copy of this class' Service Locator with added dynamic parameters, such as the tag's name, body and
+ * Context.
+ *
+ * @param TypeContext $context The Context (namespace and aliasses) that may be passed and is used to resolve FQSENs.
+ * @param string $tagName The name of the tag that may be passed onto the factory method of the Tag class.
+ * @param string $tagBody The body of the tag that may be passed onto the factory method of the Tag class.
+ *
+ * @return mixed[]
+ */
+ private function getServiceLocatorWithDynamicParameters(TypeContext $context, $tagName, $tagBody)
+ {
+ $locator = array_merge(
+ $this->serviceLocator,
+ [
+ 'name' => $tagName,
+ 'body' => $tagBody,
+ TypeContext::class => $context
+ ]
+ );
+
+ return $locator;
+ }
+
+ /**
+ * Returns whether the given tag belongs to an annotation.
+ *
+ * @param string $tagContent
+ *
+ * @todo this method should be populated once we implement Annotation notation support.
+ *
+ * @return bool
+ */
+ private function isAnnotation($tagContent)
+ {
+ // 1. Contains a namespace separator
+ // 2. Contains parenthesis
+ // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part
+ // of the annotation class name matches the found tag name
+
+ return false;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock;
+
+use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
+
+interface Tag
+{
+ public function getName();
+
+ public static function create($body);
+
+ public function render(Formatter $formatter = null);
+
+ public function __toString();
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock;
+
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+
+interface TagFactory
+{
+ /**
+ * Adds a parameter to the service locator that can be injected in a tag's factory method.
+ *
+ * When calling a tag's "create" method we always check the signature for dependencies to inject. One way is to
+ * typehint a parameter in the signature so that we can use that interface or class name to inject a dependency
+ * (see {@see addService()} for more information on that).
+ *
+ * Another way is to check the name of the argument against the names in the Service Locator. With this method
+ * you can add a variable that will be inserted when a tag's create method is not typehinted and has a matching
+ * name.
+ *
+ * Be aware that there are two reserved names:
+ *
+ * - name, representing the name of the tag.
+ * - body, representing the complete body of the tag.
+ *
+ * These parameters are injected at the last moment and will override any existing parameter with those names.
+ *
+ * @param string $name
+ * @param mixed $value
+ *
+ * @return void
+ */
+ public function addParameter($name, $value);
+
+ /**
+ * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided.
+ *
+ * When calling a tag's "create" method we always check the signature for dependencies to inject. If a parameter
+ * has a typehint then the ServiceLocator is queried to see if a Service is registered for that typehint.
+ *
+ * Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the
+ * interface is passed as alias then every time that interface is requested the provided service will be returned.
+ *
+ * @param object $service
+ * @param string $alias
+ *
+ * @return void
+ */
+ public function addService($service);
+
+ /**
+ * Factory method responsible for instantiating the correct sub type.
+ *
+ * @param string $tagLine The text for this tag, including description.
+ * @param TypeContext $context
+ *
+ * @throws \InvalidArgumentException if an invalid tag line was presented.
+ *
+ * @return Tag A new tag object.
+ */
+ public function create($tagLine, TypeContext $context = null);
+
+ /**
+ * Registers a handler for tags.
+ *
+ * If you want to use your own tags then you can use this method to instruct the TagFactory to register the name
+ * of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement the {@see Tag} interface (and thus
+ * the create method).
+ *
+ * @param string $tagName Name of tag to register a handler for. When registering a namespaced tag, the full
+ * name, along with a prefixing slash MUST be provided.
+ * @param string $handler FQCN of handler.
+ *
+ * @throws \InvalidArgumentException if the tag name is not a string
+ * @throws \InvalidArgumentException if the tag name is namespaced (contains backslashes) but does not start with
+ * a backslash
+ * @throws \InvalidArgumentException if the handler is not a string
+ * @throws \InvalidArgumentException if the handler is not an existing class
+ * @throws \InvalidArgumentException if the handler does not implement the {@see Tag} interface
+ *
+ * @return void
+ */
+ public function registerTagHandler($tagName, $handler);
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for an {@}author tag in a Docblock.
+ */
+final class Author extends BaseTag implements Factory\StaticMethod
+{
+ /** @var string register that this is the author tag. */
+ protected $name = 'author';
+
+ /** @var string The name of the author */
+ private $authorName = '';
+
+ /** @var string The email of the author */
+ private $authorEmail = '';
+
+ /**
+ * Initializes this tag with the author name and e-mail.
+ *
+ * @param string $authorName
+ * @param string $authorEmail
+ */
+ public function __construct($authorName, $authorEmail)
+ {
+ Assert::string($authorName);
+ Assert::string($authorEmail);
+ if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) {
+ throw new \InvalidArgumentException('The author tag does not have a valid e-mail address');
+ }
+
+ $this->authorName = $authorName;
+ $this->authorEmail = $authorEmail;
+ }
+
+ /**
+ * Gets the author's name.
+ *
+ * @return string The author's name.
+ */
+ public function getAuthorName()
+ {
+ return $this->authorName;
+ }
+
+ /**
+ * Returns the author's email.
+ *
+ * @return string The author's email.
+ */
+ public function getEmail()
+ {
+ return $this->authorEmail;
+ }
+
+ /**
+ * Returns this tag in string form.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->authorName . '<' . $this->authorEmail . '>';
+ }
+
+ /**
+ * Attempts to create a new Author object based on †he tag body.
+ *
+ * @param string $body
+ *
+ * @return static
+ */
+ public static function create($body)
+ {
+ Assert::string($body);
+
+ $splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $body, $matches);
+ if (!$splitTagContent) {
+ return null;
+ }
+
+ $authorName = trim($matches[1]);
+ $email = isset($matches[2]) ? trim($matches[2]) : '';
+
+ return new static($authorName, $email);
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock;
+use phpDocumentor\Reflection\DocBlock\Description;
+
+/**
+ * Parses a tag definition for a DocBlock.
+ */
+abstract class BaseTag implements DocBlock\Tag
+{
+ /** @var string Name of the tag */
+ protected $name = '';
+
+ /** @var Description|null Description of the tag. */
+ protected $description;
+
+ /**
+ * Gets the name of this tag.
+ *
+ * @return string The name of this tag.
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ public function render(Formatter $formatter = null)
+ {
+ if ($formatter === null) {
+ $formatter = new Formatter\PassthroughFormatter();
+ }
+
+ return $formatter->format($this);
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Fqsen;
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use phpDocumentor\Reflection\FqsenResolver;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a @covers tag in a Docblock.
+ */
+final class Covers extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'covers';
+
+ /** @var Fqsen */
+ private $refers = null;
+
+ /**
+ * Initializes this tag.
+ *
+ * @param Fqsen $refers
+ * @param Description $description
+ */
+ public function __construct(Fqsen $refers, Description $description = null)
+ {
+ $this->refers = $refers;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ DescriptionFactory $descriptionFactory = null,
+ FqsenResolver $resolver = null,
+ TypeContext $context = null
+ )
+ {
+ Assert::string($body);
+ Assert::notEmpty($body);
+
+ $parts = preg_split('/\s+/Su', $body, 2);
+
+ return new static(
+ $resolver->resolve($parts[0], $context),
+ $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context)
+ );
+ }
+
+ /**
+ * Returns the structural element this tag refers to.
+ *
+ * @return Fqsen
+ */
+ public function getReference()
+ {
+ return $this->refers;
+ }
+
+ /**
+ * Returns a string representation of this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->refers . ($this->description ? ' ' . $this->description->render() : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}deprecated tag in a Docblock.
+ */
+final class Deprecated extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'deprecated';
+
+ /**
+ * PCRE regular expression matching a version vector.
+ * Assumes the "x" modifier.
+ */
+ const REGEX_VECTOR = '(?:
+ # Normal release vectors.
+ \d\S*
+ |
+ # VCS version vectors. Per PHPCS, they are expected to
+ # follow the form of the VCS name, followed by ":", followed
+ # by the version vector itself.
+ # By convention, popular VCSes like CVS, SVN and GIT use "$"
+ # around the actual version vector.
+ [^\s\:]+\:\s*\$[^\$]+\$
+ )';
+
+ /** @var string The version vector. */
+ private $version = '';
+
+ public function __construct($version = null, Description $description = null)
+ {
+ Assert::nullOrStringNotEmpty($version);
+
+ $this->version = $version;
+ $this->description = $description;
+ }
+
+ /**
+ * @return static
+ */
+ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
+ {
+ Assert::nullOrString($body);
+ if (empty($body)) {
+ return new static();
+ }
+
+ $matches = [];
+ if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
+ return new static(
+ null,
+ null !== $descriptionFactory ? $descriptionFactory->create($body, $context) : null
+ );
+ }
+
+ return new static(
+ $matches[1],
+ $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
+ );
+ }
+
+ /**
+ * Gets the version section of the tag.
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->version . ($this->description ? ' ' . $this->description->render() : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Tag;
+
+/**
+ * Reflection class for a {@}example tag in a Docblock.
+ */
+final class Example extends BaseTag
+{
+ /**
+ * @var string Path to a file to use as an example. May also be an absolute URI.
+ */
+ private $filePath = '';
+
+ /**
+ * @var bool Whether the file path component represents an URI. This determines how the file portion
+ * appears at {@link getContent()}.
+ */
+ private $isURI = false;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContent()
+ {
+ if (null === $this->description) {
+ $filePath = '"' . $this->filePath . '"';
+ if ($this->isURI) {
+ $filePath = $this->isUriRelative($this->filePath)
+ ? str_replace('%2F', '/', rawurlencode($this->filePath))
+ :$this->filePath;
+ }
+
+ $this->description = $filePath . ' ' . parent::getContent();
+ }
+
+ return $this->description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create($body)
+ {
+ // File component: File path in quotes or File URI / Source information
+ if (! preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
+ return null;
+ }
+
+ $filePath = null;
+ $fileUri = null;
+ if ('' !== $matches[1]) {
+ $filePath = $matches[1];
+ } else {
+ $fileUri = $matches[2];
+ }
+
+ $startingLine = 1;
+ $lineCount = null;
+ $description = null;
+
+ // Starting line / Number of lines / Description
+ if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $matches[3], $matches)) {
+ $startingLine = (int)$matches[1];
+ if (isset($matches[2]) && $matches[2] !== '') {
+ $lineCount = (int)$matches[2];
+ }
+ $description = $matches[3];
+ }
+
+ return new static($filePath, $fileUri, $startingLine, $lineCount, $description);
+ }
+
+ /**
+ * Returns the file path.
+ *
+ * @return string Path to a file to use as an example.
+ * May also be an absolute URI.
+ */
+ public function getFilePath()
+ {
+ return $this->filePath;
+ }
+
+ /**
+ * Sets the file path.
+ *
+ * @param string $filePath The new file path to use for the example.
+ *
+ * @return $this
+ */
+ public function setFilePath($filePath)
+ {
+ $this->isURI = false;
+ $this->filePath = trim($filePath);
+
+ $this->description = null;
+ return $this;
+ }
+
+ /**
+ * Sets the file path as an URI.
+ *
+ * This function is equivalent to {@link setFilePath()}, except that it
+ * converts an URI to a file path before that.
+ *
+ * There is no getFileURI(), as {@link getFilePath()} is compatible.
+ *
+ * @param string $uri The new file URI to use as an example.
+ *
+ * @return $this
+ */
+ public function setFileURI($uri)
+ {
+ $this->isURI = true;
+ $this->description = null;
+
+ $this->filePath = $this->isUriRelative($uri)
+ ? rawurldecode(str_replace(array('/', '\\'), '%2F', $uri))
+ : $this->filePath = $uri;
+
+ return $this;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->filePath . ($this->description ? ' ' . $this->description->render() : '');
+ }
+
+ /**
+ * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute).
+ *
+ * @param string $uri
+ *
+ * @return bool
+ */
+ private function isUriRelative($uri)
+ {
+ return false === strpos($uri, ':');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+interface StaticMethod
+{
+ public static function create($body);
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+interface Strategy
+{
+ public function create($body);
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Tag;
+
+interface Formatter
+{
+ /**
+ * Formats a tag into a string representation according to a specific format, such as Markdown.
+ *
+ * @param Tag $tag
+ *
+ * @return string
+ */
+ public function format(Tag $tag);
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter;
+
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
+
+class PassthroughFormatter implements Formatter
+{
+ /**
+ * Formats the given tag to return a simple plain text version.
+ *
+ * @param Tag $tag
+ *
+ * @return string
+ */
+ public function format(Tag $tag)
+ {
+ return '@' . $tag->getName() . ' ' . (string)$tag;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Parses a tag definition for a DocBlock.
+ */
+class Generic extends BaseTag implements Factory\StaticMethod
+{
+ /**
+ * Parses a tag and populates the member variables.
+ *
+ * @param string $name Name of the tag.
+ * @param Description $description The contents of the given tag.
+ */
+ public function __construct($name, Description $description = null)
+ {
+ $this->validateTagName($name);
+
+ $this->name = $name;
+ $this->description = $description;
+ }
+
+ /**
+ * Creates a new tag that represents any unknown tag type.
+ *
+ * @param string $body
+ * @param string $name
+ * @param DescriptionFactory $descriptionFactory
+ * @param TypeContext $context
+ *
+ * @return static
+ */
+ public static function create(
+ $body,
+ $name = '',
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::string($body);
+ Assert::stringNotEmpty($name);
+ Assert::notNull($descriptionFactory);
+
+ $description = $descriptionFactory && $body ? $descriptionFactory->create($body, $context) : null;
+
+ return new static($name, $description);
+ }
+
+ /**
+ * Returns the tag as a serialized string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return ($this->description ? $this->description->render() : '');
+ }
+
+ /**
+ * Validates if the tag name matches the expected format, otherwise throws an exception.
+ *
+ * @param string $name
+ *
+ * @return void
+ */
+ private function validateTagName($name)
+ {
+ if (! preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) {
+ throw new \InvalidArgumentException(
+ 'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, '
+ . 'hyphens and backslashes.'
+ );
+ }
+ }
+}
+<?php
+/**
+ * phpDocumentor
+ *
+ * PHP Version 5.3
+ *
+ * @author Ben Selby <benmatselby@gmail.com>
+ * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a @link tag in a Docblock.
+ */
+final class Link extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'link';
+
+ /** @var string */
+ private $link = '';
+
+ /**
+ * Initializes a link to a URL.
+ *
+ * @param string $link
+ * @param Description $description
+ */
+ public function __construct($link, Description $description = null)
+ {
+ Assert::string($link);
+
+ $this->link = $link;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
+ {
+ Assert::string($body);
+ Assert::notNull($descriptionFactory);
+
+ $parts = preg_split('/\s+/Su', $body, 2);
+ $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
+
+ return new static($parts[0], $description);
+ }
+
+ /**
+ * Gets the link
+ *
+ * @return string
+ */
+ public function getLink()
+ {
+ return $this->link;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->link . ($this->description ? ' ' . $this->description->render() : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use phpDocumentor\Reflection\Types\Void_;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for an {@}method in a Docblock.
+ */
+final class Method extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'method';
+
+ /** @var string */
+ private $methodName = '';
+
+ /** @var string[] */
+ private $arguments = [];
+
+ /** @var bool */
+ private $isStatic = false;
+
+ /** @var Type */
+ private $returnType;
+
+ public function __construct(
+ $methodName,
+ array $arguments = [],
+ Type $returnType = null,
+ $static = false,
+ Description $description = null
+ ) {
+ Assert::stringNotEmpty($methodName);
+ Assert::boolean($static);
+
+ if ($returnType === null) {
+ $returnType = new Void_();
+ }
+
+ $this->methodName = $methodName;
+ $this->arguments = $this->filterArguments($arguments);
+ $this->returnType = $returnType;
+ $this->isStatic = $static;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::stringNotEmpty($body);
+ Assert::allNotNull([ $typeResolver, $descriptionFactory ]);
+
+ // 1. none or more whitespace
+ // 2. optionally the keyword "static" followed by whitespace
+ // 3. optionally a word with underscores followed by whitespace : as
+ // type for the return value
+ // 4. then optionally a word with underscores followed by () and
+ // whitespace : as method name as used by phpDocumentor
+ // 5. then a word with underscores, followed by ( and any character
+ // until a ) and whitespace : as method name with signature
+ // 6. any remaining text : as description
+ if (!preg_match(
+ '/^
+ # Static keyword
+ # Declares a static method ONLY if type is also present
+ (?:
+ (static)
+ \s+
+ )?
+ # Return type
+ (?:
+ (
+ (?:[\w\|_\\\\]+)
+ # array notation
+ (?:\[\])*
+ )?
+ \s+
+ )?
+ # Legacy method name (not captured)
+ (?:
+ [\w_]+\(\)\s+
+ )?
+ # Method name
+ ([\w\|_\\\\]+)
+ # Arguments
+ (?:
+ \(([^\)]*)\)
+ )?
+ \s*
+ # Description
+ (.*)
+ $/sux',
+ $body,
+ $matches
+ )) {
+ return null;
+ }
+
+ list(, $static, $returnType, $methodName, $arguments, $description) = $matches;
+
+ $static = $static === 'static';
+ $returnType = $typeResolver->resolve($returnType, $context);
+ $description = $descriptionFactory->create($description, $context);
+
+ if ('' !== $arguments) {
+ $arguments = explode(',', $arguments);
+ foreach($arguments as &$argument) {
+ $argument = explode(' ', self::stripRestArg(trim($argument)), 2);
+ if ($argument[0][0] === '$') {
+ $argumentName = substr($argument[0], 1);
+ $argumentType = new Void_();
+ } else {
+ $argumentType = $typeResolver->resolve($argument[0], $context);
+ $argumentName = '';
+ if (isset($argument[1])) {
+ $argument[1] = self::stripRestArg($argument[1]);
+ $argumentName = substr($argument[1], 1);
+ }
+ }
+
+ $argument = [ 'name' => $argumentName, 'type' => $argumentType];
+ }
+ } else {
+ $arguments = [];
+ }
+
+ return new static($methodName, $arguments, $returnType, $static, $description);
+ }
+
+ /**
+ * Retrieves the method name.
+ *
+ * @return string
+ */
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getArguments()
+ {
+ return $this->arguments;
+ }
+
+ /**
+ * Checks whether the method tag describes a static method or not.
+ *
+ * @return bool TRUE if the method declaration is for a static method, FALSE otherwise.
+ */
+ public function isStatic()
+ {
+ return $this->isStatic;
+ }
+
+ /**
+ * @return Type
+ */
+ public function getReturnType()
+ {
+ return $this->returnType;
+ }
+
+ public function __toString()
+ {
+ $arguments = [];
+ foreach ($this->arguments as $argument) {
+ $arguments[] = $argument['type'] . ' $' . $argument['name'];
+ }
+
+ return ($this->isStatic() ? 'static ' : '')
+ . (string)$this->returnType . ' '
+ . $this->methodName
+ . '(' . implode(', ', $arguments) . ')'
+ . ($this->description ? ' ' . $this->description->render() : '');
+ }
+
+ private function filterArguments($arguments)
+ {
+ foreach ($arguments as &$argument) {
+ if (is_string($argument)) {
+ $argument = [ 'name' => $argument ];
+ }
+ if (! isset($argument['type'])) {
+ $argument['type'] = new Void_();
+ }
+ $keys = array_keys($argument);
+ if ($keys !== [ 'name', 'type' ]) {
+ throw new \InvalidArgumentException(
+ 'Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, true)
+ );
+ }
+ }
+
+ return $arguments;
+ }
+
+ private static function stripRestArg($argument)
+ {
+ if (strpos($argument, '...') === 0) {
+ $argument = trim(substr($argument, 3));
+ }
+
+ return $argument;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for the {@}param tag in a Docblock.
+ */
+final class Param extends BaseTag implements Factory\StaticMethod
+{
+ /** @var string */
+ protected $name = 'param';
+
+ /** @var Type */
+ private $type;
+
+ /** @var string */
+ private $variableName = '';
+
+ /** @var bool determines whether this is a variadic argument */
+ private $isVariadic = false;
+
+ /**
+ * @param string $variableName
+ * @param Type $type
+ * @param bool $isVariadic
+ * @param Description $description
+ */
+ public function __construct($variableName, Type $type = null, $isVariadic = false, Description $description = null)
+ {
+ Assert::string($variableName);
+ Assert::boolean($isVariadic);
+
+ $this->variableName = $variableName;
+ $this->type = $type;
+ $this->isVariadic = $isVariadic;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::stringNotEmpty($body);
+ Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+ $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
+ $type = null;
+ $variableName = '';
+ $isVariadic = false;
+
+ // if the first item that is encountered is not a variable; it is a type
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
+ $type = $typeResolver->resolve(array_shift($parts), $context);
+ array_shift($parts);
+ }
+
+ // if the next item starts with a $ or ...$ it must be the variable name
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$' || substr($parts[0], 0, 4) === '...$')) {
+ $variableName = array_shift($parts);
+ array_shift($parts);
+
+ if (substr($variableName, 0, 3) === '...') {
+ $isVariadic = true;
+ $variableName = substr($variableName, 3);
+ }
+
+ if (substr($variableName, 0, 1) === '$') {
+ $variableName = substr($variableName, 1);
+ }
+ }
+
+ $description = $descriptionFactory->create(implode('', $parts), $context);
+
+ return new static($variableName, $type, $isVariadic, $description);
+ }
+
+ /**
+ * Returns the variable's name.
+ *
+ * @return string
+ */
+ public function getVariableName()
+ {
+ return $this->variableName;
+ }
+
+ /**
+ * Returns the variable's type or null if unknown.
+ *
+ * @return Type|null
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Returns whether this tag is variadic.
+ *
+ * @return boolean
+ */
+ public function isVariadic()
+ {
+ return $this->isVariadic;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return ($this->type ? $this->type . ' ' : '')
+ . ($this->isVariadic() ? '...' : '')
+ . '$' . $this->variableName
+ . ($this->description ? ' ' . $this->description : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}property tag in a Docblock.
+ */
+class Property extends BaseTag implements Factory\StaticMethod
+{
+ /** @var string */
+ protected $name = 'property';
+
+ /** @var Type */
+ private $type;
+
+ /** @var string */
+ protected $variableName = '';
+
+ /**
+ * @param string $variableName
+ * @param Type $type
+ * @param Description $description
+ */
+ public function __construct($variableName, Type $type = null, Description $description = null)
+ {
+ Assert::string($variableName);
+
+ $this->variableName = $variableName;
+ $this->type = $type;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::stringNotEmpty($body);
+ Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+ $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
+ $type = null;
+ $variableName = '';
+
+ // if the first item that is encountered is not a variable; it is a type
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
+ $type = $typeResolver->resolve(array_shift($parts), $context);
+ array_shift($parts);
+ }
+
+ // if the next item starts with a $ or ...$ it must be the variable name
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
+ $variableName = array_shift($parts);
+ array_shift($parts);
+
+ if (substr($variableName, 0, 1) === '$') {
+ $variableName = substr($variableName, 1);
+ }
+ }
+
+ $description = $descriptionFactory->create(implode('', $parts), $context);
+
+ return new static($variableName, $type, $description);
+ }
+
+ /**
+ * Returns the variable's name.
+ *
+ * @return string
+ */
+ public function getVariableName()
+ {
+ return $this->variableName;
+ }
+
+ /**
+ * Returns the variable's type or null if unknown.
+ *
+ * @return Type|null
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return ($this->type ? $this->type . ' ' : '')
+ . '$' . $this->variableName
+ . ($this->description ? ' ' . $this->description : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}property-read tag in a Docblock.
+ */
+class PropertyRead extends BaseTag implements Factory\StaticMethod
+{
+ /** @var string */
+ protected $name = 'property-read';
+
+ /** @var Type */
+ private $type;
+
+ /** @var string */
+ protected $variableName = '';
+
+ /**
+ * @param string $variableName
+ * @param Type $type
+ * @param Description $description
+ */
+ public function __construct($variableName, Type $type = null, Description $description = null)
+ {
+ Assert::string($variableName);
+
+ $this->variableName = $variableName;
+ $this->type = $type;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::stringNotEmpty($body);
+ Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+ $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
+ $type = null;
+ $variableName = '';
+
+ // if the first item that is encountered is not a variable; it is a type
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
+ $type = $typeResolver->resolve(array_shift($parts), $context);
+ array_shift($parts);
+ }
+
+ // if the next item starts with a $ or ...$ it must be the variable name
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
+ $variableName = array_shift($parts);
+ array_shift($parts);
+
+ if (substr($variableName, 0, 1) === '$') {
+ $variableName = substr($variableName, 1);
+ }
+ }
+
+ $description = $descriptionFactory->create(implode('', $parts), $context);
+
+ return new static($variableName, $type, $description);
+ }
+
+ /**
+ * Returns the variable's name.
+ *
+ * @return string
+ */
+ public function getVariableName()
+ {
+ return $this->variableName;
+ }
+
+ /**
+ * Returns the variable's type or null if unknown.
+ *
+ * @return Type|null
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return ($this->type ? $this->type . ' ' : '')
+ . '$' . $this->variableName
+ . ($this->description ? ' ' . $this->description : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}property-write tag in a Docblock.
+ */
+class PropertyWrite extends BaseTag implements Factory\StaticMethod
+{
+ /** @var string */
+ protected $name = 'property-write';
+
+ /** @var Type */
+ private $type;
+
+ /** @var string */
+ protected $variableName = '';
+
+ /**
+ * @param string $variableName
+ * @param Type $type
+ * @param Description $description
+ */
+ public function __construct($variableName, Type $type = null, Description $description = null)
+ {
+ Assert::string($variableName);
+
+ $this->variableName = $variableName;
+ $this->type = $type;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::stringNotEmpty($body);
+ Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+ $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
+ $type = null;
+ $variableName = '';
+
+ // if the first item that is encountered is not a variable; it is a type
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
+ $type = $typeResolver->resolve(array_shift($parts), $context);
+ array_shift($parts);
+ }
+
+ // if the next item starts with a $ or ...$ it must be the variable name
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
+ $variableName = array_shift($parts);
+ array_shift($parts);
+
+ if (substr($variableName, 0, 1) === '$') {
+ $variableName = substr($variableName, 1);
+ }
+ }
+
+ $description = $descriptionFactory->create(implode('', $parts), $context);
+
+ return new static($variableName, $type, $description);
+ }
+
+ /**
+ * Returns the variable's name.
+ *
+ * @return string
+ */
+ public function getVariableName()
+ {
+ return $this->variableName;
+ }
+
+ /**
+ * Returns the variable's type or null if unknown.
+ *
+ * @return Type|null
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return ($this->type ? $this->type . ' ' : '')
+ . '$' . $this->variableName
+ . ($this->description ? ' ' . $this->description : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}return tag in a Docblock.
+ */
+final class Return_ extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'return';
+
+ /** @var Type */
+ private $type;
+
+ public function __construct(Type $type, Description $description = null)
+ {
+ $this->type = $type;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ )
+ {
+ Assert::string($body);
+ Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+ $parts = preg_split('/\s+/Su', $body, 2);
+
+ $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context);
+ $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context);
+
+ return new static($type, $description);
+ }
+
+ /**
+ * Returns the type section of the variable.
+ *
+ * @return Type
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ public function __toString()
+ {
+ return $this->type . ' ' . $this->description;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Fqsen;
+use phpDocumentor\Reflection\FqsenResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use phpDocumentor\Reflection\DocBlock\Description;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for an {@}see tag in a Docblock.
+ */
+class See extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'see';
+
+ /** @var Fqsen */
+ protected $refers = null;
+
+ /**
+ * Initializes this tag.
+ *
+ * @param Fqsen $refers
+ * @param Description $description
+ */
+ public function __construct(Fqsen $refers, Description $description = null)
+ {
+ $this->refers = $refers;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ FqsenResolver $resolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::string($body);
+ Assert::allNotNull([$resolver, $descriptionFactory]);
+
+ $parts = preg_split('/\s+/Su', $body, 2);
+ $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
+
+ return new static($resolver->resolve($parts[0], $context), $description);
+ }
+
+ /**
+ * Returns the structural element this tag refers to.
+ *
+ * @return Fqsen
+ */
+ public function getReference()
+ {
+ return $this->refers;
+ }
+
+ /**
+ * Returns a string representation of this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->refers . ($this->description ? ' ' . $this->description->render() : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}since tag in a Docblock.
+ */
+final class Since extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'since';
+
+ /**
+ * PCRE regular expression matching a version vector.
+ * Assumes the "x" modifier.
+ */
+ const REGEX_VECTOR = '(?:
+ # Normal release vectors.
+ \d\S*
+ |
+ # VCS version vectors. Per PHPCS, they are expected to
+ # follow the form of the VCS name, followed by ":", followed
+ # by the version vector itself.
+ # By convention, popular VCSes like CVS, SVN and GIT use "$"
+ # around the actual version vector.
+ [^\s\:]+\:\s*\$[^\$]+\$
+ )';
+
+ /** @var string The version vector. */
+ private $version = '';
+
+ public function __construct($version = null, Description $description = null)
+ {
+ Assert::nullOrStringNotEmpty($version);
+
+ $this->version = $version;
+ $this->description = $description;
+ }
+
+ /**
+ * @return static
+ */
+ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
+ {
+ Assert::nullOrString($body);
+ if (empty($body)) {
+ return new static();
+ }
+
+ $matches = [];
+ if (! preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
+ return null;
+ }
+
+ return new static(
+ $matches[1],
+ $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
+ );
+ }
+
+ /**
+ * Gets the version section of the tag.
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->version . ($this->description ? ' ' . $this->description->render() : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}source tag in a Docblock.
+ */
+final class Source extends BaseTag implements Factory\StaticMethod
+{
+ /** @var string */
+ protected $name = 'source';
+
+ /** @var int The starting line, relative to the structural element's location. */
+ private $startingLine = 1;
+
+ /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */
+ private $lineCount = null;
+
+ public function __construct($startingLine, $lineCount = null, Description $description = null)
+ {
+ Assert::integerish($startingLine);
+ Assert::nullOrIntegerish($lineCount);
+
+ $this->startingLine = (int)$startingLine;
+ $this->lineCount = $lineCount !== null ? (int)$lineCount : null;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
+ {
+ Assert::stringNotEmpty($body);
+ Assert::notNull($descriptionFactory);
+
+ $startingLine = 1;
+ $lineCount = null;
+ $description = null;
+
+ // Starting line / Number of lines / Description
+ if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $body, $matches)) {
+ $startingLine = (int)$matches[1];
+ if (isset($matches[2]) && $matches[2] !== '') {
+ $lineCount = (int)$matches[2];
+ }
+ $description = $matches[3];
+ }
+
+ return new static($startingLine, $lineCount, $descriptionFactory->create($description, $context));
+ }
+
+ /**
+ * Gets the starting line.
+ *
+ * @return int The starting line, relative to the structural element's
+ * location.
+ */
+ public function getStartingLine()
+ {
+ return $this->startingLine;
+ }
+
+ /**
+ * Returns the number of lines.
+ *
+ * @return int|null The number of lines, relative to the starting line. NULL
+ * means "to the end".
+ */
+ public function getLineCount()
+ {
+ return $this->lineCount;
+ }
+
+ public function __toString()
+ {
+ return $this->startingLine
+ . ($this->lineCount !== null ? ' ' . $this->lineCount : '')
+ . ($this->description ? ' ' . $this->description->render() : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}throws tag in a Docblock.
+ */
+final class Throws extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'throws';
+
+ /** @var Type */
+ private $type;
+
+ public function __construct(Type $type, Description $description = null)
+ {
+ $this->type = $type;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::string($body);
+ Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+ $parts = preg_split('/\s+/Su', $body, 2);
+
+ $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context);
+ $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context);
+
+ return new static($type, $description);
+ }
+
+ /**
+ * Returns the type section of the variable.
+ *
+ * @return Type
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ public function __toString()
+ {
+ return $this->type . ' ' . $this->description;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Fqsen;
+use phpDocumentor\Reflection\FqsenResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}uses tag in a Docblock.
+ */
+final class Uses extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'uses';
+
+ /** @var Fqsen */
+ protected $refers = null;
+
+ /**
+ * Initializes this tag.
+ *
+ * @param Fqsen $refers
+ * @param Description $description
+ */
+ public function __construct(Fqsen $refers, Description $description = null)
+ {
+ $this->refers = $refers;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ FqsenResolver $resolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::string($body);
+ Assert::allNotNull([$resolver, $descriptionFactory]);
+
+ $parts = preg_split('/\s+/Su', $body, 2);
+
+ return new static(
+ $resolver->resolve($parts[0], $context),
+ $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context)
+ );
+ }
+
+ /**
+ * Returns the structural element this tag refers to.
+ *
+ * @return Fqsen
+ */
+ public function getReference()
+ {
+ return $this->refers;
+ }
+
+ /**
+ * Returns a string representation of this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->refers . ' ' . $this->description->render();
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}var tag in a Docblock.
+ */
+class Var_ extends BaseTag implements Factory\StaticMethod
+{
+ /** @var string */
+ protected $name = 'var';
+
+ /** @var Type */
+ private $type;
+
+ /** @var string */
+ protected $variableName = '';
+
+ /**
+ * @param string $variableName
+ * @param Type $type
+ * @param Description $description
+ */
+ public function __construct($variableName, Type $type = null, Description $description = null)
+ {
+ Assert::string($variableName);
+
+ $this->variableName = $variableName;
+ $this->type = $type;
+ $this->description = $description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(
+ $body,
+ TypeResolver $typeResolver = null,
+ DescriptionFactory $descriptionFactory = null,
+ TypeContext $context = null
+ ) {
+ Assert::stringNotEmpty($body);
+ Assert::allNotNull([$typeResolver, $descriptionFactory]);
+
+ $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
+ $type = null;
+ $variableName = '';
+
+ // if the first item that is encountered is not a variable; it is a type
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
+ $type = $typeResolver->resolve(array_shift($parts), $context);
+ array_shift($parts);
+ }
+
+ // if the next item starts with a $ or ...$ it must be the variable name
+ if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] == '$')) {
+ $variableName = array_shift($parts);
+ array_shift($parts);
+
+ if (substr($variableName, 0, 1) === '$') {
+ $variableName = substr($variableName, 1);
+ }
+ }
+
+ $description = $descriptionFactory->create(implode('', $parts), $context);
+
+ return new static($variableName, $type, $description);
+ }
+
+ /**
+ * Returns the variable's name.
+ *
+ * @return string
+ */
+ public function getVariableName()
+ {
+ return $this->variableName;
+ }
+
+ /**
+ * Returns the variable's type or null if unknown.
+ *
+ * @return Type|null
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return ($this->type ? $this->type . ' ' : '')
+ . '$' . $this->variableName
+ . ($this->description ? ' ' . $this->description : '');
+ }
+}
+<?php
+/**
+ * phpDocumentor
+ *
+ * PHP Version 5.3
+ *
+ * @author Vasil Rangelov <boen.robot@gmail.com>
+ * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use phpDocumentor\Reflection\DocBlock\Description;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use Webmozart\Assert\Assert;
+
+/**
+ * Reflection class for a {@}version tag in a Docblock.
+ */
+final class Version extends BaseTag implements Factory\StaticMethod
+{
+ protected $name = 'version';
+
+ /**
+ * PCRE regular expression matching a version vector.
+ * Assumes the "x" modifier.
+ */
+ const REGEX_VECTOR = '(?:
+ # Normal release vectors.
+ \d\S*
+ |
+ # VCS version vectors. Per PHPCS, they are expected to
+ # follow the form of the VCS name, followed by ":", followed
+ # by the version vector itself.
+ # By convention, popular VCSes like CVS, SVN and GIT use "$"
+ # around the actual version vector.
+ [^\s\:]+\:\s*\$[^\$]+\$
+ )';
+
+ /** @var string The version vector. */
+ private $version = '';
+
+ public function __construct($version = null, Description $description = null)
+ {
+ Assert::nullOrStringNotEmpty($version);
+
+ $this->version = $version;
+ $this->description = $description;
+ }
+
+ /**
+ * @return static
+ */
+ public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
+ {
+ Assert::nullOrString($body);
+ if (empty($body)) {
+ return new static();
+ }
+
+ $matches = [];
+ if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
+ return null;
+ }
+
+ return new static(
+ $matches[1],
+ $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
+ );
+ }
+
+ /**
+ * Gets the version section of the tag.
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Returns a string representation for this tag.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->version . ($this->description ? ' ' . $this->description->render() : '');
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\TagFactory;
+use Webmozart\Assert\Assert;
+
+final class DocBlockFactory implements DocBlockFactoryInterface
+{
+ /** @var DocBlock\DescriptionFactory */
+ private $descriptionFactory;
+
+ /** @var DocBlock\TagFactory */
+ private $tagFactory;
+
+ /**
+ * Initializes this factory with the required subcontractors.
+ *
+ * @param DescriptionFactory $descriptionFactory
+ * @param TagFactory $tagFactory
+ */
+ public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory)
+ {
+ $this->descriptionFactory = $descriptionFactory;
+ $this->tagFactory = $tagFactory;
+ }
+
+ /**
+ * Factory method for easy instantiation.
+ *
+ * @param string[] $additionalTags
+ *
+ * @return DocBlockFactory
+ */
+ public static function createInstance(array $additionalTags = [])
+ {
+ $fqsenResolver = new FqsenResolver();
+ $tagFactory = new StandardTagFactory($fqsenResolver);
+ $descriptionFactory = new DescriptionFactory($tagFactory);
+
+ $tagFactory->addService($descriptionFactory);
+ $tagFactory->addService(new TypeResolver($fqsenResolver));
+
+ $docBlockFactory = new self($descriptionFactory, $tagFactory);
+ foreach ($additionalTags as $tagName => $tagHandler) {
+ $docBlockFactory->registerTagHandler($tagName, $tagHandler);
+ }
+
+ return $docBlockFactory;
+ }
+
+ /**
+ * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the
+ * getDocComment method (such as a ReflectionClass object).
+ * @param Types\Context $context
+ * @param Location $location
+ *
+ * @return DocBlock
+ */
+ public function create($docblock, Types\Context $context = null, Location $location = null)
+ {
+ if (is_object($docblock)) {
+ if (!method_exists($docblock, 'getDocComment')) {
+ $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method';
+ throw new \InvalidArgumentException($exceptionMessage);
+ }
+
+ $docblock = $docblock->getDocComment();
+ }
+
+ Assert::stringNotEmpty($docblock);
+
+ if ($context === null) {
+ $context = new Types\Context('');
+ }
+
+ $parts = $this->splitDocBlock($this->stripDocComment($docblock));
+ list($templateMarker, $summary, $description, $tags) = $parts;
+
+ return new DocBlock(
+ $summary,
+ $description ? $this->descriptionFactory->create($description, $context) : null,
+ array_filter($this->parseTagBlock($tags, $context), function($tag) {
+ return $tag instanceof Tag;
+ }),
+ $context,
+ $location,
+ $templateMarker === '#@+',
+ $templateMarker === '#@-'
+ );
+ }
+
+ public function registerTagHandler($tagName, $handler)
+ {
+ $this->tagFactory->registerTagHandler($tagName, $handler);
+ }
+
+ /**
+ * Strips the asterisks from the DocBlock comment.
+ *
+ * @param string $comment String containing the comment text.
+ *
+ * @return string
+ */
+ private function stripDocComment($comment)
+ {
+ $comment = trim(preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment));
+
+ // reg ex above is not able to remove */ from a single line docblock
+ if (substr($comment, -2) == '*/') {
+ $comment = trim(substr($comment, 0, -2));
+ }
+
+ return str_replace(array("\r\n", "\r"), "\n", $comment);
+ }
+
+ /**
+ * Splits the DocBlock into a template marker, summary, description and block of tags.
+ *
+ * @param string $comment Comment to split into the sub-parts.
+ *
+ * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
+ * @author Mike van Riel <me@mikevanriel.com> for extending the regex with template marker support.
+ *
+ * @return string[] containing the template marker (if any), summary, description and a string containing the tags.
+ */
+ private function splitDocBlock($comment)
+ {
+ // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
+ // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
+ // performance impact of running a regular expression
+ if (strpos($comment, '@') === 0) {
+ return array('', '', '', $comment);
+ }
+
+ // clears all extra horizontal whitespace from the line endings to prevent parsing issues
+ $comment = preg_replace('/\h*$/Sum', '', $comment);
+
+ /*
+ * Splits the docblock into a template marker, summary, description and tags section.
+ *
+ * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may
+ * occur after it and will be stripped).
+ * - The short description is started from the first character until a dot is encountered followed by a
+ * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing
+ * errors). This is optional.
+ * - The long description, any character until a new line is encountered followed by an @ and word
+ * characters (a tag). This is optional.
+ * - Tags; the remaining characters
+ *
+ * Big thanks to RichardJ for contributing this Regular Expression
+ */
+ preg_match(
+ '/
+ \A
+ # 1. Extract the template marker
+ (?:(\#\@\+|\#\@\-)\n?)?
+
+ # 2. Extract the summary
+ (?:
+ (?! @\pL ) # The summary may not start with an @
+ (
+ [^\n.]+
+ (?:
+ (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines
+ [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
+ [^\n.]+ # Include anything else
+ )*
+ \.?
+ )?
+ )
+
+ # 3. Extract the description
+ (?:
+ \s* # Some form of whitespace _must_ precede a description because a summary must be there
+ (?! @\pL ) # The description may not start with an @
+ (
+ [^\n]+
+ (?: \n+
+ (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line
+ [^\n]+ # Include anything else
+ )*
+ )
+ )?
+
+ # 4. Extract the tags (anything that follows)
+ (\s+ [\s\S]*)? # everything that follows
+ /ux',
+ $comment,
+ $matches
+ );
+ array_shift($matches);
+
+ while (count($matches) < 4) {
+ $matches[] = '';
+ }
+
+ return $matches;
+ }
+
+ /**
+ * Creates the tag objects.
+ *
+ * @param string $tags Tag block to parse.
+ * @param Types\Context $context Context of the parsed Tag
+ *
+ * @return DocBlock\Tag[]
+ */
+ private function parseTagBlock($tags, Types\Context $context)
+ {
+ $tags = $this->filterTagBlock($tags);
+ if (!$tags) {
+ return [];
+ }
+
+ $result = $this->splitTagBlockIntoTagLines($tags);
+ foreach ($result as $key => $tagLine) {
+ $result[$key] = $this->tagFactory->create(trim($tagLine), $context);
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param string $tags
+ *
+ * @return string[]
+ */
+ private function splitTagBlockIntoTagLines($tags)
+ {
+ $result = array();
+ foreach (explode("\n", $tags) as $tag_line) {
+ if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
+ $result[] = $tag_line;
+ } else {
+ $result[count($result) - 1] .= "\n" . $tag_line;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param $tags
+ * @return string
+ */
+ private function filterTagBlock($tags)
+ {
+ $tags = trim($tags);
+ if (!$tags) {
+ return null;
+ }
+
+ if ('@' !== $tags[0]) {
+ // @codeCoverageIgnoreStart
+ // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that
+ // we didn't foresee.
+ throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $tags;
+ }
+}
+<?php
+namespace phpDocumentor\Reflection;
+
+interface DocBlockFactoryInterface
+{
+ /**
+ * Factory method for easy instantiation.
+ *
+ * @param string[] $additionalTags
+ *
+ * @return DocBlockFactory
+ */
+ public static function createInstance(array $additionalTags = []);
+
+ /**
+ * @param string $docblock
+ * @param Types\Context $context
+ * @param Location $location
+ *
+ * @return DocBlock
+ */
+ public function create($docblock, Types\Context $context = null, Location $location = null);
+}
+The MIT License (MIT)
+
+Copyright (c) 2010 Mike van Riel
+
+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.
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+use phpDocumentor\Reflection\Types\Context;
+
+class FqsenResolver
+{
+ /** @var string Definition of the NAMESPACE operator in PHP */
+ const OPERATOR_NAMESPACE = '\\';
+
+ public function resolve($fqsen, Context $context = null)
+ {
+ if ($context === null) {
+ $context = new Context('');
+ }
+
+ if ($this->isFqsen($fqsen)) {
+ return new Fqsen($fqsen);
+ }
+
+ return $this->resolvePartialStructuralElementName($fqsen, $context);
+ }
+
+ /**
+ * Tests whether the given type is a Fully Qualified Structural Element Name.
+ *
+ * @param string $type
+ *
+ * @return bool
+ */
+ private function isFqsen($type)
+ {
+ return strpos($type, self::OPERATOR_NAMESPACE) === 0;
+ }
+
+ /**
+ * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation
+ * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context.
+ *
+ * @param string $type
+ * @param Context $context
+ *
+ * @return Fqsen
+ */
+ private function resolvePartialStructuralElementName($type, Context $context)
+ {
+ $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2);
+
+ $namespaceAliases = $context->getNamespaceAliases();
+
+ // if the first segment is not an alias; prepend namespace name and return
+ if (!isset($namespaceAliases[$typeParts[0]])) {
+ $namespace = $context->getNamespace();
+ if ('' !== $namespace) {
+ $namespace .= self::OPERATOR_NAMESPACE;
+ }
+
+ return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type);
+ }
+
+ $typeParts[0] = $namespaceAliases[$typeParts[0]];
+
+ return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts));
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+interface Type
+{
+ public function __toString();
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection;
+
+use phpDocumentor\Reflection\Types\Array_;
+use phpDocumentor\Reflection\Types\Compound;
+use phpDocumentor\Reflection\Types\Context;
+use phpDocumentor\Reflection\Types\Object_;
+
+final class TypeResolver
+{
+ /** @var string Definition of the ARRAY operator for types */
+ const OPERATOR_ARRAY = '[]';
+
+ /** @var string Definition of the NAMESPACE operator in PHP */
+ const OPERATOR_NAMESPACE = '\\';
+
+ /** @var string[] List of recognized keywords and unto which Value Object they map */
+ private $keywords = array(
+ 'string' => 'phpDocumentor\Reflection\Types\String_',
+ 'int' => 'phpDocumentor\Reflection\Types\Integer',
+ 'integer' => 'phpDocumentor\Reflection\Types\Integer',
+ 'bool' => 'phpDocumentor\Reflection\Types\Boolean',
+ 'boolean' => 'phpDocumentor\Reflection\Types\Boolean',
+ 'float' => 'phpDocumentor\Reflection\Types\Float_',
+ 'double' => 'phpDocumentor\Reflection\Types\Float_',
+ 'object' => 'phpDocumentor\Reflection\Types\Object_',
+ 'mixed' => 'phpDocumentor\Reflection\Types\Mixed',
+ 'array' => 'phpDocumentor\Reflection\Types\Array_',
+ 'resource' => 'phpDocumentor\Reflection\Types\Resource',
+ 'void' => 'phpDocumentor\Reflection\Types\Void_',
+ 'null' => 'phpDocumentor\Reflection\Types\Null_',
+ 'scalar' => 'phpDocumentor\Reflection\Types\Scalar',
+ 'callback' => 'phpDocumentor\Reflection\Types\Callable_',
+ 'callable' => 'phpDocumentor\Reflection\Types\Callable_',
+ 'false' => 'phpDocumentor\Reflection\Types\Boolean',
+ 'true' => 'phpDocumentor\Reflection\Types\Boolean',
+ 'self' => 'phpDocumentor\Reflection\Types\Self_',
+ '$this' => 'phpDocumentor\Reflection\Types\This',
+ 'static' => 'phpDocumentor\Reflection\Types\Static_'
+ );
+
+ /** @var FqsenResolver */
+ private $fqsenResolver;
+
+ /**
+ * Initializes this TypeResolver with the means to create and resolve Fqsen objects.
+ *
+ * @param FqsenResolver $fqsenResolver
+ */
+ public function __construct(FqsenResolver $fqsenResolver = null)
+ {
+ $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver();
+ }
+
+ /**
+ * Analyzes the given type and returns the FQCN variant.
+ *
+ * When a type is provided this method checks whether it is not a keyword or
+ * Fully Qualified Class Name. If so it will use the given namespace and
+ * aliases to expand the type to a FQCN representation.
+ *
+ * This method only works as expected if the namespace and aliases are set;
+ * no dynamic reflection is being performed here.
+ *
+ * @param string $type The relative or absolute type.
+ * @param Context $context
+ *
+ * @uses Context::getNamespace() to determine with what to prefix the type name.
+ * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be
+ * replaced with another namespace.
+ *
+ * @return Type|null
+ */
+ public function resolve($type, Context $context = null)
+ {
+ if (!is_string($type)) {
+ throw new \InvalidArgumentException(
+ 'Attempted to resolve type but it appeared not to be a string, received: ' . var_export($type, true)
+ );
+ }
+
+ $type = trim($type);
+ if (!$type) {
+ throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty');
+ }
+
+ if ($context === null) {
+ $context = new Context('');
+ }
+
+ switch (true) {
+ case $this->isKeyword($type):
+ return $this->resolveKeyword($type);
+ case ($this->isCompoundType($type)):
+ return $this->resolveCompoundType($type, $context);
+ case $this->isTypedArray($type):
+ return $this->resolveTypedArray($type, $context);
+ case $this->isFqsen($type):
+ return $this->resolveTypedObject($type);
+ case $this->isPartialStructuralElementName($type):
+ return $this->resolveTypedObject($type, $context);
+ // @codeCoverageIgnoreStart
+ default:
+ // I haven't got the foggiest how the logic would come here but added this as a defense.
+ throw new \RuntimeException(
+ 'Unable to resolve type "' . $type . '", there is no known method to resolve it'
+ );
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ /**
+ * Adds a keyword to the list of Keywords and associates it with a specific Value Object.
+ *
+ * @param string $keyword
+ * @param string $typeClassName
+ *
+ * @return void
+ */
+ public function addKeyword($keyword, $typeClassName)
+ {
+ if (!class_exists($typeClassName)) {
+ throw new \InvalidArgumentException(
+ 'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class'
+ . ' but we could not find the class ' . $typeClassName
+ );
+ }
+
+ if (!in_array(Type::class, class_implements($typeClassName))) {
+ throw new \InvalidArgumentException(
+ 'The class "' . $typeClassName . '" must implement the interface "phpDocumentor\Reflection\Type"'
+ );
+ }
+
+ $this->keywords[$keyword] = $typeClassName;
+ }
+
+ /**
+ * Detects whether the given type represents an array.
+ *
+ * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
+ *
+ * @return bool
+ */
+ private function isTypedArray($type)
+ {
+ return substr($type, -2) === self::OPERATOR_ARRAY;
+ }
+
+ /**
+ * Detects whether the given type represents a PHPDoc keyword.
+ *
+ * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
+ *
+ * @return bool
+ */
+ private function isKeyword($type)
+ {
+ return in_array(strtolower($type), array_keys($this->keywords), true);
+ }
+
+ /**
+ * Detects whether the given type represents a relative structural element name.
+ *
+ * @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
+ *
+ * @return bool
+ */
+ private function isPartialStructuralElementName($type)
+ {
+ return ($type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type);
+ }
+
+ /**
+ * Tests whether the given type is a Fully Qualified Structural Element Name.
+ *
+ * @param string $type
+ *
+ * @return bool
+ */
+ private function isFqsen($type)
+ {
+ return strpos($type, self::OPERATOR_NAMESPACE) === 0;
+ }
+
+ /**
+ * Tests whether the given type is a compound type (i.e. `string|int`).
+ *
+ * @param string $type
+ *
+ * @return bool
+ */
+ private function isCompoundType($type)
+ {
+ return strpos($type, '|') !== false;
+ }
+
+ /**
+ * Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set.
+ *
+ * @param string $type
+ * @param Context $context
+ *
+ * @return Array_
+ */
+ private function resolveTypedArray($type, Context $context)
+ {
+ return new Array_($this->resolve(substr($type, 0, -2), $context));
+ }
+
+ /**
+ * Resolves the given keyword (such as `string`) into a Type object representing that keyword.
+ *
+ * @param string $type
+ *
+ * @return Type
+ */
+ private function resolveKeyword($type)
+ {
+ $className = $this->keywords[strtolower($type)];
+
+ return new $className();
+ }
+
+ /**
+ * Resolves the given FQSEN string into an FQSEN object.
+ *
+ * @param string $type
+ *
+ * @return Object_
+ */
+ private function resolveTypedObject($type, Context $context = null)
+ {
+ return new Object_($this->fqsenResolver->resolve($type, $context));
+ }
+
+ /**
+ * Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN.
+ *
+ * @param string $type
+ * @param Context $context
+ *
+ * @return Compound
+ */
+ private function resolveCompoundType($type, Context $context)
+ {
+ $types = [];
+
+ foreach (explode('|', $type) as $part) {
+ $types[] = $this->resolve($part, $context);
+ }
+
+ return new Compound($types);
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Fqsen;
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Represents an array type as described in the PSR-5, the PHPDoc Standard.
+ *
+ * An array can be represented in two forms:
+ *
+ * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed'.
+ * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a
+ * type name.
+ */
+final class Array_ implements Type
+{
+ /** @var Type */
+ private $valueType;
+
+ /** @var Type */
+ private $keyType;
+
+ /**
+ * Initializes this representation of an array with the given Type or Fqsen.
+ *
+ * @param Type $valueType
+ * @param Type $keyType
+ */
+ public function __construct(Type $valueType = null, Type $keyType = null)
+ {
+ if ($keyType === null) {
+ $keyType = new Compound([ new String_(), new Integer() ]);
+ }
+ if ($valueType === null) {
+ $valueType = new Mixed();
+ }
+
+ $this->valueType = $valueType;
+ $this->keyType = $keyType;
+ }
+
+ /**
+ * Returns the type for the keys of this array.
+ *
+ * @return Type
+ */
+ public function getKeyType()
+ {
+ return $this->keyType;
+ }
+
+ /**
+ * Returns the value for the keys of this array.
+ *
+ * @return Type
+ */
+ public function getValueType()
+ {
+ return $this->valueType;
+ }
+
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if ($this->valueType instanceof Mixed) {
+ return 'array';
+ }
+
+ return $this->valueType . '[]';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing a Boolean type.
+ */
+final class Boolean implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'bool';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing a Callable type.
+ */
+final class Callable_ implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'callable';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing a Compound Type.
+ *
+ * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated
+ * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type
+ * may contain a value with any of the given types.
+ */
+final class Compound implements Type
+{
+ /** @var Type[] */
+ private $types = [];
+
+ /**
+ * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface.
+ *
+ * @param Type[] $types
+ */
+ public function __construct(array $types)
+ {
+ foreach ($types as $type) {
+ if (!$type instanceof Type) {
+ throw new \InvalidArgumentException('A compound type can only have other types as elements');
+ }
+ }
+
+ $this->types = $types;
+ }
+
+ /**
+ * Returns the type at the given index.
+ *
+ * @param integer $index
+ *
+ * @return Type|null
+ */
+ public function get($index)
+ {
+ if (!$this->has($index)) {
+ return null;
+ }
+
+ return $this->types[$index];
+ }
+
+ /**
+ * Tests if this compound type has a type with the given index.
+ *
+ * @param integer $index
+ *
+ * @return bool
+ */
+ public function has($index)
+ {
+ return isset($this->types[$index]);
+ }
+
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return implode('|', $this->types);
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+/**
+ * Provides information about the Context in which the DocBlock occurs that receives this context.
+ *
+ * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable
+ * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since
+ * you can provide a short form or make use of namespace aliases.
+ *
+ * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your
+ * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in
+ * which an associated class resides for its namespace and imports.
+ *
+ * @see ContextFactory::createFromClassReflector()
+ * @see ContextFactory::createForNamespace()
+ */
+final class Context
+{
+ /** @var string The current namespace. */
+ private $namespace = '';
+
+ /** @var array List of namespace aliases => Fully Qualified Namespace. */
+ private $namespaceAliases = [];
+
+ /**
+ * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN)
+ * format (without a preceding `\`).
+ *
+ * @param string $namespace The namespace where this DocBlock resides in.
+ * @param array $namespaceAliases List of namespace aliases => Fully Qualified Namespace.
+ */
+ public function __construct($namespace, array $namespaceAliases = [])
+ {
+ $this->namespace = ('global' !== $namespace && 'default' !== $namespace)
+ ? trim((string)$namespace, '\\')
+ : '';
+
+ foreach ($namespaceAliases as $alias => $fqnn) {
+ if ($fqnn[0] === '\\') {
+ $fqnn = substr($fqnn, 1);
+ }
+ if ($fqnn[strlen($fqnn) - 1] === '\\') {
+ $fqnn = substr($fqnn, 0, -1);
+ }
+
+ $namespaceAliases[$alias] = $fqnn;
+ }
+
+ $this->namespaceAliases = $namespaceAliases;
+ }
+
+ /**
+ * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in.
+ *
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return $this->namespace;
+ }
+
+ /**
+ * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent
+ * the alias for the imported Namespace.
+ *
+ * @return string[]
+ */
+ public function getNamespaceAliases()
+ {
+ return $this->namespaceAliases;
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+/**
+ * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor.
+ *
+ * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to
+ * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to
+ * Fully Qualified names.
+ *
+ * @see Context for more information.
+ */
+final class ContextFactory
+{
+ /** The literal used at the end of a use statement. */
+ const T_LITERAL_END_OF_USE = ';';
+
+ /** The literal used between sets of use statements */
+ const T_LITERAL_USE_SEPARATOR = ',';
+
+ /**
+ * Build a Context given a Class Reflection.
+ *
+ * @param \ReflectionClass $reflector
+ *
+ * @see Context for more information on Contexts.
+ *
+ * @return Context
+ */
+ public function createFromReflector(\Reflector $reflector)
+ {
+ if (method_exists($reflector, 'getDeclaringClass')) {
+ $reflector = $reflector->getDeclaringClass();
+ }
+
+ $fileName = $reflector->getFileName();
+ $namespace = $reflector->getNamespaceName();
+
+ if (file_exists($fileName)) {
+ return $this->createForNamespace($namespace, file_get_contents($fileName));
+ }
+
+ return new Context($namespace, []);
+ }
+
+ /**
+ * Build a Context for a namespace in the provided file contents.
+ *
+ * @param string $namespace It does not matter if a `\` precedes the namespace name, this method first normalizes.
+ * @param string $fileContents the file's contents to retrieve the aliases from with the given namespace.
+ *
+ * @see Context for more information on Contexts.
+ *
+ * @return Context
+ */
+ public function createForNamespace($namespace, $fileContents)
+ {
+ $namespace = trim($namespace, '\\');
+ $useStatements = [];
+ $currentNamespace = '';
+ $tokens = new \ArrayIterator(token_get_all($fileContents));
+
+ while ($tokens->valid()) {
+ switch ($tokens->current()[0]) {
+ case T_NAMESPACE:
+ $currentNamespace = $this->parseNamespace($tokens);
+ break;
+ case T_CLASS:
+ // Fast-forward the iterator through the class so that any
+ // T_USE tokens found within are skipped - these are not
+ // valid namespace use statements so should be ignored.
+ $braceLevel = 0;
+ $firstBraceFound = false;
+ while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) {
+ if ($tokens->current() === '{'
+ || $tokens->current()[0] === T_CURLY_OPEN
+ || $tokens->current()[0] === T_DOLLAR_OPEN_CURLY_BRACES) {
+ if (!$firstBraceFound) {
+ $firstBraceFound = true;
+ }
+ $braceLevel++;
+ }
+
+ if ($tokens->current() === '}') {
+ $braceLevel--;
+ }
+ $tokens->next();
+ }
+ break;
+ case T_USE:
+ if ($currentNamespace === $namespace) {
+ $useStatements = array_merge($useStatements, $this->parseUseStatement($tokens));
+ }
+ break;
+ }
+ $tokens->next();
+ }
+
+ return new Context($namespace, $useStatements);
+ }
+
+ /**
+ * Deduce the name from tokens when we are at the T_NAMESPACE token.
+ *
+ * @param \ArrayIterator $tokens
+ *
+ * @return string
+ */
+ private function parseNamespace(\ArrayIterator $tokens)
+ {
+ // skip to the first string or namespace separator
+ $this->skipToNextStringOrNamespaceSeparator($tokens);
+
+ $name = '';
+ while ($tokens->valid() && ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR)
+ ) {
+ $name .= $tokens->current()[1];
+ $tokens->next();
+ }
+
+ return $name;
+ }
+
+ /**
+ * Deduce the names of all imports when we are at the T_USE token.
+ *
+ * @param \ArrayIterator $tokens
+ *
+ * @return string[]
+ */
+ private function parseUseStatement(\ArrayIterator $tokens)
+ {
+ $uses = [];
+ $continue = true;
+
+ while ($continue) {
+ $this->skipToNextStringOrNamespaceSeparator($tokens);
+
+ list($alias, $fqnn) = $this->extractUseStatement($tokens);
+ $uses[$alias] = $fqnn;
+ if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) {
+ $continue = false;
+ }
+ }
+
+ return $uses;
+ }
+
+ /**
+ * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.
+ *
+ * @param \ArrayIterator $tokens
+ *
+ * @return void
+ */
+ private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens)
+ {
+ while ($tokens->valid() && ($tokens->current()[0] !== T_STRING) && ($tokens->current()[0] !== T_NS_SEPARATOR)) {
+ $tokens->next();
+ }
+ }
+
+ /**
+ * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of
+ * a USE statement yet.
+ *
+ * @param \ArrayIterator $tokens
+ *
+ * @return string
+ */
+ private function extractUseStatement(\ArrayIterator $tokens)
+ {
+ $result = [''];
+ while ($tokens->valid()
+ && ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR)
+ && ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE)
+ ) {
+ if ($tokens->current()[0] === T_AS) {
+ $result[] = '';
+ }
+ if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) {
+ $result[count($result) - 1] .= $tokens->current()[1];
+ }
+ $tokens->next();
+ }
+
+ if (count($result) == 1) {
+ $backslashPos = strrpos($result[0], '\\');
+
+ if (false !== $backslashPos) {
+ $result[] = substr($result[0], $backslashPos + 1);
+ } else {
+ $result[] = $result[0];
+ }
+ }
+
+ return array_reverse($result);
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing a Float.
+ */
+final class Float_ implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'float';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+final class Integer implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'int';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing an unknown, or mixed, type.
+ */
+final class Mixed implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'mixed';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing a null value or type.
+ */
+final class Null_ implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'null';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Fqsen;
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing an object.
+ *
+ * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN,
+ * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects
+ * in general.
+ */
+final class Object_ implements Type
+{
+ /** @var Fqsen|null */
+ private $fqsen;
+
+ /**
+ * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'.
+ *
+ * @param Fqsen $fqsen
+ */
+ public function __construct(Fqsen $fqsen = null)
+ {
+ if (strpos((string)$fqsen, '::') !== false || strpos((string)$fqsen, '()') !== false) {
+ throw new \InvalidArgumentException(
+ 'Object types can only refer to a class, interface or trait but a method, function, constant or '
+ . 'property was received: ' . (string)$fqsen
+ );
+ }
+
+ $this->fqsen = $fqsen;
+ }
+
+ /**
+ * Returns the FQSEN associated with this object.
+ *
+ * @return Fqsen|null
+ */
+ public function getFqsen()
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if ($this->fqsen) {
+ return (string)$this->fqsen;
+ }
+
+ return 'object';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing the 'resource' Type.
+ */
+final class Resource implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'resource';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean.
+ */
+final class Scalar implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'scalar';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing the 'self' type.
+ *
+ * Self, as a Type, represents the class in which the associated element was defined.
+ */
+final class Self_ implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'self';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing the 'static' type.
+ *
+ * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does
+ * not take inheritance into account but static means that the return type is always that of the class of the called
+ * element.
+ *
+ * See the documentation on late static binding in the PHP Documentation for more information on the difference between
+ * static and self.
+ */
+final class Static_ implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'static';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing the type 'string'.
+ */
+final class String_ implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'string';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing the '$this' pseudo-type.
+ *
+ * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is
+ * commonly used when documenting fluent interfaces since it represents that the same object is returned.
+ */
+final class This implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return '$this';
+ }
+}
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing the pseudo-type 'void'.
+ *
+ * Void is generally only used when working with return types as it signifies that the method intentionally does not
+ * return any value.
+ */
+final class Void_ implements Type
+{
+ /**
+ * Returns a rendered output of the Type as it would be used in a DocBlock.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'void';
+ }
+}
+Copyright (c) 2013 Konstantin Kudryashov <ever.zet@gmail.com>
+ Marcello Duarte <marcello.duarte@gmail.com>
+
+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.
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy;
+
+use Prophecy\Argument\Token;
+
+/**
+ * Argument tokens shortcuts.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Argument
+{
+ /**
+ * Checks that argument is exact value or object.
+ *
+ * @param mixed $value
+ *
+ * @return Token\ExactValueToken
+ */
+ public static function exact($value)
+ {
+ return new Token\ExactValueToken($value);
+ }
+
+ /**
+ * Checks that argument is of specific type or instance of specific class.
+ *
+ * @param string $type Type name (`integer`, `string`) or full class name
+ *
+ * @return Token\TypeToken
+ */
+ public static function type($type)
+ {
+ return new Token\TypeToken($type);
+ }
+
+ /**
+ * Checks that argument object has specific state.
+ *
+ * @param string $methodName
+ * @param mixed $value
+ *
+ * @return Token\ObjectStateToken
+ */
+ public static function which($methodName, $value)
+ {
+ return new Token\ObjectStateToken($methodName, $value);
+ }
+
+ /**
+ * Checks that argument matches provided callback.
+ *
+ * @param callable $callback
+ *
+ * @return Token\CallbackToken
+ */
+ public static function that($callback)
+ {
+ return new Token\CallbackToken($callback);
+ }
+
+ /**
+ * Matches any single value.
+ *
+ * @return Token\AnyValueToken
+ */
+ public static function any()
+ {
+ return new Token\AnyValueToken;
+ }
+
+ /**
+ * Matches all values to the rest of the signature.
+ *
+ * @return Token\AnyValuesToken
+ */
+ public static function cetera()
+ {
+ return new Token\AnyValuesToken;
+ }
+
+ /**
+ * Checks that argument matches all tokens
+ *
+ * @param mixed ... a list of tokens
+ *
+ * @return Token\LogicalAndToken
+ */
+ public static function allOf()
+ {
+ return new Token\LogicalAndToken(func_get_args());
+ }
+
+ /**
+ * Checks that argument array or countable object has exact number of elements.
+ *
+ * @param integer $value array elements count
+ *
+ * @return Token\ArrayCountToken
+ */
+ public static function size($value)
+ {
+ return new Token\ArrayCountToken($value);
+ }
+
+ /**
+ * Checks that argument array contains (key, value) pair
+ *
+ * @param mixed $key exact value or token
+ * @param mixed $value exact value or token
+ *
+ * @return Token\ArrayEntryToken
+ */
+ public static function withEntry($key, $value)
+ {
+ return new Token\ArrayEntryToken($key, $value);
+ }
+
+ /**
+ * Checks that arguments array entries all match value
+ *
+ * @param mixed $value
+ *
+ * @return Token\ArrayEveryEntryToken
+ */
+ public static function withEveryEntry($value)
+ {
+ return new Token\ArrayEveryEntryToken($value);
+ }
+
+ /**
+ * Checks that argument array contains value
+ *
+ * @param mixed $value
+ *
+ * @return Token\ArrayEntryToken
+ */
+ public static function containing($value)
+ {
+ return new Token\ArrayEntryToken(self::any(), $value);
+ }
+
+ /**
+ * Checks that argument array has key
+ *
+ * @param mixed $key exact value or token
+ *
+ * @return Token\ArrayEntryToken
+ */
+ public static function withKey($key)
+ {
+ return new Token\ArrayEntryToken($key, self::any());
+ }
+
+ /**
+ * Checks that argument does not match the value|token.
+ *
+ * @param mixed $value either exact value or argument token
+ *
+ * @return Token\LogicalNotToken
+ */
+ public static function not($value)
+ {
+ return new Token\LogicalNotToken($value);
+ }
+
+ /**
+ * @param string $value
+ *
+ * @return Token\StringContainsToken
+ */
+ public static function containingString($value)
+ {
+ return new Token\StringContainsToken($value);
+ }
+
+ /**
+ * Checks that argument is identical value.
+ *
+ * @param mixed $value
+ *
+ * @return Token\IdenticalValueToken
+ */
+ public static function is($value)
+ {
+ return new Token\IdenticalValueToken($value);
+ }
+
+ /**
+ * Check that argument is same value when rounding to the
+ * given precision.
+ *
+ * @param float $value
+ * @param float $precision
+ *
+ * @return Token\ApproximateValueToken
+ */
+ public static function approximate($value, $precision = 0)
+ {
+ return new Token\ApproximateValueToken($value, $precision);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument;
+
+/**
+ * Arguments wildcarding.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ArgumentsWildcard
+{
+ /**
+ * @var Token\TokenInterface[]
+ */
+ private $tokens = array();
+ private $string;
+
+ /**
+ * Initializes wildcard.
+ *
+ * @param array $arguments Array of argument tokens or values
+ */
+ public function __construct(array $arguments)
+ {
+ foreach ($arguments as $argument) {
+ if (!$argument instanceof Token\TokenInterface) {
+ $argument = new Token\ExactValueToken($argument);
+ }
+
+ $this->tokens[] = $argument;
+ }
+ }
+
+ /**
+ * Calculates wildcard match score for provided arguments.
+ *
+ * @param array $arguments
+ *
+ * @return false|int False OR integer score (higher - better)
+ */
+ public function scoreArguments(array $arguments)
+ {
+ if (0 == count($arguments) && 0 == count($this->tokens)) {
+ return 1;
+ }
+
+ $arguments = array_values($arguments);
+ $totalScore = 0;
+ foreach ($this->tokens as $i => $token) {
+ $argument = isset($arguments[$i]) ? $arguments[$i] : null;
+ if (1 >= $score = $token->scoreArgument($argument)) {
+ return false;
+ }
+
+ $totalScore += $score;
+
+ if (true === $token->isLast()) {
+ return $totalScore;
+ }
+ }
+
+ if (count($arguments) > count($this->tokens)) {
+ return false;
+ }
+
+ return $totalScore;
+ }
+
+ /**
+ * Returns string representation for wildcard.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if (null === $this->string) {
+ $this->string = implode(', ', array_map(function ($token) {
+ return (string) $token;
+ }, $this->tokens));
+ }
+
+ return $this->string;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTokens()
+ {
+ return $this->tokens;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Any single value token.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class AnyValueToken implements TokenInterface
+{
+ /**
+ * Always scores 3 for any argument.
+ *
+ * @param $argument
+ *
+ * @return int
+ */
+ public function scoreArgument($argument)
+ {
+ return 3;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return '*';
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Any values token.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class AnyValuesToken implements TokenInterface
+{
+ /**
+ * Always scores 2 for any argument.
+ *
+ * @param $argument
+ *
+ * @return int
+ */
+ public function scoreArgument($argument)
+ {
+ return 2;
+ }
+
+ /**
+ * Returns true to stop wildcard from processing other tokens.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return true;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return '* [, ...]';
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Approximate value token
+ *
+ * @author Daniel Leech <daniel@dantleech.com>
+ */
+class ApproximateValueToken implements TokenInterface
+{
+ private $value;
+ private $precision;
+
+ public function __construct($value, $precision = 0)
+ {
+ $this->value = $value;
+ $this->precision = $precision;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function scoreArgument($argument)
+ {
+ return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('≅%s', round($this->value, $this->precision));
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Array elements count token.
+ *
+ * @author Boris Mikhaylov <kaguxmail@gmail.com>
+ */
+
+class ArrayCountToken implements TokenInterface
+{
+ private $count;
+
+ /**
+ * @param integer $value
+ */
+ public function __construct($value)
+ {
+ $this->count = $value;
+ }
+
+ /**
+ * Scores 6 when argument has preset number of elements.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return boolean
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('count(%s)', $this->count);
+ }
+
+ /**
+ * Returns true if object is either array or instance of \Countable
+ *
+ * @param $argument
+ * @return bool
+ */
+ private function isCountable($argument)
+ {
+ return (is_array($argument) || $argument instanceof \Countable);
+ }
+
+ /**
+ * Returns true if $argument has expected number of elements
+ *
+ * @param array|\Countable $argument
+ *
+ * @return bool
+ */
+ private function hasProperCount($argument)
+ {
+ return $this->count === count($argument);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+use Prophecy\Exception\InvalidArgumentException;
+
+/**
+ * Array entry token.
+ *
+ * @author Boris Mikhaylov <kaguxmail@gmail.com>
+ */
+class ArrayEntryToken implements TokenInterface
+{
+ /** @var \Prophecy\Argument\Token\TokenInterface */
+ private $key;
+ /** @var \Prophecy\Argument\Token\TokenInterface */
+ private $value;
+
+ /**
+ * @param mixed $key exact value or token
+ * @param mixed $value exact value or token
+ */
+ public function __construct($key, $value)
+ {
+ $this->key = $this->wrapIntoExactValueToken($key);
+ $this->value = $this->wrapIntoExactValueToken($value);
+ }
+
+ /**
+ * Scores half of combined scores from key and value tokens for same entry. Capped at 8.
+ * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken.
+ *
+ * @param array|\ArrayAccess|\Traversable $argument
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ if ($argument instanceof \Traversable) {
+ $argument = iterator_to_array($argument);
+ }
+
+ if ($argument instanceof \ArrayAccess) {
+ $argument = $this->convertArrayAccessToEntry($argument);
+ }
+
+ if (!is_array($argument) || empty($argument)) {
+ return false;
+ }
+
+ $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument));
+ $valueScores = array_map(array($this->value,'scoreArgument'), $argument);
+ $scoreEntry = function ($value, $key) {
+ return $value && $key ? min(8, ($key + $value) / 2) : false;
+ };
+
+ return max(array_map($scoreEntry, $valueScores, $keyScores));
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return boolean
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('[..., %s => %s, ...]', $this->key, $this->value);
+ }
+
+ /**
+ * Returns key
+ *
+ * @return TokenInterface
+ */
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * Returns value
+ *
+ * @return TokenInterface
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Wraps non token $value into ExactValueToken
+ *
+ * @param $value
+ * @return TokenInterface
+ */
+ private function wrapIntoExactValueToken($value)
+ {
+ return $value instanceof TokenInterface ? $value : new ExactValueToken($value);
+ }
+
+ /**
+ * Converts instance of \ArrayAccess to key => value array entry
+ *
+ * @param \ArrayAccess $object
+ *
+ * @return array|null
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ private function convertArrayAccessToEntry(\ArrayAccess $object)
+ {
+ if (!$this->key instanceof ExactValueToken) {
+ throw new InvalidArgumentException(sprintf(
+ 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL.
+ 'But you used `%s`.',
+ $this->key
+ ));
+ }
+
+ $key = $this->key->getValue();
+
+ return $object->offsetExists($key) ? array($key => $object[$key]) : array();
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Array every entry token.
+ *
+ * @author Adrien Brault <adrien.brault@gmail.com>
+ */
+class ArrayEveryEntryToken implements TokenInterface
+{
+ /**
+ * @var TokenInterface
+ */
+ private $value;
+
+ /**
+ * @param mixed $value exact value or token
+ */
+ public function __construct($value)
+ {
+ if (!$value instanceof TokenInterface) {
+ $value = new ExactValueToken($value);
+ }
+
+ $this->value = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function scoreArgument($argument)
+ {
+ if (!$argument instanceof \Traversable && !is_array($argument)) {
+ return false;
+ }
+
+ $scores = array();
+ foreach ($argument as $key => $argumentEntry) {
+ $scores[] = $this->value->scoreArgument($argumentEntry);
+ }
+
+ if (empty($scores) || in_array(false, $scores, true)) {
+ return false;
+ }
+
+ return array_sum($scores) / count($scores);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __toString()
+ {
+ return sprintf('[%s, ..., %s]', $this->value, $this->value);
+ }
+
+ /**
+ * @return TokenInterface
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+use Prophecy\Exception\InvalidArgumentException;
+
+/**
+ * Callback-verified token.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CallbackToken implements TokenInterface
+{
+ private $callback;
+
+ /**
+ * Initializes token.
+ *
+ * @param callable $callback
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function __construct($callback)
+ {
+ if (!is_callable($callback)) {
+ throw new InvalidArgumentException(sprintf(
+ 'Callable expected as an argument to CallbackToken, but got %s.',
+ gettype($callback)
+ ));
+ }
+
+ $this->callback = $callback;
+ }
+
+ /**
+ * Scores 7 if callback returns true, false otherwise.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ return call_user_func($this->callback, $argument) ? 7 : false;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return 'callback()';
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+use SebastianBergmann\Comparator\ComparisonFailure;
+use Prophecy\Comparator\Factory as ComparatorFactory;
+use Prophecy\Util\StringUtil;
+
+/**
+ * Exact value token.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ExactValueToken implements TokenInterface
+{
+ private $value;
+ private $string;
+ private $util;
+ private $comparatorFactory;
+
+ /**
+ * Initializes token.
+ *
+ * @param mixed $value
+ * @param StringUtil $util
+ * @param ComparatorFactory $comparatorFactory
+ */
+ public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null)
+ {
+ $this->value = $value;
+ $this->util = $util ?: new StringUtil();
+
+ $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance();
+ }
+
+ /**
+ * Scores 10 if argument matches preset value.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ if (is_object($argument) && is_object($this->value)) {
+ $comparator = $this->comparatorFactory->getComparatorFor(
+ $argument, $this->value
+ );
+
+ try {
+ $comparator->assertEquals($argument, $this->value);
+ return 10;
+ } catch (ComparisonFailure $failure) {}
+ }
+
+ // If either one is an object it should be castable to a string
+ if (is_object($argument) xor is_object($this->value)) {
+ if (is_object($argument) && !method_exists($argument, '__toString')) {
+ return false;
+ }
+
+ if (is_object($this->value) && !method_exists($this->value, '__toString')) {
+ return false;
+ }
+ } elseif (is_numeric($argument) && is_numeric($this->value)) {
+ // noop
+ } elseif (gettype($argument) !== gettype($this->value)) {
+ return false;
+ }
+
+ return $argument == $this->value ? 10 : false;
+ }
+
+ /**
+ * Returns preset value against which token checks arguments.
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if (null === $this->string) {
+ $this->string = sprintf('exact(%s)', $this->util->stringify($this->value));
+ }
+
+ return $this->string;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+use Prophecy\Util\StringUtil;
+
+/**
+ * Identical value token.
+ *
+ * @author Florian Voutzinos <florian@voutzinos.com>
+ */
+class IdenticalValueToken implements TokenInterface
+{
+ private $value;
+ private $string;
+ private $util;
+
+ /**
+ * Initializes token.
+ *
+ * @param mixed $value
+ * @param StringUtil $util
+ */
+ public function __construct($value, StringUtil $util = null)
+ {
+ $this->value = $value;
+ $this->util = $util ?: new StringUtil();
+ }
+
+ /**
+ * Scores 11 if argument matches preset value.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ return $argument === $this->value ? 11 : false;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if (null === $this->string) {
+ $this->string = sprintf('identical(%s)', $this->util->stringify($this->value));
+ }
+
+ return $this->string;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Logical AND token.
+ *
+ * @author Boris Mikhaylov <kaguxmail@gmail.com>
+ */
+class LogicalAndToken implements TokenInterface
+{
+ private $tokens = array();
+
+ /**
+ * @param array $arguments exact values or tokens
+ */
+ public function __construct(array $arguments)
+ {
+ foreach ($arguments as $argument) {
+ if (!$argument instanceof TokenInterface) {
+ $argument = new ExactValueToken($argument);
+ }
+ $this->tokens[] = $argument;
+ }
+ }
+
+ /**
+ * Scores maximum score from scores returned by tokens for this argument if all of them score.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ if (0 === count($this->tokens)) {
+ return false;
+ }
+
+ $maxScore = 0;
+ foreach ($this->tokens as $token) {
+ $score = $token->scoreArgument($argument);
+ if (false === $score) {
+ return false;
+ }
+ $maxScore = max($score, $maxScore);
+ }
+
+ return $maxScore;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return boolean
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('bool(%s)', implode(' AND ', $this->tokens));
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Logical NOT token.
+ *
+ * @author Boris Mikhaylov <kaguxmail@gmail.com>
+ */
+class LogicalNotToken implements TokenInterface
+{
+ /** @var \Prophecy\Argument\Token\TokenInterface */
+ private $token;
+
+ /**
+ * @param mixed $value exact value or token
+ */
+ public function __construct($value)
+ {
+ $this->token = $value instanceof TokenInterface? $value : new ExactValueToken($value);
+ }
+
+ /**
+ * Scores 4 when preset token does not match the argument.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ return false === $this->token->scoreArgument($argument) ? 4 : false;
+ }
+
+ /**
+ * Returns true if preset token is last.
+ *
+ * @return bool|int
+ */
+ public function isLast()
+ {
+ return $this->token->isLast();
+ }
+
+ /**
+ * Returns originating token.
+ *
+ * @return TokenInterface
+ */
+ public function getOriginatingToken()
+ {
+ return $this->token;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('not(%s)', $this->token);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+use SebastianBergmann\Comparator\ComparisonFailure;
+use Prophecy\Comparator\Factory as ComparatorFactory;
+use Prophecy\Util\StringUtil;
+
+/**
+ * Object state-checker token.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ObjectStateToken implements TokenInterface
+{
+ private $name;
+ private $value;
+ private $util;
+ private $comparatorFactory;
+
+ /**
+ * Initializes token.
+ *
+ * @param string $methodName
+ * @param mixed $value Expected return value
+ * @param null|StringUtil $util
+ * @param ComparatorFactory $comparatorFactory
+ */
+ public function __construct(
+ $methodName,
+ $value,
+ StringUtil $util = null,
+ ComparatorFactory $comparatorFactory = null
+ ) {
+ $this->name = $methodName;
+ $this->value = $value;
+ $this->util = $util ?: new StringUtil;
+
+ $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance();
+ }
+
+ /**
+ * Scores 8 if argument is an object, which method returns expected value.
+ *
+ * @param mixed $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ if (is_object($argument) && method_exists($argument, $this->name)) {
+ $actual = call_user_func(array($argument, $this->name));
+
+ $comparator = $this->comparatorFactory->getComparatorFor(
+ $this->value, $actual
+ );
+
+ try {
+ $comparator->assertEquals($this->value, $actual);
+ return 8;
+ } catch (ComparisonFailure $failure) {
+ return false;
+ }
+ }
+
+ if (is_object($argument) && property_exists($argument, $this->name)) {
+ return $argument->{$this->name} === $this->value ? 8 : false;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('state(%s(), %s)',
+ $this->name,
+ $this->util->stringify($this->value)
+ );
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * String contains token.
+ *
+ * @author Peter Mitchell <pete@peterjmit.com>
+ */
+class StringContainsToken implements TokenInterface
+{
+ private $value;
+
+ /**
+ * Initializes token.
+ *
+ * @param string $value
+ */
+ public function __construct($value)
+ {
+ $this->value = $value;
+ }
+
+ public function scoreArgument($argument)
+ {
+ return strpos($argument, $this->value) !== false ? 6 : false;
+ }
+
+ /**
+ * Returns preset value against which token checks arguments.
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('contains("%s")', $this->value);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+/**
+ * Argument token interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface TokenInterface
+{
+ /**
+ * Calculates token match score for provided argument.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument);
+
+ /**
+ * Returns true if this token prevents check of other tokens (is last one).
+ *
+ * @return bool|int
+ */
+ public function isLast();
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString();
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Argument\Token;
+
+use Prophecy\Exception\InvalidArgumentException;
+
+/**
+ * Value type token.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class TypeToken implements TokenInterface
+{
+ private $type;
+
+ /**
+ * @param string $type
+ */
+ public function __construct($type)
+ {
+ $checker = "is_{$type}";
+ if (!function_exists($checker) && !interface_exists($type) && !class_exists($type)) {
+ throw new InvalidArgumentException(sprintf(
+ 'Type or class name expected as an argument to TypeToken, but got %s.', $type
+ ));
+ }
+
+ $this->type = $type;
+ }
+
+ /**
+ * Scores 5 if argument has the same type this token was constructed with.
+ *
+ * @param $argument
+ *
+ * @return bool|int
+ */
+ public function scoreArgument($argument)
+ {
+ $checker = "is_{$this->type}";
+ if (function_exists($checker)) {
+ return call_user_func($checker, $argument) ? 5 : false;
+ }
+
+ return $argument instanceof $this->type ? 5 : false;
+ }
+
+ /**
+ * Returns false.
+ *
+ * @return bool
+ */
+ public function isLast()
+ {
+ return false;
+ }
+
+ /**
+ * Returns string representation for token.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return sprintf('type(%s)', $this->type);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Call;
+
+use Exception;
+
+/**
+ * Call object.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Call
+{
+ private $methodName;
+ private $arguments;
+ private $returnValue;
+ private $exception;
+ private $file;
+ private $line;
+
+ /**
+ * Initializes call.
+ *
+ * @param string $methodName
+ * @param array $arguments
+ * @param mixed $returnValue
+ * @param Exception $exception
+ * @param null|string $file
+ * @param null|int $line
+ */
+ public function __construct($methodName, array $arguments, $returnValue,
+ Exception $exception = null, $file, $line)
+ {
+ $this->methodName = $methodName;
+ $this->arguments = $arguments;
+ $this->returnValue = $returnValue;
+ $this->exception = $exception;
+
+ if ($file) {
+ $this->file = $file;
+ $this->line = intval($line);
+ }
+ }
+
+ /**
+ * Returns called method name.
+ *
+ * @return string
+ */
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+
+ /**
+ * Returns called method arguments.
+ *
+ * @return array
+ */
+ public function getArguments()
+ {
+ return $this->arguments;
+ }
+
+ /**
+ * Returns called method return value.
+ *
+ * @return null|mixed
+ */
+ public function getReturnValue()
+ {
+ return $this->returnValue;
+ }
+
+ /**
+ * Returns exception that call thrown.
+ *
+ * @return null|Exception
+ */
+ public function getException()
+ {
+ return $this->exception;
+ }
+
+ /**
+ * Returns callee filename.
+ *
+ * @return string
+ */
+ public function getFile()
+ {
+ return $this->file;
+ }
+
+ /**
+ * Returns callee line number.
+ *
+ * @return int
+ */
+ public function getLine()
+ {
+ return $this->line;
+ }
+
+ /**
+ * Returns short notation for callee place.
+ *
+ * @return string
+ */
+ public function getCallPlace()
+ {
+ if (null === $this->file) {
+ return 'unknown';
+ }
+
+ return sprintf('%s:%d', $this->file, $this->line);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Call;
+
+use Prophecy\Exception\Prophecy\MethodProphecyException;
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Argument\ArgumentsWildcard;
+use Prophecy\Util\StringUtil;
+use Prophecy\Exception\Call\UnexpectedCallException;
+
+/**
+ * Calls receiver & manager.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CallCenter
+{
+ private $util;
+
+ /**
+ * @var Call[]
+ */
+ private $recordedCalls = array();
+
+ /**
+ * Initializes call center.
+ *
+ * @param StringUtil $util
+ */
+ public function __construct(StringUtil $util = null)
+ {
+ $this->util = $util ?: new StringUtil;
+ }
+
+ /**
+ * Makes and records specific method call for object prophecy.
+ *
+ * @param ObjectProphecy $prophecy
+ * @param string $methodName
+ * @param array $arguments
+ *
+ * @return mixed Returns null if no promise for prophecy found or promise return value.
+ *
+ * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found
+ */
+ public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments)
+ {
+ // For efficiency exclude 'args' from the generated backtrace
+ if (PHP_VERSION_ID >= 50400) {
+ // Limit backtrace to last 3 calls as we don't use the rest
+ // Limit argument was introduced in PHP 5.4.0
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
+ } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) {
+ // DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+ } else {
+ $backtrace = debug_backtrace();
+ }
+
+ $file = $line = null;
+ if (isset($backtrace[2]) && isset($backtrace[2]['file'])) {
+ $file = $backtrace[2]['file'];
+ $line = $backtrace[2]['line'];
+ }
+
+ // If no method prophecies defined, then it's a dummy, so we'll just return null
+ if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) {
+ $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line);
+
+ return null;
+ }
+
+ // There are method prophecies, so it's a fake/stub. Searching prophecy for this call
+ $matches = array();
+ foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) {
+ if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) {
+ $matches[] = array($score, $methodProphecy);
+ }
+ }
+
+ // If fake/stub doesn't have method prophecy for this call - throw exception
+ if (!count($matches)) {
+ throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments);
+ }
+
+ // Sort matches by their score value
+ @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; });
+
+ // If Highest rated method prophecy has a promise - execute it or return null instead
+ $methodProphecy = $matches[0][1];
+ $returnValue = null;
+ $exception = null;
+ if ($promise = $methodProphecy->getPromise()) {
+ try {
+ $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy);
+ } catch (\Exception $e) {
+ $exception = $e;
+ }
+ }
+
+ if ($methodProphecy->hasReturnVoid() && $returnValue !== null) {
+ throw new MethodProphecyException(
+ "The method \"$methodName\" has a void return type, but the promise returned a value",
+ $methodProphecy
+ );
+ }
+
+ $this->recordedCalls[] = new Call(
+ $methodName, $arguments, $returnValue, $exception, $file, $line
+ );
+
+ if (null !== $exception) {
+ throw $exception;
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Searches for calls by method name & arguments wildcard.
+ *
+ * @param string $methodName
+ * @param ArgumentsWildcard $wildcard
+ *
+ * @return Call[]
+ */
+ public function findCalls($methodName, ArgumentsWildcard $wildcard)
+ {
+ return array_values(
+ array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) {
+ return $methodName === $call->getMethodName()
+ && 0 < $wildcard->scoreArguments($call->getArguments())
+ ;
+ })
+ );
+ }
+
+ private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName,
+ array $arguments)
+ {
+ $classname = get_class($prophecy->reveal());
+ $argstring = implode(', ', array_map(array($this->util, 'stringify'), $arguments));
+ $expected = implode("\n", array_map(function (MethodProphecy $methodProphecy) {
+ return sprintf(' - %s(%s)',
+ $methodProphecy->getMethodName(),
+ $methodProphecy->getArgumentsWildcard()
+ );
+ }, call_user_func_array('array_merge', $prophecy->getMethodProphecies())));
+
+ return new UnexpectedCallException(
+ sprintf(
+ "Method call:\n".
+ " - %s(%s)\n".
+ "on %s was not expected, expected calls were:\n%s",
+
+ $methodName, $argstring, $classname, $expected
+ ),
+ $prophecy, $methodName, $arguments
+ );
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Comparator;
+
+use SebastianBergmann\Comparator\Comparator;
+use SebastianBergmann\Comparator\ComparisonFailure;
+
+/**
+ * Closure comparator.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+final class ClosureComparator extends Comparator
+{
+ public function accepts($expected, $actual)
+ {
+ return is_object($expected) && $expected instanceof \Closure
+ && is_object($actual) && $actual instanceof \Closure;
+ }
+
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ // we don't need a diff
+ '',
+ '',
+ false,
+ 'all closures are born different'
+ );
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Comparator;
+
+use SebastianBergmann\Comparator\Factory as BaseFactory;
+
+/**
+ * Prophecy comparator factory.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+final class Factory extends BaseFactory
+{
+ /**
+ * @var Factory
+ */
+ private static $instance;
+
+ public function __construct()
+ {
+ parent::__construct();
+
+ $this->register(new ClosureComparator());
+ $this->register(new ProphecyComparator());
+ }
+
+ /**
+ * @return Factory
+ */
+ public static function getInstance()
+ {
+ if (self::$instance === null) {
+ self::$instance = new Factory;
+ }
+
+ return self::$instance;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Comparator;
+
+use Prophecy\Prophecy\ProphecyInterface;
+use SebastianBergmann\Comparator\ObjectComparator;
+
+class ProphecyComparator extends ObjectComparator
+{
+ public function accepts($expected, $actual)
+ {
+ return is_object($expected) && is_object($actual) && $actual instanceof ProphecyInterface;
+ }
+
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
+ {
+ parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler;
+
+use ReflectionClass;
+
+/**
+ * Cached class doubler.
+ * Prevents mirroring/creation of the same structure twice.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CachedDoubler extends Doubler
+{
+ private $classes = array();
+
+ /**
+ * {@inheritdoc}
+ */
+ public function registerClassPatch(ClassPatch\ClassPatchInterface $patch)
+ {
+ $this->classes[] = array();
+
+ parent::registerClassPatch($patch);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createDoubleClass(ReflectionClass $class = null, array $interfaces)
+ {
+ $classId = $this->generateClassId($class, $interfaces);
+ if (isset($this->classes[$classId])) {
+ return $this->classes[$classId];
+ }
+
+ return $this->classes[$classId] = parent::createDoubleClass($class, $interfaces);
+ }
+
+ /**
+ * @param ReflectionClass $class
+ * @param ReflectionClass[] $interfaces
+ *
+ * @return string
+ */
+ private function generateClassId(ReflectionClass $class = null, array $interfaces)
+ {
+ $parts = array();
+ if (null !== $class) {
+ $parts[] = $class->getName();
+ }
+ foreach ($interfaces as $interface) {
+ $parts[] = $interface->getName();
+ }
+ sort($parts);
+
+ return md5(implode('', $parts));
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+
+/**
+ * Class patch interface.
+ * Class patches extend doubles functionality or help
+ * Prophecy to avoid some internal PHP bugs.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface ClassPatchInterface
+{
+ /**
+ * Checks if patch supports specific class node.
+ *
+ * @param ClassNode $node
+ *
+ * @return bool
+ */
+ public function supports(ClassNode $node);
+
+ /**
+ * Applies patch to the specific class node.
+ *
+ * @param ClassNode $node
+ * @return void
+ */
+ public function apply(ClassNode $node);
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority();
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+use Prophecy\Doubler\Generator\Node\MethodNode;
+
+/**
+ * Disable constructor.
+ * Makes all constructor arguments optional.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class DisableConstructorPatch implements ClassPatchInterface
+{
+ /**
+ * Checks if class has `__construct` method.
+ *
+ * @param ClassNode $node
+ *
+ * @return bool
+ */
+ public function supports(ClassNode $node)
+ {
+ return true;
+ }
+
+ /**
+ * Makes all class constructor arguments optional.
+ *
+ * @param ClassNode $node
+ */
+ public function apply(ClassNode $node)
+ {
+ if (!$node->hasMethod('__construct')) {
+ $node->addMethod(new MethodNode('__construct', ''));
+
+ return;
+ }
+
+ $constructor = $node->getMethod('__construct');
+ foreach ($constructor->getArguments() as $argument) {
+ $argument->setDefault(null);
+ }
+
+ $constructor->setCode(<<<PHP
+if (0 < func_num_args()) {
+ call_user_func_array(array('parent', '__construct'), func_get_args());
+}
+PHP
+ );
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 100;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+
+/**
+ * Exception patch for HHVM to remove the stubs from special methods
+ *
+ * @author Christophe Coevoet <stof@notk.org>
+ */
+class HhvmExceptionPatch implements ClassPatchInterface
+{
+ /**
+ * Supports exceptions on HHVM.
+ *
+ * @param ClassNode $node
+ *
+ * @return bool
+ */
+ public function supports(ClassNode $node)
+ {
+ if (!defined('HHVM_VERSION')) {
+ return false;
+ }
+
+ return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception');
+ }
+
+ /**
+ * Removes special exception static methods from the doubled methods.
+ *
+ * @param ClassNode $node
+ *
+ * @return void
+ */
+ public function apply(ClassNode $node)
+ {
+ if ($node->hasMethod('setTraceOptions')) {
+ $node->getMethod('setTraceOptions')->useParentCode();
+ }
+ if ($node->hasMethod('getTraceOptions')) {
+ $node->getMethod('getTraceOptions')->useParentCode();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPriority()
+ {
+ return -50;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+
+/**
+ * Remove method functionality from the double which will clash with php keywords.
+ *
+ * @author Milan Magudia <milan@magudia.com>
+ */
+class KeywordPatch implements ClassPatchInterface
+{
+ /**
+ * Support any class
+ *
+ * @param ClassNode $node
+ *
+ * @return boolean
+ */
+ public function supports(ClassNode $node)
+ {
+ return true;
+ }
+
+ /**
+ * Remove methods that clash with php keywords
+ *
+ * @param ClassNode $node
+ */
+ public function apply(ClassNode $node)
+ {
+ $methodNames = array_keys($node->getMethods());
+ $methodsToRemove = array_intersect($methodNames, $this->getKeywords());
+ foreach ($methodsToRemove as $methodName) {
+ $node->removeMethod($methodName);
+ }
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority() {
+ return 49;
+ }
+
+ /**
+ * Returns array of php keywords.
+ *
+ * @return array
+ */
+ private function getKeywords() {
+
+ return array(
+ '__halt_compiler',
+ 'abstract',
+ 'and',
+ 'array',
+ 'as',
+ 'break',
+ 'callable',
+ 'case',
+ 'catch',
+ 'class',
+ 'clone',
+ 'const',
+ 'continue',
+ 'declare',
+ 'default',
+ 'die',
+ 'do',
+ 'echo',
+ 'else',
+ 'elseif',
+ 'empty',
+ 'enddeclare',
+ 'endfor',
+ 'endforeach',
+ 'endif',
+ 'endswitch',
+ 'endwhile',
+ 'eval',
+ 'exit',
+ 'extends',
+ 'final',
+ 'finally',
+ 'for',
+ 'foreach',
+ 'function',
+ 'global',
+ 'goto',
+ 'if',
+ 'implements',
+ 'include',
+ 'include_once',
+ 'instanceof',
+ 'insteadof',
+ 'interface',
+ 'isset',
+ 'list',
+ 'namespace',
+ 'new',
+ 'or',
+ 'print',
+ 'private',
+ 'protected',
+ 'public',
+ 'require',
+ 'require_once',
+ 'return',
+ 'static',
+ 'switch',
+ 'throw',
+ 'trait',
+ 'try',
+ 'unset',
+ 'use',
+ 'var',
+ 'while',
+ 'xor',
+ 'yield',
+ );
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+use Prophecy\Doubler\Generator\Node\MethodNode;
+use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever;
+use Prophecy\PhpDocumentor\MethodTagRetrieverInterface;
+
+/**
+ * Discover Magical API using "@method" PHPDoc format.
+ *
+ * @author Thomas Tourlourat <thomas@tourlourat.com>
+ * @author Kévin Dunglas <dunglas@gmail.com>
+ * @author Théo FIDRY <theo.fidry@gmail.com>
+ */
+class MagicCallPatch implements ClassPatchInterface
+{
+ private $tagRetriever;
+
+ public function __construct(MethodTagRetrieverInterface $tagRetriever = null)
+ {
+ $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever;
+ }
+
+ /**
+ * Support any class
+ *
+ * @param ClassNode $node
+ *
+ * @return boolean
+ */
+ public function supports(ClassNode $node)
+ {
+ return true;
+ }
+
+ /**
+ * Discover Magical API
+ *
+ * @param ClassNode $node
+ */
+ public function apply(ClassNode $node)
+ {
+ $types = array_filter($node->getInterfaces(), function ($interface) {
+ return 0 !== strpos($interface, 'Prophecy\\');
+ });
+ $types[] = $node->getParentClass();
+
+ foreach ($types as $type) {
+ $reflectionClass = new \ReflectionClass($type);
+ $tagList = $this->tagRetriever->getTagList($reflectionClass);
+
+ foreach($tagList as $tag) {
+ $methodName = $tag->getMethodName();
+
+ if (empty($methodName)) {
+ continue;
+ }
+
+ if (!$reflectionClass->hasMethod($methodName)) {
+ $methodNode = new MethodNode($methodName);
+ $methodNode->setStatic($tag->isStatic());
+ $node->addMethod($methodNode);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return integer Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 50;
+ }
+}
+
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+use Prophecy\Doubler\Generator\Node\MethodNode;
+use Prophecy\Doubler\Generator\Node\ArgumentNode;
+
+/**
+ * Add Prophecy functionality to the double.
+ * This is a core class patch for Prophecy.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ProphecySubjectPatch implements ClassPatchInterface
+{
+ /**
+ * Always returns true.
+ *
+ * @param ClassNode $node
+ *
+ * @return bool
+ */
+ public function supports(ClassNode $node)
+ {
+ return true;
+ }
+
+ /**
+ * Apply Prophecy functionality to class node.
+ *
+ * @param ClassNode $node
+ */
+ public function apply(ClassNode $node)
+ {
+ $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface');
+ $node->addProperty('objectProphecy', 'private');
+
+ foreach ($node->getMethods() as $name => $method) {
+ if ('__construct' === strtolower($name)) {
+ continue;
+ }
+
+ if ($method->getReturnType() === 'void') {
+ $method->setCode(
+ '$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'
+ );
+ } else {
+ $method->setCode(
+ 'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'
+ );
+ }
+ }
+
+ $prophecySetter = new MethodNode('setProphecy');
+ $prophecyArgument = new ArgumentNode('prophecy');
+ $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface');
+ $prophecySetter->addArgument($prophecyArgument);
+ $prophecySetter->setCode('$this->objectProphecy = $prophecy;');
+
+ $prophecyGetter = new MethodNode('getProphecy');
+ $prophecyGetter->setCode('return $this->objectProphecy;');
+
+ if ($node->hasMethod('__call')) {
+ $__call = $node->getMethod('__call');
+ } else {
+ $__call = new MethodNode('__call');
+ $__call->addArgument(new ArgumentNode('name'));
+ $__call->addArgument(new ArgumentNode('arguments'));
+
+ $node->addMethod($__call);
+ }
+
+ $__call->setCode(<<<PHP
+throw new \Prophecy\Exception\Doubler\MethodNotFoundException(
+ sprintf('Method `%s::%s()` not found.', get_class(\$this), func_get_arg(0)),
+ \$this->getProphecy(), func_get_arg(0)
+);
+PHP
+ );
+
+ $node->addMethod($prophecySetter);
+ $node->addMethod($prophecyGetter);
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 0;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+
+/**
+ * ReflectionClass::newInstance patch.
+ * Makes first argument of newInstance optional, since it works but signature is misleading
+ *
+ * @author Florian Klein <florian.klein@free.fr>
+ */
+class ReflectionClassNewInstancePatch implements ClassPatchInterface
+{
+ /**
+ * Supports ReflectionClass
+ *
+ * @param ClassNode $node
+ *
+ * @return bool
+ */
+ public function supports(ClassNode $node)
+ {
+ return 'ReflectionClass' === $node->getParentClass();
+ }
+
+ /**
+ * Updates newInstance's first argument to make it optional
+ *
+ * @param ClassNode $node
+ */
+ public function apply(ClassNode $node)
+ {
+ foreach ($node->getMethod('newInstance')->getArguments() as $argument) {
+ $argument->setDefault(null);
+ }
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher = earlier)
+ */
+ public function getPriority()
+ {
+ return 50;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+use Prophecy\Doubler\Generator\Node\MethodNode;
+
+/**
+ * SplFileInfo patch.
+ * Makes SplFileInfo and derivative classes usable with Prophecy.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class SplFileInfoPatch implements ClassPatchInterface
+{
+ /**
+ * Supports everything that extends SplFileInfo.
+ *
+ * @param ClassNode $node
+ *
+ * @return bool
+ */
+ public function supports(ClassNode $node)
+ {
+ if (null === $node->getParentClass()) {
+ return false;
+ }
+
+ return 'SplFileInfo' === $node->getParentClass()
+ || is_subclass_of($node->getParentClass(), 'SplFileInfo')
+ ;
+ }
+
+ /**
+ * Updated constructor code to call parent one with dummy file argument.
+ *
+ * @param ClassNode $node
+ */
+ public function apply(ClassNode $node)
+ {
+ if ($node->hasMethod('__construct')) {
+ $constructor = $node->getMethod('__construct');
+ } else {
+ $constructor = new MethodNode('__construct');
+ $node->addMethod($constructor);
+ }
+
+ if ($this->nodeIsDirectoryIterator($node)) {
+ $constructor->setCode('return parent::__construct("' . __DIR__ . '");');
+
+ return;
+ }
+
+ if ($this->nodeIsSplFileObject($node)) {
+ $constructor->setCode('return parent::__construct("' . __FILE__ .'");');
+
+ return;
+ }
+
+ $constructor->useParentCode();
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 50;
+ }
+
+ /**
+ * @param ClassNode $node
+ * @return boolean
+ */
+ private function nodeIsDirectoryIterator(ClassNode $node)
+ {
+ $parent = $node->getParentClass();
+
+ return 'DirectoryIterator' === $parent
+ || is_subclass_of($parent, 'DirectoryIterator');
+ }
+
+ /**
+ * @param ClassNode $node
+ * @return boolean
+ */
+ private function nodeIsSplFileObject(ClassNode $node)
+ {
+ $parent = $node->getParentClass();
+
+ return 'SplFileObject' === $parent
+ || is_subclass_of($parent, 'SplFileObject');
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+use Prophecy\Doubler\Generator\Node\MethodNode;
+
+/**
+ * Traversable interface patch.
+ * Forces classes that implement interfaces, that extend Traversable to also implement Iterator.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class TraversablePatch implements ClassPatchInterface
+{
+ /**
+ * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate.
+ *
+ * @param ClassNode $node
+ *
+ * @return bool
+ */
+ public function supports(ClassNode $node)
+ {
+ if (in_array('Iterator', $node->getInterfaces())) {
+ return false;
+ }
+ if (in_array('IteratorAggregate', $node->getInterfaces())) {
+ return false;
+ }
+
+ foreach ($node->getInterfaces() as $interface) {
+ if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) {
+ continue;
+ }
+ if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) {
+ continue;
+ }
+ if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Forces class to implement Iterator interface.
+ *
+ * @param ClassNode $node
+ */
+ public function apply(ClassNode $node)
+ {
+ $node->addInterface('Iterator');
+
+ $node->addMethod(new MethodNode('current'));
+ $node->addMethod(new MethodNode('key'));
+ $node->addMethod(new MethodNode('next'));
+ $node->addMethod(new MethodNode('rewind'));
+ $node->addMethod(new MethodNode('valid'));
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 100;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler;
+
+/**
+ * Core double interface.
+ * All doubled classes will implement this one.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface DoubleInterface
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler;
+
+use Doctrine\Instantiator\Instantiator;
+use Prophecy\Doubler\ClassPatch\ClassPatchInterface;
+use Prophecy\Doubler\Generator\ClassMirror;
+use Prophecy\Doubler\Generator\ClassCreator;
+use Prophecy\Exception\InvalidArgumentException;
+use ReflectionClass;
+
+/**
+ * Cached class doubler.
+ * Prevents mirroring/creation of the same structure twice.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Doubler
+{
+ private $mirror;
+ private $creator;
+ private $namer;
+
+ /**
+ * @var ClassPatchInterface[]
+ */
+ private $patches = array();
+
+ /**
+ * @var \Doctrine\Instantiator\Instantiator
+ */
+ private $instantiator;
+
+ /**
+ * Initializes doubler.
+ *
+ * @param ClassMirror $mirror
+ * @param ClassCreator $creator
+ * @param NameGenerator $namer
+ */
+ public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null,
+ NameGenerator $namer = null)
+ {
+ $this->mirror = $mirror ?: new ClassMirror;
+ $this->creator = $creator ?: new ClassCreator;
+ $this->namer = $namer ?: new NameGenerator;
+ }
+
+ /**
+ * Returns list of registered class patches.
+ *
+ * @return ClassPatchInterface[]
+ */
+ public function getClassPatches()
+ {
+ return $this->patches;
+ }
+
+ /**
+ * Registers new class patch.
+ *
+ * @param ClassPatchInterface $patch
+ */
+ public function registerClassPatch(ClassPatchInterface $patch)
+ {
+ $this->patches[] = $patch;
+
+ @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) {
+ return $patch2->getPriority() - $patch1->getPriority();
+ });
+ }
+
+ /**
+ * Creates double from specific class or/and list of interfaces.
+ *
+ * @param ReflectionClass $class
+ * @param ReflectionClass[] $interfaces Array of ReflectionClass instances
+ * @param array $args Constructor arguments
+ *
+ * @return DoubleInterface
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function double(ReflectionClass $class = null, array $interfaces, array $args = null)
+ {
+ foreach ($interfaces as $interface) {
+ if (!$interface instanceof ReflectionClass) {
+ throw new InvalidArgumentException(sprintf(
+ "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n".
+ "a second argument to `Doubler::double(...)`, but got %s.",
+ is_object($interface) ? get_class($interface).' class' : gettype($interface)
+ ));
+ }
+ }
+
+ $classname = $this->createDoubleClass($class, $interfaces);
+ $reflection = new ReflectionClass($classname);
+
+ if (null !== $args) {
+ return $reflection->newInstanceArgs($args);
+ }
+ if ((null === $constructor = $reflection->getConstructor())
+ || ($constructor->isPublic() && !$constructor->isFinal())) {
+ return $reflection->newInstance();
+ }
+
+ if (!$this->instantiator) {
+ $this->instantiator = new Instantiator();
+ }
+
+ return $this->instantiator->instantiate($classname);
+ }
+
+ /**
+ * Creates double class and returns its FQN.
+ *
+ * @param ReflectionClass $class
+ * @param ReflectionClass[] $interfaces
+ *
+ * @return string
+ */
+ protected function createDoubleClass(ReflectionClass $class = null, array $interfaces)
+ {
+ $name = $this->namer->name($class, $interfaces);
+ $node = $this->mirror->reflect($class, $interfaces);
+
+ foreach ($this->patches as $patch) {
+ if ($patch->supports($node)) {
+ $patch->apply($node);
+ }
+ }
+
+ $this->creator->create($name, $node);
+
+ return $name;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\Generator;
+
+/**
+ * Class code creator.
+ * Generates PHP code for specific class node tree.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ClassCodeGenerator
+{
+ /**
+ * Generates PHP code for class node.
+ *
+ * @param string $classname
+ * @param Node\ClassNode $class
+ *
+ * @return string
+ */
+ public function generate($classname, Node\ClassNode $class)
+ {
+ $parts = explode('\\', $classname);
+ $classname = array_pop($parts);
+ $namespace = implode('\\', $parts);
+
+ $code = sprintf("class %s extends \%s implements %s {\n",
+ $classname, $class->getParentClass(), implode(', ',
+ array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces())
+ )
+ );
+
+ foreach ($class->getProperties() as $name => $visibility) {
+ $code .= sprintf("%s \$%s;\n", $visibility, $name);
+ }
+ $code .= "\n";
+
+ foreach ($class->getMethods() as $method) {
+ $code .= $this->generateMethod($method)."\n";
+ }
+ $code .= "\n}";
+
+ return sprintf("namespace %s {\n%s\n}", $namespace, $code);
+ }
+
+ private function generateMethod(Node\MethodNode $method)
+ {
+ $php = sprintf("%s %s function %s%s(%s)%s {\n",
+ $method->getVisibility(),
+ $method->isStatic() ? 'static' : '',
+ $method->returnsReference() ? '&':'',
+ $method->getName(),
+ implode(', ', $this->generateArguments($method->getArguments())),
+ $this->getReturnType($method)
+ );
+ $php .= $method->getCode()."\n";
+
+ return $php.'}';
+ }
+
+ /**
+ * @return string
+ */
+ private function getReturnType(Node\MethodNode $method)
+ {
+ if (version_compare(PHP_VERSION, '7.1', '>=')) {
+ if ($method->hasReturnType()) {
+ return $method->hasNullableReturnType()
+ ? sprintf(': ?%s', $method->getReturnType())
+ : sprintf(': %s', $method->getReturnType());
+ }
+ }
+
+ if (version_compare(PHP_VERSION, '7.0', '>=')) {
+ return $method->hasReturnType() && $method->getReturnType() !== 'void'
+ ? sprintf(': %s', $method->getReturnType())
+ : '';
+ }
+
+ return '';
+ }
+
+ private function generateArguments(array $arguments)
+ {
+ return array_map(function (Node\ArgumentNode $argument) {
+ $php = '';
+
+ if (version_compare(PHP_VERSION, '7.1', '>=')) {
+ $php .= $argument->isNullable() ? '?' : '';
+ }
+
+ if ($hint = $argument->getTypeHint()) {
+ switch ($hint) {
+ case 'array':
+ case 'callable':
+ $php .= $hint;
+ break;
+
+ case 'iterable':
+ if (version_compare(PHP_VERSION, '7.1', '>=')) {
+ $php .= $hint;
+ break;
+ }
+
+ $php .= '\\'.$hint;
+ break;
+
+ case 'string':
+ case 'int':
+ case 'float':
+ case 'bool':
+ if (version_compare(PHP_VERSION, '7.0', '>=')) {
+ $php .= $hint;
+ break;
+ }
+ // Fall-through to default case for PHP 5.x
+
+ default:
+ $php .= '\\'.$hint;
+ }
+ }
+
+ $php .= ' '.($argument->isPassedByReference() ? '&' : '');
+
+ $php .= $argument->isVariadic() ? '...' : '';
+
+ $php .= '$'.$argument->getName();
+
+ if ($argument->isOptional() && !$argument->isVariadic()) {
+ $php .= ' = '.var_export($argument->getDefault(), true);
+ }
+
+ return $php;
+ }, $arguments);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\Generator;
+
+use Prophecy\Exception\Doubler\ClassCreatorException;
+
+/**
+ * Class creator.
+ * Creates specific class in current environment.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ClassCreator
+{
+ private $generator;
+
+ /**
+ * Initializes creator.
+ *
+ * @param ClassCodeGenerator $generator
+ */
+ public function __construct(ClassCodeGenerator $generator = null)
+ {
+ $this->generator = $generator ?: new ClassCodeGenerator;
+ }
+
+ /**
+ * Creates class.
+ *
+ * @param string $classname
+ * @param Node\ClassNode $class
+ *
+ * @return mixed
+ *
+ * @throws \Prophecy\Exception\Doubler\ClassCreatorException
+ */
+ public function create($classname, Node\ClassNode $class)
+ {
+ $code = $this->generator->generate($classname, $class);
+ $return = eval($code);
+
+ if (!class_exists($classname, false)) {
+ if (count($class->getInterfaces())) {
+ throw new ClassCreatorException(sprintf(
+ 'Could not double `%s` and implement interfaces: [%s].',
+ $class->getParentClass(), implode(', ', $class->getInterfaces())
+ ), $class);
+ }
+
+ throw new ClassCreatorException(
+ sprintf('Could not double `%s`.', $class->getParentClass()),
+ $class
+ );
+ }
+
+ return $return;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\Generator;
+
+use Prophecy\Exception\InvalidArgumentException;
+use Prophecy\Exception\Doubler\ClassMirrorException;
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionParameter;
+
+/**
+ * Class mirror.
+ * Core doubler class. Mirrors specific class and/or interfaces into class node tree.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ClassMirror
+{
+ private static $reflectableMethods = array(
+ '__construct',
+ '__destruct',
+ '__sleep',
+ '__wakeup',
+ '__toString',
+ '__call',
+ '__invoke'
+ );
+
+ /**
+ * Reflects provided arguments into class node.
+ *
+ * @param ReflectionClass $class
+ * @param ReflectionClass[] $interfaces
+ *
+ * @return Node\ClassNode
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function reflect(ReflectionClass $class = null, array $interfaces)
+ {
+ $node = new Node\ClassNode;
+
+ if (null !== $class) {
+ if (true === $class->isInterface()) {
+ throw new InvalidArgumentException(sprintf(
+ "Could not reflect %s as a class, because it\n".
+ "is interface - use the second argument instead.",
+ $class->getName()
+ ));
+ }
+
+ $this->reflectClassToNode($class, $node);
+ }
+
+ foreach ($interfaces as $interface) {
+ if (!$interface instanceof ReflectionClass) {
+ throw new InvalidArgumentException(sprintf(
+ "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n".
+ "a second argument to `ClassMirror::reflect(...)`, but got %s.",
+ is_object($interface) ? get_class($interface).' class' : gettype($interface)
+ ));
+ }
+ if (false === $interface->isInterface()) {
+ throw new InvalidArgumentException(sprintf(
+ "Could not reflect %s as an interface, because it\n".
+ "is class - use the first argument instead.",
+ $interface->getName()
+ ));
+ }
+
+ $this->reflectInterfaceToNode($interface, $node);
+ }
+
+ $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface');
+
+ return $node;
+ }
+
+ private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node)
+ {
+ if (true === $class->isFinal()) {
+ throw new ClassMirrorException(sprintf(
+ 'Could not reflect class %s as it is marked final.', $class->getName()
+ ), $class);
+ }
+
+ $node->setParentClass($class->getName());
+
+ foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) {
+ if (false === $method->isProtected()) {
+ continue;
+ }
+
+ $this->reflectMethodToNode($method, $node);
+ }
+
+ foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
+ if (0 === strpos($method->getName(), '_')
+ && !in_array($method->getName(), self::$reflectableMethods)) {
+ continue;
+ }
+
+ if (true === $method->isFinal()) {
+ $node->addUnextendableMethod($method->getName());
+ continue;
+ }
+
+ $this->reflectMethodToNode($method, $node);
+ }
+ }
+
+ private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node)
+ {
+ $node->addInterface($interface->getName());
+
+ foreach ($interface->getMethods() as $method) {
+ $this->reflectMethodToNode($method, $node);
+ }
+ }
+
+ private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode)
+ {
+ $node = new Node\MethodNode($method->getName());
+
+ if (true === $method->isProtected()) {
+ $node->setVisibility('protected');
+ }
+
+ if (true === $method->isStatic()) {
+ $node->setStatic();
+ }
+
+ if (true === $method->returnsReference()) {
+ $node->setReturnsReference();
+ }
+
+ if (version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()) {
+ $returnType = (string) $method->getReturnType();
+ $returnTypeLower = strtolower($returnType);
+
+ if ('self' === $returnTypeLower) {
+ $returnType = $method->getDeclaringClass()->getName();
+ }
+ if ('parent' === $returnTypeLower) {
+ $returnType = $method->getDeclaringClass()->getParentClass()->getName();
+ }
+
+ $node->setReturnType($returnType);
+
+ if (version_compare(PHP_VERSION, '7.1', '>=') && $method->getReturnType()->allowsNull()) {
+ $node->setNullableReturnType(true);
+ }
+ }
+
+ if (is_array($params = $method->getParameters()) && count($params)) {
+ foreach ($params as $param) {
+ $this->reflectArgumentToNode($param, $node);
+ }
+ }
+
+ $classNode->addMethod($node);
+ }
+
+ private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode)
+ {
+ $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName();
+ $node = new Node\ArgumentNode($name);
+
+ $node->setTypeHint($this->getTypeHint($parameter));
+
+ if ($this->isVariadic($parameter)) {
+ $node->setAsVariadic();
+ }
+
+ if ($this->hasDefaultValue($parameter)) {
+ $node->setDefault($this->getDefaultValue($parameter));
+ }
+
+ if ($parameter->isPassedByReference()) {
+ $node->setAsPassedByReference();
+ }
+
+ $methodNode->addArgument($node);
+ }
+
+ private function hasDefaultValue(ReflectionParameter $parameter)
+ {
+ if ($this->isVariadic($parameter)) {
+ return false;
+ }
+
+ if ($parameter->isDefaultValueAvailable()) {
+ return true;
+ }
+
+ return $parameter->isOptional() || $this->isNullable($parameter);
+ }
+
+ private function getDefaultValue(ReflectionParameter $parameter)
+ {
+ if (!$parameter->isDefaultValueAvailable()) {
+ return null;
+ }
+
+ return $parameter->getDefaultValue();
+ }
+
+ private function getTypeHint(ReflectionParameter $parameter)
+ {
+ if (null !== $className = $this->getParameterClassName($parameter)) {
+ return $className;
+ }
+
+ if (true === $parameter->isArray()) {
+ return 'array';
+ }
+
+ if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) {
+ return 'callable';
+ }
+
+ if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) {
+ return (string) $parameter->getType();
+ }
+
+ return null;
+ }
+
+ private function isVariadic(ReflectionParameter $parameter)
+ {
+ return PHP_VERSION_ID >= 50600 && $parameter->isVariadic();
+ }
+
+ private function isNullable(ReflectionParameter $parameter)
+ {
+ return $parameter->allowsNull() && null !== $this->getTypeHint($parameter);
+ }
+
+ private function getParameterClassName(ReflectionParameter $parameter)
+ {
+ try {
+ return $parameter->getClass() ? $parameter->getClass()->getName() : null;
+ } catch (\ReflectionException $e) {
+ preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches);
+
+ return isset($matches[1]) ? $matches[1] : null;
+ }
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\Generator\Node;
+
+/**
+ * Argument node.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ArgumentNode
+{
+ private $name;
+ private $typeHint;
+ private $default;
+ private $optional = false;
+ private $byReference = false;
+ private $isVariadic = false;
+ private $isNullable = false;
+
+ /**
+ * @param string $name
+ */
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function getTypeHint()
+ {
+ return $this->typeHint;
+ }
+
+ public function setTypeHint($typeHint = null)
+ {
+ $this->typeHint = $typeHint;
+ }
+
+ public function hasDefault()
+ {
+ return $this->isOptional() && !$this->isVariadic();
+ }
+
+ public function getDefault()
+ {
+ return $this->default;
+ }
+
+ public function setDefault($default = null)
+ {
+ $this->optional = true;
+ $this->default = $default;
+ }
+
+ public function isOptional()
+ {
+ return $this->optional;
+ }
+
+ public function setAsPassedByReference($byReference = true)
+ {
+ $this->byReference = $byReference;
+ }
+
+ public function isPassedByReference()
+ {
+ return $this->byReference;
+ }
+
+ public function setAsVariadic($isVariadic = true)
+ {
+ $this->isVariadic = $isVariadic;
+ }
+
+ public function isVariadic()
+ {
+ return $this->isVariadic;
+ }
+
+ public function isNullable()
+ {
+ return $this->isNullable;
+ }
+
+ public function setAsNullable($isNullable = true)
+ {
+ $this->isNullable = $isNullable;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\Generator\Node;
+
+use Prophecy\Exception\Doubler\MethodNotExtendableException;
+use Prophecy\Exception\InvalidArgumentException;
+
+/**
+ * Class node.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ClassNode
+{
+ private $parentClass = 'stdClass';
+ private $interfaces = array();
+ private $properties = array();
+ private $unextendableMethods = array();
+
+ /**
+ * @var MethodNode[]
+ */
+ private $methods = array();
+
+ public function getParentClass()
+ {
+ return $this->parentClass;
+ }
+
+ /**
+ * @param string $class
+ */
+ public function setParentClass($class)
+ {
+ $this->parentClass = $class ?: 'stdClass';
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getInterfaces()
+ {
+ return $this->interfaces;
+ }
+
+ /**
+ * @param string $interface
+ */
+ public function addInterface($interface)
+ {
+ if ($this->hasInterface($interface)) {
+ return;
+ }
+
+ array_unshift($this->interfaces, $interface);
+ }
+
+ /**
+ * @param string $interface
+ *
+ * @return bool
+ */
+ public function hasInterface($interface)
+ {
+ return in_array($interface, $this->interfaces);
+ }
+
+ public function getProperties()
+ {
+ return $this->properties;
+ }
+
+ public function addProperty($name, $visibility = 'public')
+ {
+ $visibility = strtolower($visibility);
+
+ if (!in_array($visibility, array('public', 'private', 'protected'))) {
+ throw new InvalidArgumentException(sprintf(
+ '`%s` property visibility is not supported.', $visibility
+ ));
+ }
+
+ $this->properties[$name] = $visibility;
+ }
+
+ /**
+ * @return MethodNode[]
+ */
+ public function getMethods()
+ {
+ return $this->methods;
+ }
+
+ public function addMethod(MethodNode $method)
+ {
+ if (!$this->isExtendable($method->getName())){
+ $message = sprintf(
+ 'Method `%s` is not extendable, so can not be added.', $method->getName()
+ );
+ throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName());
+ }
+ $this->methods[$method->getName()] = $method;
+ }
+
+ public function removeMethod($name)
+ {
+ unset($this->methods[$name]);
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return MethodNode|null
+ */
+ public function getMethod($name)
+ {
+ return $this->hasMethod($name) ? $this->methods[$name] : null;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasMethod($name)
+ {
+ return isset($this->methods[$name]);
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getUnextendableMethods()
+ {
+ return $this->unextendableMethods;
+ }
+
+ /**
+ * @param string $unextendableMethod
+ */
+ public function addUnextendableMethod($unextendableMethod)
+ {
+ if (!$this->isExtendable($unextendableMethod)){
+ return;
+ }
+ $this->unextendableMethods[] = $unextendableMethod;
+ }
+
+ /**
+ * @param string $method
+ * @return bool
+ */
+ public function isExtendable($method)
+ {
+ return !in_array($method, $this->unextendableMethods);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\Generator\Node;
+
+use Prophecy\Exception\InvalidArgumentException;
+
+/**
+ * Method node.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class MethodNode
+{
+ private $name;
+ private $code;
+ private $visibility = 'public';
+ private $static = false;
+ private $returnsReference = false;
+ private $returnType;
+ private $nullableReturnType = false;
+
+ /**
+ * @var ArgumentNode[]
+ */
+ private $arguments = array();
+
+ /**
+ * @param string $name
+ * @param string $code
+ */
+ public function __construct($name, $code = null)
+ {
+ $this->name = $name;
+ $this->code = $code;
+ }
+
+ public function getVisibility()
+ {
+ return $this->visibility;
+ }
+
+ /**
+ * @param string $visibility
+ */
+ public function setVisibility($visibility)
+ {
+ $visibility = strtolower($visibility);
+
+ if (!in_array($visibility, array('public', 'private', 'protected'))) {
+ throw new InvalidArgumentException(sprintf(
+ '`%s` method visibility is not supported.', $visibility
+ ));
+ }
+
+ $this->visibility = $visibility;
+ }
+
+ public function isStatic()
+ {
+ return $this->static;
+ }
+
+ public function setStatic($static = true)
+ {
+ $this->static = (bool) $static;
+ }
+
+ public function returnsReference()
+ {
+ return $this->returnsReference;
+ }
+
+ public function setReturnsReference()
+ {
+ $this->returnsReference = true;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function addArgument(ArgumentNode $argument)
+ {
+ $this->arguments[] = $argument;
+ }
+
+ /**
+ * @return ArgumentNode[]
+ */
+ public function getArguments()
+ {
+ return $this->arguments;
+ }
+
+ public function hasReturnType()
+ {
+ return null !== $this->returnType;
+ }
+
+ /**
+ * @param string $type
+ */
+ public function setReturnType($type = null)
+ {
+ switch ($type) {
+ case '':
+ $this->returnType = null;
+ break;
+
+ case 'string';
+ case 'float':
+ case 'int':
+ case 'bool':
+ case 'array':
+ case 'callable':
+ case 'iterable':
+ case 'void':
+ $this->returnType = $type;
+ break;
+
+ case 'double':
+ case 'real':
+ $this->returnType = 'float';
+ break;
+
+ case 'boolean':
+ $this->returnType = 'bool';
+ break;
+
+ case 'integer':
+ $this->returnType = 'int';
+ break;
+
+ default:
+ $this->returnType = '\\' . ltrim($type, '\\');
+ }
+ }
+
+ public function getReturnType()
+ {
+ return $this->returnType;
+ }
+
+ /**
+ * @param bool $bool
+ */
+ public function setNullableReturnType($bool = true)
+ {
+ $this->nullableReturnType = (bool) $bool;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasNullableReturnType()
+ {
+ return $this->nullableReturnType;
+ }
+
+ /**
+ * @param string $code
+ */
+ public function setCode($code)
+ {
+ $this->code = $code;
+ }
+
+ public function getCode()
+ {
+ if ($this->returnsReference)
+ {
+ return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');";
+ }
+
+ return (string) $this->code;
+ }
+
+ public function useParentCode()
+ {
+ $this->code = sprintf(
+ 'return parent::%s(%s);', $this->getName(), implode(', ',
+ array_map(array($this, 'generateArgument'), $this->arguments)
+ )
+ );
+ }
+
+ private function generateArgument(ArgumentNode $arg)
+ {
+ $argument = '$'.$arg->getName();
+
+ if ($arg->isVariadic()) {
+ $argument = '...'.$argument;
+ }
+
+ return $argument;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler\Generator;
+
+/**
+ * Reflection interface.
+ * All reflected classes implement this interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface ReflectionInterface
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler;
+
+use Prophecy\Exception\Doubler\DoubleException;
+use Prophecy\Exception\Doubler\ClassNotFoundException;
+use Prophecy\Exception\Doubler\InterfaceNotFoundException;
+use ReflectionClass;
+
+/**
+ * Lazy double.
+ * Gives simple interface to describe double before creating it.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class LazyDouble
+{
+ private $doubler;
+ private $class;
+ private $interfaces = array();
+ private $arguments = null;
+ private $double;
+
+ /**
+ * Initializes lazy double.
+ *
+ * @param Doubler $doubler
+ */
+ public function __construct(Doubler $doubler)
+ {
+ $this->doubler = $doubler;
+ }
+
+ /**
+ * Tells doubler to use specific class as parent one for double.
+ *
+ * @param string|ReflectionClass $class
+ *
+ * @throws \Prophecy\Exception\Doubler\ClassNotFoundException
+ * @throws \Prophecy\Exception\Doubler\DoubleException
+ */
+ public function setParentClass($class)
+ {
+ if (null !== $this->double) {
+ throw new DoubleException('Can not extend class with already instantiated double.');
+ }
+
+ if (!$class instanceof ReflectionClass) {
+ if (!class_exists($class)) {
+ throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class);
+ }
+
+ $class = new ReflectionClass($class);
+ }
+
+ $this->class = $class;
+ }
+
+ /**
+ * Tells doubler to implement specific interface with double.
+ *
+ * @param string|ReflectionClass $interface
+ *
+ * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException
+ * @throws \Prophecy\Exception\Doubler\DoubleException
+ */
+ public function addInterface($interface)
+ {
+ if (null !== $this->double) {
+ throw new DoubleException(
+ 'Can not implement interface with already instantiated double.'
+ );
+ }
+
+ if (!$interface instanceof ReflectionClass) {
+ if (!interface_exists($interface)) {
+ throw new InterfaceNotFoundException(
+ sprintf('Interface %s not found.', $interface),
+ $interface
+ );
+ }
+
+ $interface = new ReflectionClass($interface);
+ }
+
+ $this->interfaces[] = $interface;
+ }
+
+ /**
+ * Sets constructor arguments.
+ *
+ * @param array $arguments
+ */
+ public function setArguments(array $arguments = null)
+ {
+ $this->arguments = $arguments;
+ }
+
+ /**
+ * Creates double instance or returns already created one.
+ *
+ * @return DoubleInterface
+ */
+ public function getInstance()
+ {
+ if (null === $this->double) {
+ if (null !== $this->arguments) {
+ return $this->double = $this->doubler->double(
+ $this->class, $this->interfaces, $this->arguments
+ );
+ }
+
+ $this->double = $this->doubler->double($this->class, $this->interfaces);
+ }
+
+ return $this->double;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Doubler;
+
+use ReflectionClass;
+
+/**
+ * Name generator.
+ * Generates classname for double.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class NameGenerator
+{
+ private static $counter = 1;
+
+ /**
+ * Generates name.
+ *
+ * @param ReflectionClass $class
+ * @param ReflectionClass[] $interfaces
+ *
+ * @return string
+ */
+ public function name(ReflectionClass $class = null, array $interfaces)
+ {
+ $parts = array();
+
+ if (null !== $class) {
+ $parts[] = $class->getName();
+ } else {
+ foreach ($interfaces as $interface) {
+ $parts[] = $interface->getShortName();
+ }
+ }
+
+ if (!count($parts)) {
+ $parts[] = 'stdClass';
+ }
+
+ return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Call;
+
+use Prophecy\Exception\Prophecy\ObjectProphecyException;
+use Prophecy\Prophecy\ObjectProphecy;
+
+class UnexpectedCallException extends ObjectProphecyException
+{
+ private $methodName;
+ private $arguments;
+
+ public function __construct($message, ObjectProphecy $objectProphecy,
+ $methodName, array $arguments)
+ {
+ parent::__construct($message, $objectProphecy);
+
+ $this->methodName = $methodName;
+ $this->arguments = $arguments;
+ }
+
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+
+ public function getArguments()
+ {
+ return $this->arguments;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+
+class ClassCreatorException extends \RuntimeException implements DoublerException
+{
+ private $node;
+
+ public function __construct($message, ClassNode $node)
+ {
+ parent::__construct($message);
+
+ $this->node = $node;
+ }
+
+ public function getClassNode()
+ {
+ return $this->node;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+use ReflectionClass;
+
+class ClassMirrorException extends \RuntimeException implements DoublerException
+{
+ private $class;
+
+ public function __construct($message, ReflectionClass $class)
+ {
+ parent::__construct($message);
+
+ $this->class = $class;
+ }
+
+ public function getReflectedClass()
+ {
+ return $this->class;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+class ClassNotFoundException extends DoubleException
+{
+ private $classname;
+
+ /**
+ * @param string $message
+ * @param string $classname
+ */
+ public function __construct($message, $classname)
+ {
+ parent::__construct($message);
+
+ $this->classname = $classname;
+ }
+
+ public function getClassname()
+ {
+ return $this->classname;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+use RuntimeException;
+
+class DoubleException extends RuntimeException implements DoublerException
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+use Prophecy\Exception\Exception;
+
+interface DoublerException extends Exception
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+class InterfaceNotFoundException extends ClassNotFoundException
+{
+ public function getInterfaceName()
+ {
+ return $this->getClassname();
+ }
+}
+<?php
+
+ namespace Prophecy\Exception\Doubler;
+
+ class MethodNotExtendableException extends DoubleException
+ {
+ private $methodName;
+
+ private $className;
+
+ /**
+ * @param string $message
+ * @param string $className
+ * @param string $methodName
+ */
+ public function __construct($message, $className, $methodName)
+ {
+ parent::__construct($message);
+
+ $this->methodName = $methodName;
+ $this->className = $className;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ }
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+class MethodNotFoundException extends DoubleException
+{
+ /**
+ * @var string
+ */
+ private $classname;
+
+ /**
+ * @var string
+ */
+ private $methodName;
+
+ /**
+ * @var array
+ */
+ private $arguments;
+
+ /**
+ * @param string $message
+ * @param string $classname
+ * @param string $methodName
+ * @param null|Argument\ArgumentsWildcard|array $arguments
+ */
+ public function __construct($message, $classname, $methodName, $arguments = null)
+ {
+ parent::__construct($message);
+
+ $this->classname = $classname;
+ $this->methodName = $methodName;
+ $this->arguments = $arguments;
+ }
+
+ public function getClassname()
+ {
+ return $this->classname;
+ }
+
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+
+ public function getArguments()
+ {
+ return $this->arguments;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Doubler;
+
+class ReturnByReferenceException extends DoubleException
+{
+ private $classname;
+ private $methodName;
+
+ /**
+ * @param string $message
+ * @param string $classname
+ * @param string $methodName
+ */
+ public function __construct($message, $classname, $methodName)
+ {
+ parent::__construct($message);
+
+ $this->classname = $classname;
+ $this->methodName = $methodName;
+ }
+
+ public function getClassname()
+ {
+ return $this->classname;
+ }
+
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception;
+
+/**
+ * Core Prophecy exception interface.
+ * All Prophecy exceptions implement it.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface Exception
+{
+ /**
+ * @return string
+ */
+ public function getMessage();
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception;
+
+class InvalidArgumentException extends \InvalidArgumentException implements Exception
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prediction;
+
+use Prophecy\Prophecy\ObjectProphecy;
+
+class AggregateException extends \RuntimeException implements PredictionException
+{
+ private $exceptions = array();
+ private $objectProphecy;
+
+ public function append(PredictionException $exception)
+ {
+ $message = $exception->getMessage();
+ $message = ' '.strtr($message, array("\n" => "\n "))."\n";
+
+ $this->message = rtrim($this->message.$message);
+ $this->exceptions[] = $exception;
+ }
+
+ /**
+ * @return PredictionException[]
+ */
+ public function getExceptions()
+ {
+ return $this->exceptions;
+ }
+
+ public function setObjectProphecy(ObjectProphecy $objectProphecy)
+ {
+ $this->objectProphecy = $objectProphecy;
+ }
+
+ /**
+ * @return ObjectProphecy
+ */
+ public function getObjectProphecy()
+ {
+ return $this->objectProphecy;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prediction;
+
+use RuntimeException;
+
+/**
+ * Basic failed prediction exception.
+ * Use it for custom prediction failures.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class FailedPredictionException extends RuntimeException implements PredictionException
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prediction;
+
+use Prophecy\Exception\Prophecy\MethodProphecyException;
+
+class NoCallsException extends MethodProphecyException implements PredictionException
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prediction;
+
+use Prophecy\Exception\Exception;
+
+interface PredictionException extends Exception
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prediction;
+
+use Prophecy\Prophecy\MethodProphecy;
+
+class UnexpectedCallsCountException extends UnexpectedCallsException
+{
+ private $expectedCount;
+
+ public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls)
+ {
+ parent::__construct($message, $methodProphecy, $calls);
+
+ $this->expectedCount = intval($count);
+ }
+
+ public function getExpectedCount()
+ {
+ return $this->expectedCount;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prediction;
+
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Exception\Prophecy\MethodProphecyException;
+
+class UnexpectedCallsException extends MethodProphecyException implements PredictionException
+{
+ private $calls = array();
+
+ public function __construct($message, MethodProphecy $methodProphecy, array $calls)
+ {
+ parent::__construct($message, $methodProphecy);
+
+ $this->calls = $calls;
+ }
+
+ public function getCalls()
+ {
+ return $this->calls;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prophecy;
+
+use Prophecy\Prophecy\MethodProphecy;
+
+class MethodProphecyException extends ObjectProphecyException
+{
+ private $methodProphecy;
+
+ public function __construct($message, MethodProphecy $methodProphecy)
+ {
+ parent::__construct($message, $methodProphecy->getObjectProphecy());
+
+ $this->methodProphecy = $methodProphecy;
+ }
+
+ /**
+ * @return MethodProphecy
+ */
+ public function getMethodProphecy()
+ {
+ return $this->methodProphecy;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prophecy;
+
+use Prophecy\Prophecy\ObjectProphecy;
+
+class ObjectProphecyException extends \RuntimeException implements ProphecyException
+{
+ private $objectProphecy;
+
+ public function __construct($message, ObjectProphecy $objectProphecy)
+ {
+ parent::__construct($message);
+
+ $this->objectProphecy = $objectProphecy;
+ }
+
+ /**
+ * @return ObjectProphecy
+ */
+ public function getObjectProphecy()
+ {
+ return $this->objectProphecy;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Exception\Prophecy;
+
+use Prophecy\Exception\Exception;
+
+interface ProphecyException extends Exception
+{
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\PhpDocumentor;
+
+use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag;
+use phpDocumentor\Reflection\DocBlock\Tags\Method;
+
+/**
+ * @author Théo FIDRY <theo.fidry@gmail.com>
+ *
+ * @internal
+ */
+final class ClassAndInterfaceTagRetriever implements MethodTagRetrieverInterface
+{
+ private $classRetriever;
+
+ public function __construct(MethodTagRetrieverInterface $classRetriever = null)
+ {
+ if (null !== $classRetriever) {
+ $this->classRetriever = $classRetriever;
+
+ return;
+ }
+
+ $this->classRetriever = class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory')
+ ? new ClassTagRetriever()
+ : new LegacyClassTagRetriever()
+ ;
+ }
+
+ /**
+ * @param \ReflectionClass $reflectionClass
+ *
+ * @return LegacyMethodTag[]|Method[]
+ */
+ public function getTagList(\ReflectionClass $reflectionClass)
+ {
+ return array_merge(
+ $this->classRetriever->getTagList($reflectionClass),
+ $this->getInterfacesTagList($reflectionClass)
+ );
+ }
+
+ /**
+ * @param \ReflectionClass $reflectionClass
+ *
+ * @return LegacyMethodTag[]|Method[]
+ */
+ private function getInterfacesTagList(\ReflectionClass $reflectionClass)
+ {
+ $interfaces = $reflectionClass->getInterfaces();
+ $tagList = array();
+
+ foreach($interfaces as $interface) {
+ $tagList = array_merge($tagList, $this->classRetriever->getTagList($interface));
+ }
+
+ return $tagList;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\PhpDocumentor;
+
+use phpDocumentor\Reflection\DocBlock\Tags\Method;
+use phpDocumentor\Reflection\DocBlockFactory;
+use phpDocumentor\Reflection\Types\ContextFactory;
+
+/**
+ * @author Théo FIDRY <theo.fidry@gmail.com>
+ *
+ * @internal
+ */
+final class ClassTagRetriever implements MethodTagRetrieverInterface
+{
+ private $docBlockFactory;
+ private $contextFactory;
+
+ public function __construct()
+ {
+ $this->docBlockFactory = DocBlockFactory::createInstance();
+ $this->contextFactory = new ContextFactory();
+ }
+
+ /**
+ * @param \ReflectionClass $reflectionClass
+ *
+ * @return Method[]
+ */
+ public function getTagList(\ReflectionClass $reflectionClass)
+ {
+ try {
+ $phpdoc = $this->docBlockFactory->create(
+ $reflectionClass,
+ $this->contextFactory->createFromReflector($reflectionClass)
+ );
+
+ return $phpdoc->getTagsByName('method');
+ } catch (\InvalidArgumentException $e) {
+ return array();
+ }
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\PhpDocumentor;
+
+use phpDocumentor\Reflection\DocBlock;
+use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag;
+
+/**
+ * @author Théo FIDRY <theo.fidry@gmail.com>
+ *
+ * @internal
+ */
+final class LegacyClassTagRetriever implements MethodTagRetrieverInterface
+{
+ /**
+ * @param \ReflectionClass $reflectionClass
+ *
+ * @return LegacyMethodTag[]
+ */
+ public function getTagList(\ReflectionClass $reflectionClass)
+ {
+ $phpdoc = new DocBlock($reflectionClass->getDocComment());
+
+ return $phpdoc->getTagsByName('method');
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\PhpDocumentor;
+
+use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag;
+use phpDocumentor\Reflection\DocBlock\Tags\Method;
+
+/**
+ * @author Théo FIDRY <theo.fidry@gmail.com>
+ *
+ * @internal
+ */
+interface MethodTagRetrieverInterface
+{
+ /**
+ * @param \ReflectionClass $reflectionClass
+ *
+ * @return LegacyMethodTag[]|Method[]
+ */
+ public function getTagList(\ReflectionClass $reflectionClass);
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prediction;
+
+use Prophecy\Call\Call;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Argument\ArgumentsWildcard;
+use Prophecy\Argument\Token\AnyValuesToken;
+use Prophecy\Util\StringUtil;
+use Prophecy\Exception\Prediction\NoCallsException;
+
+/**
+ * Call prediction.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CallPrediction implements PredictionInterface
+{
+ private $util;
+
+ /**
+ * Initializes prediction.
+ *
+ * @param StringUtil $util
+ */
+ public function __construct(StringUtil $util = null)
+ {
+ $this->util = $util ?: new StringUtil;
+ }
+
+ /**
+ * Tests that there was at least one call.
+ *
+ * @param Call[] $calls
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @throws \Prophecy\Exception\Prediction\NoCallsException
+ */
+ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
+ {
+ if (count($calls)) {
+ return;
+ }
+
+ $methodCalls = $object->findProphecyMethodCalls(
+ $method->getMethodName(),
+ new ArgumentsWildcard(array(new AnyValuesToken))
+ );
+
+ if (count($methodCalls)) {
+ throw new NoCallsException(sprintf(
+ "No calls have been made that match:\n".
+ " %s->%s(%s)\n".
+ "but expected at least one.\n".
+ "Recorded `%s(...)` calls:\n%s",
+
+ get_class($object->reveal()),
+ $method->getMethodName(),
+ $method->getArgumentsWildcard(),
+ $method->getMethodName(),
+ $this->util->stringifyCalls($methodCalls)
+ ), $method);
+ }
+
+ throw new NoCallsException(sprintf(
+ "No calls have been made that match:\n".
+ " %s->%s(%s)\n".
+ "but expected at least one.",
+
+ get_class($object->reveal()),
+ $method->getMethodName(),
+ $method->getArgumentsWildcard()
+ ), $method);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prediction;
+
+use Prophecy\Call\Call;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Argument\ArgumentsWildcard;
+use Prophecy\Argument\Token\AnyValuesToken;
+use Prophecy\Util\StringUtil;
+use Prophecy\Exception\Prediction\UnexpectedCallsCountException;
+
+/**
+ * Prediction interface.
+ * Predictions are logical test blocks, tied to `should...` keyword.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CallTimesPrediction implements PredictionInterface
+{
+ private $times;
+ private $util;
+
+ /**
+ * Initializes prediction.
+ *
+ * @param int $times
+ * @param StringUtil $util
+ */
+ public function __construct($times, StringUtil $util = null)
+ {
+ $this->times = intval($times);
+ $this->util = $util ?: new StringUtil;
+ }
+
+ /**
+ * Tests that there was exact amount of calls made.
+ *
+ * @param Call[] $calls
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException
+ */
+ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
+ {
+ if ($this->times == count($calls)) {
+ return;
+ }
+
+ $methodCalls = $object->findProphecyMethodCalls(
+ $method->getMethodName(),
+ new ArgumentsWildcard(array(new AnyValuesToken))
+ );
+
+ if (count($calls)) {
+ $message = sprintf(
+ "Expected exactly %d calls that match:\n".
+ " %s->%s(%s)\n".
+ "but %d were made:\n%s",
+
+ $this->times,
+ get_class($object->reveal()),
+ $method->getMethodName(),
+ $method->getArgumentsWildcard(),
+ count($calls),
+ $this->util->stringifyCalls($calls)
+ );
+ } elseif (count($methodCalls)) {
+ $message = sprintf(
+ "Expected exactly %d calls that match:\n".
+ " %s->%s(%s)\n".
+ "but none were made.\n".
+ "Recorded `%s(...)` calls:\n%s",
+
+ $this->times,
+ get_class($object->reveal()),
+ $method->getMethodName(),
+ $method->getArgumentsWildcard(),
+ $method->getMethodName(),
+ $this->util->stringifyCalls($methodCalls)
+ );
+ } else {
+ $message = sprintf(
+ "Expected exactly %d calls that match:\n".
+ " %s->%s(%s)\n".
+ "but none were made.",
+
+ $this->times,
+ get_class($object->reveal()),
+ $method->getMethodName(),
+ $method->getArgumentsWildcard()
+ );
+ }
+
+ throw new UnexpectedCallsCountException($message, $method, $this->times, $calls);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prediction;
+
+use Prophecy\Call\Call;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Exception\InvalidArgumentException;
+use Closure;
+
+/**
+ * Callback prediction.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CallbackPrediction implements PredictionInterface
+{
+ private $callback;
+
+ /**
+ * Initializes callback prediction.
+ *
+ * @param callable $callback Custom callback
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function __construct($callback)
+ {
+ if (!is_callable($callback)) {
+ throw new InvalidArgumentException(sprintf(
+ 'Callable expected as an argument to CallbackPrediction, but got %s.',
+ gettype($callback)
+ ));
+ }
+
+ $this->callback = $callback;
+ }
+
+ /**
+ * Executes preset callback.
+ *
+ * @param Call[] $calls
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ */
+ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
+ {
+ $callback = $this->callback;
+
+ if ($callback instanceof Closure && method_exists('Closure', 'bind')) {
+ $callback = Closure::bind($callback, $object);
+ }
+
+ call_user_func($callback, $calls, $object, $method);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prediction;
+
+use Prophecy\Call\Call;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Util\StringUtil;
+use Prophecy\Exception\Prediction\UnexpectedCallsException;
+
+/**
+ * No calls prediction.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class NoCallsPrediction implements PredictionInterface
+{
+ private $util;
+
+ /**
+ * Initializes prediction.
+ *
+ * @param null|StringUtil $util
+ */
+ public function __construct(StringUtil $util = null)
+ {
+ $this->util = $util ?: new StringUtil;
+ }
+
+ /**
+ * Tests that there were no calls made.
+ *
+ * @param Call[] $calls
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException
+ */
+ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method)
+ {
+ if (!count($calls)) {
+ return;
+ }
+
+ $verb = count($calls) === 1 ? 'was' : 'were';
+
+ throw new UnexpectedCallsException(sprintf(
+ "No calls expected that match:\n".
+ " %s->%s(%s)\n".
+ "but %d %s made:\n%s",
+ get_class($object->reveal()),
+ $method->getMethodName(),
+ $method->getArgumentsWildcard(),
+ count($calls),
+ $verb,
+ $this->util->stringifyCalls($calls)
+ ), $method, $calls);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prediction;
+
+use Prophecy\Call\Call;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+
+/**
+ * Prediction interface.
+ * Predictions are logical test blocks, tied to `should...` keyword.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface PredictionInterface
+{
+ /**
+ * Tests that double fulfilled prediction.
+ *
+ * @param Call[] $calls
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @throws object
+ * @return void
+ */
+ public function check(array $calls, ObjectProphecy $object, MethodProphecy $method);
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Promise;
+
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Exception\InvalidArgumentException;
+use Closure;
+
+/**
+ * Callback promise.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class CallbackPromise implements PromiseInterface
+{
+ private $callback;
+
+ /**
+ * Initializes callback promise.
+ *
+ * @param callable $callback Custom callback
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function __construct($callback)
+ {
+ if (!is_callable($callback)) {
+ throw new InvalidArgumentException(sprintf(
+ 'Callable expected as an argument to CallbackPromise, but got %s.',
+ gettype($callback)
+ ));
+ }
+
+ $this->callback = $callback;
+ }
+
+ /**
+ * Evaluates promise callback.
+ *
+ * @param array $args
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @return mixed
+ */
+ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
+ {
+ $callback = $this->callback;
+
+ if ($callback instanceof Closure && method_exists('Closure', 'bind')) {
+ $callback = Closure::bind($callback, $object);
+ }
+
+ return call_user_func($callback, $args, $object, $method);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Promise;
+
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+
+/**
+ * Promise interface.
+ * Promises are logical blocks, tied to `will...` keyword.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface PromiseInterface
+{
+ /**
+ * Evaluates promise.
+ *
+ * @param array $args
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @return mixed
+ */
+ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method);
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Promise;
+
+use Prophecy\Exception\InvalidArgumentException;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+
+/**
+ * Return argument promise.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ReturnArgumentPromise implements PromiseInterface
+{
+ /**
+ * @var int
+ */
+ private $index;
+
+ /**
+ * Initializes callback promise.
+ *
+ * @param int $index The zero-indexed number of the argument to return
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function __construct($index = 0)
+ {
+ if (!is_int($index) || $index < 0) {
+ throw new InvalidArgumentException(sprintf(
+ 'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.',
+ $index
+ ));
+ }
+ $this->index = $index;
+ }
+
+ /**
+ * Returns nth argument if has one, null otherwise.
+ *
+ * @param array $args
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @return null|mixed
+ */
+ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
+ {
+ return count($args) > $this->index ? $args[$this->index] : null;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Promise;
+
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+
+/**
+ * Return promise.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ReturnPromise implements PromiseInterface
+{
+ private $returnValues = array();
+
+ /**
+ * Initializes promise.
+ *
+ * @param array $returnValues Array of values
+ */
+ public function __construct(array $returnValues)
+ {
+ $this->returnValues = $returnValues;
+ }
+
+ /**
+ * Returns saved values one by one until last one, then continuously returns last value.
+ *
+ * @param array $args
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @return mixed
+ */
+ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
+ {
+ $value = array_shift($this->returnValues);
+
+ if (!count($this->returnValues)) {
+ $this->returnValues[] = $value;
+ }
+
+ return $value;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Promise;
+
+use Doctrine\Instantiator\Instantiator;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\MethodProphecy;
+use Prophecy\Exception\InvalidArgumentException;
+use ReflectionClass;
+
+/**
+ * Throw promise.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ThrowPromise implements PromiseInterface
+{
+ private $exception;
+
+ /**
+ * @var \Doctrine\Instantiator\Instantiator
+ */
+ private $instantiator;
+
+ /**
+ * Initializes promise.
+ *
+ * @param string|\Exception|\Throwable $exception Exception class name or instance
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function __construct($exception)
+ {
+ if (is_string($exception)) {
+ if (!class_exists($exception) || !$this->isAValidThrowable($exception)) {
+ throw new InvalidArgumentException(sprintf(
+ 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.',
+ $exception
+ ));
+ }
+ } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) {
+ throw new InvalidArgumentException(sprintf(
+ 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.',
+ is_object($exception) ? get_class($exception) : gettype($exception)
+ ));
+ }
+
+ $this->exception = $exception;
+ }
+
+ /**
+ * Throws predefined exception.
+ *
+ * @param array $args
+ * @param ObjectProphecy $object
+ * @param MethodProphecy $method
+ *
+ * @throws object
+ */
+ public function execute(array $args, ObjectProphecy $object, MethodProphecy $method)
+ {
+ if (is_string($this->exception)) {
+ $classname = $this->exception;
+ $reflection = new ReflectionClass($classname);
+ $constructor = $reflection->getConstructor();
+
+ if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) {
+ throw $reflection->newInstance();
+ }
+
+ if (!$this->instantiator) {
+ $this->instantiator = new Instantiator();
+ }
+
+ throw $this->instantiator->instantiate($classname);
+ }
+
+ throw $this->exception;
+ }
+
+ /**
+ * @param string $exception
+ *
+ * @return bool
+ */
+ private function isAValidThrowable($exception)
+ {
+ return is_a($exception, 'Exception', true) || is_subclass_of($exception, 'Throwable', true);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prophecy;
+
+use Prophecy\Argument;
+use Prophecy\Prophet;
+use Prophecy\Promise;
+use Prophecy\Prediction;
+use Prophecy\Exception\Doubler\MethodNotFoundException;
+use Prophecy\Exception\InvalidArgumentException;
+use Prophecy\Exception\Prophecy\MethodProphecyException;
+
+/**
+ * Method prophecy.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class MethodProphecy
+{
+ private $objectProphecy;
+ private $methodName;
+ private $argumentsWildcard;
+ private $promise;
+ private $prediction;
+ private $checkedPredictions = array();
+ private $bound = false;
+ private $voidReturnType = false;
+
+ /**
+ * Initializes method prophecy.
+ *
+ * @param ObjectProphecy $objectProphecy
+ * @param string $methodName
+ * @param null|Argument\ArgumentsWildcard|array $arguments
+ *
+ * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found
+ */
+ public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null)
+ {
+ $double = $objectProphecy->reveal();
+ if (!method_exists($double, $methodName)) {
+ throw new MethodNotFoundException(sprintf(
+ 'Method `%s::%s()` is not defined.', get_class($double), $methodName
+ ), get_class($double), $methodName, $arguments);
+ }
+
+ $this->objectProphecy = $objectProphecy;
+ $this->methodName = $methodName;
+
+ $reflectedMethod = new \ReflectionMethod($double, $methodName);
+ if ($reflectedMethod->isFinal()) {
+ throw new MethodProphecyException(sprintf(
+ "Can not add prophecy for a method `%s::%s()`\n".
+ "as it is a final method.",
+ get_class($double),
+ $methodName
+ ), $this);
+ }
+
+ if (null !== $arguments) {
+ $this->withArguments($arguments);
+ }
+
+ if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) {
+ $type = (string) $reflectedMethod->getReturnType();
+
+ if ('void' === $type) {
+ $this->voidReturnType = true;
+ return;
+ }
+
+ $this->will(function () use ($type) {
+ switch ($type) {
+ case 'string': return '';
+ case 'float': return 0.0;
+ case 'int': return 0;
+ case 'bool': return false;
+ case 'array': return array();
+
+ case 'callable':
+ case 'Closure':
+ return function () {};
+
+ case 'Traversable':
+ case 'Generator':
+ // Remove eval() when minimum version >=5.5
+ /** @var callable $generator */
+ $generator = eval('return function () { yield; };');
+ return $generator();
+
+ default:
+ $prophet = new Prophet;
+ return $prophet->prophesize($type)->reveal();
+ }
+ });
+ }
+ }
+
+ /**
+ * Sets argument wildcard.
+ *
+ * @param array|Argument\ArgumentsWildcard $arguments
+ *
+ * @return $this
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function withArguments($arguments)
+ {
+ if (is_array($arguments)) {
+ $arguments = new Argument\ArgumentsWildcard($arguments);
+ }
+
+ if (!$arguments instanceof Argument\ArgumentsWildcard) {
+ throw new InvalidArgumentException(sprintf(
+ "Either an array or an instance of ArgumentsWildcard expected as\n".
+ 'a `MethodProphecy::withArguments()` argument, but got %s.',
+ gettype($arguments)
+ ));
+ }
+
+ $this->argumentsWildcard = $arguments;
+
+ return $this;
+ }
+
+ /**
+ * Sets custom promise to the prophecy.
+ *
+ * @param callable|Promise\PromiseInterface $promise
+ *
+ * @return $this
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function will($promise)
+ {
+ if (is_callable($promise)) {
+ $promise = new Promise\CallbackPromise($promise);
+ }
+
+ if (!$promise instanceof Promise\PromiseInterface) {
+ throw new InvalidArgumentException(sprintf(
+ 'Expected callable or instance of PromiseInterface, but got %s.',
+ gettype($promise)
+ ));
+ }
+
+ $this->bindToObjectProphecy();
+ $this->promise = $promise;
+
+ return $this;
+ }
+
+ /**
+ * Sets return promise to the prophecy.
+ *
+ * @see Prophecy\Promise\ReturnPromise
+ *
+ * @return $this
+ */
+ public function willReturn()
+ {
+ if ($this->voidReturnType) {
+ throw new MethodProphecyException(
+ "The method \"$this->methodName\" has a void return type, and so cannot return anything",
+ $this
+ );
+ }
+
+ return $this->will(new Promise\ReturnPromise(func_get_args()));
+ }
+
+ /**
+ * Sets return argument promise to the prophecy.
+ *
+ * @param int $index The zero-indexed number of the argument to return
+ *
+ * @see Prophecy\Promise\ReturnArgumentPromise
+ *
+ * @return $this
+ */
+ public function willReturnArgument($index = 0)
+ {
+ if ($this->voidReturnType) {
+ throw new MethodProphecyException("The method \"$this->methodName\" has a void return type", $this);
+ }
+
+ return $this->will(new Promise\ReturnArgumentPromise($index));
+ }
+
+ /**
+ * Sets throw promise to the prophecy.
+ *
+ * @see Prophecy\Promise\ThrowPromise
+ *
+ * @param string|\Exception $exception Exception class or instance
+ *
+ * @return $this
+ */
+ public function willThrow($exception)
+ {
+ return $this->will(new Promise\ThrowPromise($exception));
+ }
+
+ /**
+ * Sets custom prediction to the prophecy.
+ *
+ * @param callable|Prediction\PredictionInterface $prediction
+ *
+ * @return $this
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function should($prediction)
+ {
+ if (is_callable($prediction)) {
+ $prediction = new Prediction\CallbackPrediction($prediction);
+ }
+
+ if (!$prediction instanceof Prediction\PredictionInterface) {
+ throw new InvalidArgumentException(sprintf(
+ 'Expected callable or instance of PredictionInterface, but got %s.',
+ gettype($prediction)
+ ));
+ }
+
+ $this->bindToObjectProphecy();
+ $this->prediction = $prediction;
+
+ return $this;
+ }
+
+ /**
+ * Sets call prediction to the prophecy.
+ *
+ * @see Prophecy\Prediction\CallPrediction
+ *
+ * @return $this
+ */
+ public function shouldBeCalled()
+ {
+ return $this->should(new Prediction\CallPrediction);
+ }
+
+ /**
+ * Sets no calls prediction to the prophecy.
+ *
+ * @see Prophecy\Prediction\NoCallsPrediction
+ *
+ * @return $this
+ */
+ public function shouldNotBeCalled()
+ {
+ return $this->should(new Prediction\NoCallsPrediction);
+ }
+
+ /**
+ * Sets call times prediction to the prophecy.
+ *
+ * @see Prophecy\Prediction\CallTimesPrediction
+ *
+ * @param $count
+ *
+ * @return $this
+ */
+ public function shouldBeCalledTimes($count)
+ {
+ return $this->should(new Prediction\CallTimesPrediction($count));
+ }
+
+ /**
+ * Checks provided prediction immediately.
+ *
+ * @param callable|Prediction\PredictionInterface $prediction
+ *
+ * @return $this
+ *
+ * @throws \Prophecy\Exception\InvalidArgumentException
+ */
+ public function shouldHave($prediction)
+ {
+ if (is_callable($prediction)) {
+ $prediction = new Prediction\CallbackPrediction($prediction);
+ }
+
+ if (!$prediction instanceof Prediction\PredictionInterface) {
+ throw new InvalidArgumentException(sprintf(
+ 'Expected callable or instance of PredictionInterface, but got %s.',
+ gettype($prediction)
+ ));
+ }
+
+ if (null === $this->promise && !$this->voidReturnType) {
+ $this->willReturn();
+ }
+
+ $calls = $this->getObjectProphecy()->findProphecyMethodCalls(
+ $this->getMethodName(),
+ $this->getArgumentsWildcard()
+ );
+
+ try {
+ $prediction->check($calls, $this->getObjectProphecy(), $this);
+ $this->checkedPredictions[] = $prediction;
+ } catch (\Exception $e) {
+ $this->checkedPredictions[] = $prediction;
+
+ throw $e;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Checks call prediction.
+ *
+ * @see Prophecy\Prediction\CallPrediction
+ *
+ * @return $this
+ */
+ public function shouldHaveBeenCalled()
+ {
+ return $this->shouldHave(new Prediction\CallPrediction);
+ }
+
+ /**
+ * Checks no calls prediction.
+ *
+ * @see Prophecy\Prediction\NoCallsPrediction
+ *
+ * @return $this
+ */
+ public function shouldNotHaveBeenCalled()
+ {
+ return $this->shouldHave(new Prediction\NoCallsPrediction);
+ }
+
+ /**
+ * Checks no calls prediction.
+ *
+ * @see Prophecy\Prediction\NoCallsPrediction
+ * @deprecated
+ *
+ * @return $this
+ */
+ public function shouldNotBeenCalled()
+ {
+ return $this->shouldNotHaveBeenCalled();
+ }
+
+ /**
+ * Checks call times prediction.
+ *
+ * @see Prophecy\Prediction\CallTimesPrediction
+ *
+ * @param int $count
+ *
+ * @return $this
+ */
+ public function shouldHaveBeenCalledTimes($count)
+ {
+ return $this->shouldHave(new Prediction\CallTimesPrediction($count));
+ }
+
+ /**
+ * Checks currently registered [with should(...)] prediction.
+ */
+ public function checkPrediction()
+ {
+ if (null === $this->prediction) {
+ return;
+ }
+
+ $this->shouldHave($this->prediction);
+ }
+
+ /**
+ * Returns currently registered promise.
+ *
+ * @return null|Promise\PromiseInterface
+ */
+ public function getPromise()
+ {
+ return $this->promise;
+ }
+
+ /**
+ * Returns currently registered prediction.
+ *
+ * @return null|Prediction\PredictionInterface
+ */
+ public function getPrediction()
+ {
+ return $this->prediction;
+ }
+
+ /**
+ * Returns predictions that were checked on this object.
+ *
+ * @return Prediction\PredictionInterface[]
+ */
+ public function getCheckedPredictions()
+ {
+ return $this->checkedPredictions;
+ }
+
+ /**
+ * Returns object prophecy this method prophecy is tied to.
+ *
+ * @return ObjectProphecy
+ */
+ public function getObjectProphecy()
+ {
+ return $this->objectProphecy;
+ }
+
+ /**
+ * Returns method name.
+ *
+ * @return string
+ */
+ public function getMethodName()
+ {
+ return $this->methodName;
+ }
+
+ /**
+ * Returns arguments wildcard.
+ *
+ * @return Argument\ArgumentsWildcard
+ */
+ public function getArgumentsWildcard()
+ {
+ return $this->argumentsWildcard;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasReturnVoid()
+ {
+ return $this->voidReturnType;
+ }
+
+ private function bindToObjectProphecy()
+ {
+ if ($this->bound) {
+ return;
+ }
+
+ $this->getObjectProphecy()->addMethodProphecy($this);
+ $this->bound = true;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prophecy;
+
+use SebastianBergmann\Comparator\ComparisonFailure;
+use Prophecy\Comparator\Factory as ComparatorFactory;
+use Prophecy\Call\Call;
+use Prophecy\Doubler\LazyDouble;
+use Prophecy\Argument\ArgumentsWildcard;
+use Prophecy\Call\CallCenter;
+use Prophecy\Exception\Prophecy\ObjectProphecyException;
+use Prophecy\Exception\Prophecy\MethodProphecyException;
+use Prophecy\Exception\Prediction\AggregateException;
+use Prophecy\Exception\Prediction\PredictionException;
+
+/**
+ * Object prophecy.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class ObjectProphecy implements ProphecyInterface
+{
+ private $lazyDouble;
+ private $callCenter;
+ private $revealer;
+ private $comparatorFactory;
+
+ /**
+ * @var MethodProphecy[][]
+ */
+ private $methodProphecies = array();
+
+ /**
+ * Initializes object prophecy.
+ *
+ * @param LazyDouble $lazyDouble
+ * @param CallCenter $callCenter
+ * @param RevealerInterface $revealer
+ * @param ComparatorFactory $comparatorFactory
+ */
+ public function __construct(
+ LazyDouble $lazyDouble,
+ CallCenter $callCenter = null,
+ RevealerInterface $revealer = null,
+ ComparatorFactory $comparatorFactory = null
+ ) {
+ $this->lazyDouble = $lazyDouble;
+ $this->callCenter = $callCenter ?: new CallCenter;
+ $this->revealer = $revealer ?: new Revealer;
+
+ $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance();
+ }
+
+ /**
+ * Forces double to extend specific class.
+ *
+ * @param string $class
+ *
+ * @return $this
+ */
+ public function willExtend($class)
+ {
+ $this->lazyDouble->setParentClass($class);
+
+ return $this;
+ }
+
+ /**
+ * Forces double to implement specific interface.
+ *
+ * @param string $interface
+ *
+ * @return $this
+ */
+ public function willImplement($interface)
+ {
+ $this->lazyDouble->addInterface($interface);
+
+ return $this;
+ }
+
+ /**
+ * Sets constructor arguments.
+ *
+ * @param array $arguments
+ *
+ * @return $this
+ */
+ public function willBeConstructedWith(array $arguments = null)
+ {
+ $this->lazyDouble->setArguments($arguments);
+
+ return $this;
+ }
+
+ /**
+ * Reveals double.
+ *
+ * @return object
+ *
+ * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface
+ */
+ public function reveal()
+ {
+ $double = $this->lazyDouble->getInstance();
+
+ if (null === $double || !$double instanceof ProphecySubjectInterface) {
+ throw new ObjectProphecyException(
+ "Generated double must implement ProphecySubjectInterface, but it does not.\n".
+ 'It seems you have wrongly configured doubler without required ClassPatch.',
+ $this
+ );
+ }
+
+ $double->setProphecy($this);
+
+ return $double;
+ }
+
+ /**
+ * Adds method prophecy to object prophecy.
+ *
+ * @param MethodProphecy $methodProphecy
+ *
+ * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't
+ * have arguments wildcard
+ */
+ public function addMethodProphecy(MethodProphecy $methodProphecy)
+ {
+ $argumentsWildcard = $methodProphecy->getArgumentsWildcard();
+ if (null === $argumentsWildcard) {
+ throw new MethodProphecyException(sprintf(
+ "Can not add prophecy for a method `%s::%s()`\n".
+ "as you did not specify arguments wildcard for it.",
+ get_class($this->reveal()),
+ $methodProphecy->getMethodName()
+ ), $methodProphecy);
+ }
+
+ $methodName = $methodProphecy->getMethodName();
+
+ if (!isset($this->methodProphecies[$methodName])) {
+ $this->methodProphecies[$methodName] = array();
+ }
+
+ $this->methodProphecies[$methodName][] = $methodProphecy;
+ }
+
+ /**
+ * Returns either all or related to single method prophecies.
+ *
+ * @param null|string $methodName
+ *
+ * @return MethodProphecy[]
+ */
+ public function getMethodProphecies($methodName = null)
+ {
+ if (null === $methodName) {
+ return $this->methodProphecies;
+ }
+
+ if (!isset($this->methodProphecies[$methodName])) {
+ return array();
+ }
+
+ return $this->methodProphecies[$methodName];
+ }
+
+ /**
+ * Makes specific method call.
+ *
+ * @param string $methodName
+ * @param array $arguments
+ *
+ * @return mixed
+ */
+ public function makeProphecyMethodCall($methodName, array $arguments)
+ {
+ $arguments = $this->revealer->reveal($arguments);
+ $return = $this->callCenter->makeCall($this, $methodName, $arguments);
+
+ return $this->revealer->reveal($return);
+ }
+
+ /**
+ * Finds calls by method name & arguments wildcard.
+ *
+ * @param string $methodName
+ * @param ArgumentsWildcard $wildcard
+ *
+ * @return Call[]
+ */
+ public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard)
+ {
+ return $this->callCenter->findCalls($methodName, $wildcard);
+ }
+
+ /**
+ * Checks that registered method predictions do not fail.
+ *
+ * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail
+ */
+ public function checkProphecyMethodsPredictions()
+ {
+ $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal())));
+ $exception->setObjectProphecy($this);
+
+ foreach ($this->methodProphecies as $prophecies) {
+ foreach ($prophecies as $prophecy) {
+ try {
+ $prophecy->checkPrediction();
+ } catch (PredictionException $e) {
+ $exception->append($e);
+ }
+ }
+ }
+
+ if (count($exception->getExceptions())) {
+ throw $exception;
+ }
+ }
+
+ /**
+ * Creates new method prophecy using specified method name and arguments.
+ *
+ * @param string $methodName
+ * @param array $arguments
+ *
+ * @return MethodProphecy
+ */
+ public function __call($methodName, array $arguments)
+ {
+ $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments));
+
+ foreach ($this->getMethodProphecies($methodName) as $prophecy) {
+ $argumentsWildcard = $prophecy->getArgumentsWildcard();
+ $comparator = $this->comparatorFactory->getComparatorFor(
+ $argumentsWildcard, $arguments
+ );
+
+ try {
+ $comparator->assertEquals($argumentsWildcard, $arguments);
+ return $prophecy;
+ } catch (ComparisonFailure $failure) {}
+ }
+
+ return new MethodProphecy($this, $methodName, $arguments);
+ }
+
+ /**
+ * Tries to get property value from double.
+ *
+ * @param string $name
+ *
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return $this->reveal()->$name;
+ }
+
+ /**
+ * Tries to set property value to double.
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function __set($name, $value)
+ {
+ $this->reveal()->$name = $this->revealer->reveal($value);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prophecy;
+
+/**
+ * Core Prophecy interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface ProphecyInterface
+{
+ /**
+ * Reveals prophecy object (double) .
+ *
+ * @return object
+ */
+ public function reveal();
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prophecy;
+
+/**
+ * Controllable doubles interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface ProphecySubjectInterface
+{
+ /**
+ * Sets subject prophecy.
+ *
+ * @param ProphecyInterface $prophecy
+ */
+ public function setProphecy(ProphecyInterface $prophecy);
+
+ /**
+ * Returns subject prophecy.
+ *
+ * @return ProphecyInterface
+ */
+ public function getProphecy();
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prophecy;
+
+/**
+ * Basic prophecies revealer.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Revealer implements RevealerInterface
+{
+ /**
+ * Unwraps value(s).
+ *
+ * @param mixed $value
+ *
+ * @return mixed
+ */
+ public function reveal($value)
+ {
+ if (is_array($value)) {
+ return array_map(array($this, __FUNCTION__), $value);
+ }
+
+ if (!is_object($value)) {
+ return $value;
+ }
+
+ if ($value instanceof ProphecyInterface) {
+ $value = $value->reveal();
+ }
+
+ return $value;
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Prophecy;
+
+/**
+ * Prophecies revealer interface.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+interface RevealerInterface
+{
+ /**
+ * Unwraps value(s).
+ *
+ * @param mixed $value
+ *
+ * @return mixed
+ */
+ public function reveal($value);
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy;
+
+use Prophecy\Doubler\Doubler;
+use Prophecy\Doubler\LazyDouble;
+use Prophecy\Doubler\ClassPatch;
+use Prophecy\Prophecy\ObjectProphecy;
+use Prophecy\Prophecy\RevealerInterface;
+use Prophecy\Prophecy\Revealer;
+use Prophecy\Call\CallCenter;
+use Prophecy\Util\StringUtil;
+use Prophecy\Exception\Prediction\PredictionException;
+use Prophecy\Exception\Prediction\AggregateException;
+
+/**
+ * Prophet creates prophecies.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class Prophet
+{
+ private $doubler;
+ private $revealer;
+ private $util;
+
+ /**
+ * @var ObjectProphecy[]
+ */
+ private $prophecies = array();
+
+ /**
+ * Initializes Prophet.
+ *
+ * @param null|Doubler $doubler
+ * @param null|RevealerInterface $revealer
+ * @param null|StringUtil $util
+ */
+ public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null,
+ StringUtil $util = null)
+ {
+ if (null === $doubler) {
+ $doubler = new Doubler;
+ $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch);
+ $doubler->registerClassPatch(new ClassPatch\TraversablePatch);
+ $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch);
+ $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch);
+ $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch);
+ $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch());
+ $doubler->registerClassPatch(new ClassPatch\MagicCallPatch);
+ $doubler->registerClassPatch(new ClassPatch\KeywordPatch);
+ }
+
+ $this->doubler = $doubler;
+ $this->revealer = $revealer ?: new Revealer;
+ $this->util = $util ?: new StringUtil;
+ }
+
+ /**
+ * Creates new object prophecy.
+ *
+ * @param null|string $classOrInterface Class or interface name
+ *
+ * @return ObjectProphecy
+ */
+ public function prophesize($classOrInterface = null)
+ {
+ $this->prophecies[] = $prophecy = new ObjectProphecy(
+ new LazyDouble($this->doubler),
+ new CallCenter($this->util),
+ $this->revealer
+ );
+
+ if ($classOrInterface && class_exists($classOrInterface)) {
+ return $prophecy->willExtend($classOrInterface);
+ }
+
+ if ($classOrInterface && interface_exists($classOrInterface)) {
+ return $prophecy->willImplement($classOrInterface);
+ }
+
+ return $prophecy;
+ }
+
+ /**
+ * Returns all created object prophecies.
+ *
+ * @return ObjectProphecy[]
+ */
+ public function getProphecies()
+ {
+ return $this->prophecies;
+ }
+
+ /**
+ * Returns Doubler instance assigned to this Prophet.
+ *
+ * @return Doubler
+ */
+ public function getDoubler()
+ {
+ return $this->doubler;
+ }
+
+ /**
+ * Checks all predictions defined by prophecies of this Prophet.
+ *
+ * @throws Exception\Prediction\AggregateException If any prediction fails
+ */
+ public function checkPredictions()
+ {
+ $exception = new AggregateException("Some predictions failed:\n");
+ foreach ($this->prophecies as $prophecy) {
+ try {
+ $prophecy->checkProphecyMethodsPredictions();
+ } catch (PredictionException $e) {
+ $exception->append($e);
+ }
+ }
+
+ if (count($exception->getExceptions())) {
+ throw $exception;
+ }
+ }
+}
+<?php
+
+namespace Prophecy\Util;
+
+use Prophecy\Prophecy\ProphecyInterface;
+use SebastianBergmann\RecursionContext\Context;
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This class is a modification from sebastianbergmann/exporter
+ * @see https://github.com/sebastianbergmann/exporter
+ */
+class ExportUtil
+{
+ /**
+ * Exports a value as a string
+ *
+ * The output of this method is similar to the output of print_r(), but
+ * improved in various aspects:
+ *
+ * - NULL is rendered as "null" (instead of "")
+ * - TRUE is rendered as "true" (instead of "1")
+ * - FALSE is rendered as "false" (instead of "")
+ * - Strings are always quoted with single quotes
+ * - Carriage returns and newlines are normalized to \n
+ * - Recursion and repeated rendering is treated properly
+ *
+ * @param mixed $value
+ * @param int $indentation The indentation level of the 2nd+ line
+ * @return string
+ */
+ public static function export($value, $indentation = 0)
+ {
+ return self::recursiveExport($value, $indentation);
+ }
+
+ /**
+ * Converts an object to an array containing all of its private, protected
+ * and public properties.
+ *
+ * @param mixed $value
+ * @return array
+ */
+ public static function toArray($value)
+ {
+ if (!is_object($value)) {
+ return (array) $value;
+ }
+
+ $array = array();
+
+ foreach ((array) $value as $key => $val) {
+ // properties are transformed to keys in the following way:
+ // private $property => "\0Classname\0property"
+ // protected $property => "\0*\0property"
+ // public $property => "property"
+ if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) {
+ $key = $matches[1];
+ }
+
+ // See https://github.com/php/php-src/commit/5721132
+ if ($key === "\0gcdata") {
+ continue;
+ }
+
+ $array[$key] = $val;
+ }
+
+ // Some internal classes like SplObjectStorage don't work with the
+ // above (fast) mechanism nor with reflection in Zend.
+ // Format the output similarly to print_r() in this case
+ if ($value instanceof \SplObjectStorage) {
+ // However, the fast method does work in HHVM, and exposes the
+ // internal implementation. Hide it again.
+ if (property_exists('\SplObjectStorage', '__storage')) {
+ unset($array['__storage']);
+ } elseif (property_exists('\SplObjectStorage', 'storage')) {
+ unset($array['storage']);
+ }
+
+ if (property_exists('\SplObjectStorage', '__key')) {
+ unset($array['__key']);
+ }
+
+ foreach ($value as $key => $val) {
+ $array[spl_object_hash($val)] = array(
+ 'obj' => $val,
+ 'inf' => $value->getInfo(),
+ );
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Recursive implementation of export
+ *
+ * @param mixed $value The value to export
+ * @param int $indentation The indentation level of the 2nd+ line
+ * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects
+ * @return string
+ * @see SebastianBergmann\Exporter\Exporter::export
+ */
+ protected static function recursiveExport(&$value, $indentation, $processed = null)
+ {
+ if ($value === null) {
+ return 'null';
+ }
+
+ if ($value === true) {
+ return 'true';
+ }
+
+ if ($value === false) {
+ return 'false';
+ }
+
+ if (is_float($value) && floatval(intval($value)) === $value) {
+ return "$value.0";
+ }
+
+ if (is_resource($value)) {
+ return sprintf(
+ 'resource(%d) of type (%s)',
+ $value,
+ get_resource_type($value)
+ );
+ }
+
+ if (is_string($value)) {
+ // Match for most non printable chars somewhat taking multibyte chars into account
+ if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) {
+ return 'Binary String: 0x' . bin2hex($value);
+ }
+
+ return "'" .
+ str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) .
+ "'";
+ }
+
+ $whitespace = str_repeat(' ', 4 * $indentation);
+
+ if (!$processed) {
+ $processed = new Context;
+ }
+
+ if (is_array($value)) {
+ if (($key = $processed->contains($value)) !== false) {
+ return 'Array &' . $key;
+ }
+
+ $array = $value;
+ $key = $processed->add($value);
+ $values = '';
+
+ if (count($array) > 0) {
+ foreach ($array as $k => $v) {
+ $values .= sprintf(
+ '%s %s => %s' . "\n",
+ $whitespace,
+ self::recursiveExport($k, $indentation),
+ self::recursiveExport($value[$k], $indentation + 1, $processed)
+ );
+ }
+
+ $values = "\n" . $values . $whitespace;
+ }
+
+ return sprintf('Array &%s (%s)', $key, $values);
+ }
+
+ if (is_object($value)) {
+ $class = get_class($value);
+
+ if ($value instanceof ProphecyInterface) {
+ return sprintf('%s Object (*Prophecy*)', $class);
+ } elseif ($hash = $processed->contains($value)) {
+ return sprintf('%s:%s Object', $class, $hash);
+ }
+
+ $hash = $processed->add($value);
+ $values = '';
+ $array = self::toArray($value);
+
+ if (count($array) > 0) {
+ foreach ($array as $k => $v) {
+ $values .= sprintf(
+ '%s %s => %s' . "\n",
+ $whitespace,
+ self::recursiveExport($k, $indentation),
+ self::recursiveExport($v, $indentation + 1, $processed)
+ );
+ }
+
+ $values = "\n" . $values . $whitespace;
+ }
+
+ return sprintf('%s:%s Object (%s)', $class, $hash, $values);
+ }
+
+ return var_export($value, true);
+ }
+}
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ * Marcello Duarte <marcello.duarte@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Prophecy\Util;
+
+use Prophecy\Call\Call;
+
+/**
+ * String utility.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+class StringUtil
+{
+ /**
+ * Stringifies any provided value.
+ *
+ * @param mixed $value
+ * @param boolean $exportObject
+ *
+ * @return string
+ */
+ public function stringify($value, $exportObject = true)
+ {
+ if (is_array($value)) {
+ if (range(0, count($value) - 1) === array_keys($value)) {
+ return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']';
+ }
+
+ $stringify = array($this, __FUNCTION__);
+
+ return '['.implode(', ', array_map(function ($item, $key) use ($stringify) {
+ return (is_integer($key) ? $key : '"'.$key.'"').
+ ' => '.call_user_func($stringify, $item);
+ }, $value, array_keys($value))).']';
+ }
+ if (is_resource($value)) {
+ return get_resource_type($value).':'.$value;
+ }
+ if (is_object($value)) {
+ return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value));
+ }
+ if (true === $value || false === $value) {
+ return $value ? 'true' : 'false';
+ }
+ if (is_string($value)) {
+ $str = sprintf('"%s"', str_replace("\n", '\\n', $value));
+
+ if (50 <= strlen($str)) {
+ return substr($str, 0, 50).'"...';
+ }
+
+ return $str;
+ }
+ if (null === $value) {
+ return 'null';
+ }
+
+ return (string) $value;
+ }
+
+ /**
+ * Stringifies provided array of calls.
+ *
+ * @param Call[] $calls Array of Call instances
+ *
+ * @return string
+ */
+ public function stringifyCalls(array $calls)
+ {
+ $self = $this;
+
+ return implode(PHP_EOL, array_map(function (Call $call) use ($self) {
+ return sprintf(' - %s(%s) @ %s',
+ $call->getMethodName(),
+ implode(', ', array_map(array($self, 'stringify'), $call->getArguments())),
+ str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace())
+ );
+ }, $calls));
+ }
+}
+The MIT License (MIT)
+
+Copyright (c) 2013 My C-Sense
+
+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.
+<?php
+
+namespace DeepCopy;
+
+use DeepCopy\Exception\CloneException;
+use DeepCopy\Filter\Filter;
+use DeepCopy\Matcher\Matcher;
+use DeepCopy\TypeFilter\Spl\SplDoublyLinkedList;
+use DeepCopy\TypeFilter\TypeFilter;
+use DeepCopy\TypeMatcher\TypeMatcher;
+use ReflectionProperty;
+use DeepCopy\Reflection\ReflectionHelper;
+
+/**
+ * DeepCopy
+ */
+class DeepCopy
+{
+ /**
+ * @var array
+ */
+ private $hashMap = [];
+
+ /**
+ * Filters to apply.
+ * @var array
+ */
+ private $filters = [];
+
+ /**
+ * Type Filters to apply.
+ * @var array
+ */
+ private $typeFilters = [];
+
+ private $skipUncloneable = false;
+
+ /**
+ * @var bool
+ */
+ private $useCloneMethod;
+
+ /**
+ * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used
+ * instead of the regular deep cloning.
+ */
+ public function __construct($useCloneMethod = false)
+ {
+ $this->useCloneMethod = $useCloneMethod;
+
+ $this->addTypeFilter(new SplDoublyLinkedList($this), new TypeMatcher('\SplDoublyLinkedList'));
+ }
+
+ /**
+ * Cloning uncloneable properties won't throw exception.
+ * @param $skipUncloneable
+ * @return $this
+ */
+ public function skipUncloneable($skipUncloneable = true)
+ {
+ $this->skipUncloneable = $skipUncloneable;
+ return $this;
+ }
+
+ /**
+ * Perform a deep copy of the object.
+ * @param mixed $object
+ * @return mixed
+ */
+ public function copy($object)
+ {
+ $this->hashMap = [];
+
+ return $this->recursiveCopy($object);
+ }
+
+ public function addFilter(Filter $filter, Matcher $matcher)
+ {
+ $this->filters[] = [
+ 'matcher' => $matcher,
+ 'filter' => $filter,
+ ];
+ }
+
+ public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
+ {
+ $this->typeFilters[] = [
+ 'matcher' => $matcher,
+ 'filter' => $filter,
+ ];
+ }
+
+
+ private function recursiveCopy($var)
+ {
+ // Matches Type Filter
+ if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) {
+ return $filter->apply($var);
+ }
+
+ // Resource
+ if (is_resource($var)) {
+ return $var;
+ }
+ // Array
+ if (is_array($var)) {
+ return $this->copyArray($var);
+ }
+ // Scalar
+ if (! is_object($var)) {
+ return $var;
+ }
+ // Object
+ return $this->copyObject($var);
+ }
+
+ /**
+ * Copy an array
+ * @param array $array
+ * @return array
+ */
+ private function copyArray(array $array)
+ {
+ foreach ($array as $key => $value) {
+ $array[$key] = $this->recursiveCopy($value);
+ }
+
+ return $array;
+ }
+
+ /**
+ * Copy an object
+ * @param object $object
+ * @return object
+ */
+ private function copyObject($object)
+ {
+ $objectHash = spl_object_hash($object);
+
+ if (isset($this->hashMap[$objectHash])) {
+ return $this->hashMap[$objectHash];
+ }
+
+ $reflectedObject = new \ReflectionObject($object);
+
+ if (false === $isCloneable = $reflectedObject->isCloneable() and $this->skipUncloneable) {
+ $this->hashMap[$objectHash] = $object;
+ return $object;
+ }
+
+ if (false === $isCloneable) {
+ throw new CloneException(sprintf(
+ 'Class "%s" is not cloneable.',
+ $reflectedObject->getName()
+ ));
+ }
+
+ $newObject = clone $object;
+ $this->hashMap[$objectHash] = $newObject;
+ if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) {
+ return $object;
+ }
+
+ if ($newObject instanceof \DateTimeInterface) {
+ return $newObject;
+ }
+ foreach (ReflectionHelper::getProperties($reflectedObject) as $property) {
+ $this->copyObjectProperty($newObject, $property);
+ }
+
+ return $newObject;
+ }
+
+ private function copyObjectProperty($object, ReflectionProperty $property)
+ {
+ // Ignore static properties
+ if ($property->isStatic()) {
+ return;
+ }
+
+ // Apply the filters
+ foreach ($this->filters as $item) {
+ /** @var Matcher $matcher */
+ $matcher = $item['matcher'];
+ /** @var Filter $filter */
+ $filter = $item['filter'];
+
+ if ($matcher->matches($object, $property->getName())) {
+ $filter->apply(
+ $object,
+ $property->getName(),
+ function ($object) {
+ return $this->recursiveCopy($object);
+ }
+ );
+ // If a filter matches, we stop processing this property
+ return;
+ }
+ }
+
+ $property->setAccessible(true);
+ $propertyValue = $property->getValue($object);
+
+ // Copy the property
+ $property->setValue($object, $this->recursiveCopy($propertyValue));
+ }
+
+ /**
+ * Returns first filter that matches variable, NULL if no such filter found.
+ * @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and
+ * 'matcher' with value of type {@see TypeMatcher}
+ * @param mixed $var
+ * @return TypeFilter|null
+ */
+ private function getFirstMatchedTypeFilter(array $filterRecords, $var)
+ {
+ $matched = $this->first(
+ $filterRecords,
+ function (array $record) use ($var) {
+ /* @var TypeMatcher $matcher */
+ $matcher = $record['matcher'];
+
+ return $matcher->matches($var);
+ }
+ );
+
+ return isset($matched) ? $matched['filter'] : null;
+ }
+
+ /**
+ * Returns first element that matches predicate, NULL if no such element found.
+ * @param array $elements
+ * @param callable $predicate Predicate arguments are: element.
+ * @return mixed|null
+ */
+ private function first(array $elements, callable $predicate)
+ {
+ foreach ($elements as $element) {
+ if (call_user_func($predicate, $element)) {
+ return $element;
+ }
+ }
+
+ return null;
+ }
+}
+<?php
+namespace DeepCopy\Exception;
+
+class CloneException extends \UnexpectedValueException
+{
+} <?php
+
+namespace DeepCopy\Filter\Doctrine;
+
+use DeepCopy\Filter\Filter;
+use ReflectionProperty;
+
+/**
+ * Set a null value for a property
+ */
+class DoctrineCollectionFilter implements Filter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = new ReflectionProperty($object, $property);
+
+ $reflectionProperty->setAccessible(true);
+ $oldCollection = $reflectionProperty->getValue($object);
+
+ $newCollection = $oldCollection->map(
+ function ($item) use ($objectCopier) {
+ return $objectCopier($item);
+ }
+ );
+
+ $reflectionProperty->setValue($object, $newCollection);
+ }
+}
+<?php
+
+namespace DeepCopy\Filter\Doctrine;
+
+use DeepCopy\Filter\Filter;
+use Doctrine\Common\Collections\ArrayCollection;
+
+class DoctrineEmptyCollectionFilter implements Filter
+{
+ /**
+ * Apply the filter to the object.
+ *
+ * @param object $object
+ * @param string $property
+ * @param callable $objectCopier
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = new \ReflectionProperty($object, $property);
+ $reflectionProperty->setAccessible(true);
+
+ $reflectionProperty->setValue($object, new ArrayCollection());
+ }
+} <?php
+
+namespace DeepCopy\Filter\Doctrine;
+
+use DeepCopy\Filter\Filter;
+
+/**
+ * Trigger the magic method __load() on a Doctrine Proxy class to load the
+ * actual entity from the database.
+ */
+class DoctrineProxyFilter implements Filter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $object->__load();
+ }
+}
+<?php
+
+namespace DeepCopy\Filter;
+
+/**
+ * Filter to apply to a property while copying an object
+ */
+interface Filter
+{
+ /**
+ * Apply the filter to the object.
+ * @param object $object
+ * @param string $property
+ * @param callable $objectCopier
+ */
+ public function apply($object, $property, $objectCopier);
+}
+<?php
+
+namespace DeepCopy\Filter;
+
+/**
+ * Keep the value of a property
+ */
+class KeepFilter implements Filter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ // Nothing to do
+ }
+}
+<?php
+
+namespace DeepCopy\Filter;
+
+/**
+ * Replace the value of a property
+ */
+class ReplaceFilter implements Filter
+{
+ /**
+ * @var callable
+ */
+ protected $callback;
+
+ /**
+ * @param callable $callable Will be called to get the new value for each property to replace
+ */
+ public function __construct(callable $callable)
+ {
+ $this->callback = $callable;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = new \ReflectionProperty($object, $property);
+ $reflectionProperty->setAccessible(true);
+
+ $value = call_user_func($this->callback, $reflectionProperty->getValue($object));
+
+ $reflectionProperty->setValue($object, $value);
+ }
+}
+<?php
+
+namespace DeepCopy\Filter;
+
+use ReflectionProperty;
+
+/**
+ * Set a null value for a property
+ */
+class SetNullFilter implements Filter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($object, $property, $objectCopier)
+ {
+ $reflectionProperty = new ReflectionProperty($object, $property);
+
+ $reflectionProperty->setAccessible(true);
+ $reflectionProperty->setValue($object, null);
+ }
+}
+<?php
+
+namespace DeepCopy\Matcher\Doctrine;
+
+use DeepCopy\Matcher\Matcher;
+use Doctrine\Common\Persistence\Proxy;
+
+/**
+ * Match a Doctrine Proxy class.
+ */
+class DoctrineProxyMatcher implements Matcher
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ return $object instanceof Proxy;
+ }
+}
+<?php
+
+namespace DeepCopy\Matcher;
+
+/**
+ * Matcher interface
+ */
+interface Matcher
+{
+ /**
+ * @param object $object
+ * @param string $property
+ * @return boolean
+ */
+ public function matches($object, $property);
+}
+<?php
+
+namespace DeepCopy\Matcher;
+
+/**
+ * Match a specific property of a specific class
+ */
+class PropertyMatcher implements Matcher
+{
+ /**
+ * @var string
+ */
+ private $class;
+
+ /**
+ * @var string
+ */
+ private $property;
+
+ /**
+ * @param string $class Class name
+ * @param string $property Property name
+ */
+ public function __construct($class, $property)
+ {
+ $this->class = $class;
+ $this->property = $property;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ return ($object instanceof $this->class) && ($property == $this->property);
+ }
+}
+<?php
+
+namespace DeepCopy\Matcher;
+
+/**
+ * Match a property by its name
+ */
+class PropertyNameMatcher implements Matcher
+{
+ /**
+ * @var string
+ */
+ private $property;
+
+ /**
+ * @param string $property Property name
+ */
+ public function __construct($property)
+ {
+ $this->property = $property;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ return $property == $this->property;
+ }
+}
+<?php
+
+namespace DeepCopy\Matcher;
+
+use ReflectionProperty;
+
+/**
+ * Match a property by its type
+ *
+ * It is recommended to use {@see DeepCopy\TypeFilter\TypeFilter} instead, as it applies on all occurrences
+ * of given type in copied context (eg. array elements), not just on object properties.
+ */
+class PropertyTypeMatcher implements Matcher
+{
+ /**
+ * @var string
+ */
+ private $propertyType;
+
+ /**
+ * @param string $propertyType Property type
+ */
+ public function __construct($propertyType)
+ {
+ $this->propertyType = $propertyType;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches($object, $property)
+ {
+ $reflectionProperty = new ReflectionProperty($object, $property);
+ $reflectionProperty->setAccessible(true);
+
+ return $reflectionProperty->getValue($object) instanceof $this->propertyType;
+ }
+}
+<?php
+
+namespace DeepCopy\Reflection;
+
+class ReflectionHelper
+{
+ /**
+ * Retrieves all properties (including private ones), from object and all its ancestors.
+ *
+ * Standard \ReflectionClass->getProperties() does not return private properties from ancestor classes.
+ *
+ * @author muratyaman@gmail.com
+ * @see http://php.net/manual/en/reflectionclass.getproperties.php
+ *
+ * @param \ReflectionClass $ref
+ * @return \ReflectionProperty[]
+ */
+ public static function getProperties(\ReflectionClass $ref)
+ {
+ $props = $ref->getProperties();
+ $propsArr = array();
+
+ foreach ($props as $prop) {
+ $propertyName = $prop->getName();
+ $propsArr[$propertyName] = $prop;
+ }
+
+ if ($parentClass = $ref->getParentClass()) {
+ $parentPropsArr = self::getProperties($parentClass);
+ foreach ($propsArr as $key => $property) {
+ $parentPropsArr[$key] = $property;
+ }
+
+ return $parentPropsArr;
+ }
+
+ return $propsArr;
+ }
+}
+<?php
+
+namespace DeepCopy\TypeFilter;
+
+class ReplaceFilter implements TypeFilter
+{
+ /**
+ * @var callable
+ */
+ protected $callback;
+
+ /**
+ * @param callable $callable Will be called to get the new value for each element to replace
+ */
+ public function __construct(callable $callable)
+ {
+ $this->callback = $callable;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($element)
+ {
+ return call_user_func($this->callback, $element);
+ }
+}
+<?php
+
+namespace DeepCopy\TypeFilter;
+
+class ShallowCopyFilter implements TypeFilter
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($element)
+ {
+ return clone $element;
+ }
+}
+<?php
+
+namespace DeepCopy\TypeFilter\Spl;
+
+use DeepCopy\DeepCopy;
+use DeepCopy\TypeFilter\TypeFilter;
+
+class SplDoublyLinkedList implements TypeFilter
+{
+ /**
+ * @var DeepCopy
+ */
+ private $deepCopy;
+
+ public function __construct(DeepCopy $deepCopy)
+ {
+ $this->deepCopy = $deepCopy;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply($element)
+ {
+ $newElement = clone $element;
+
+ if ($element instanceof \SplDoublyLinkedList) {
+ // Replace each element in the list with a deep copy of itself
+ for ($i = 1; $i <= $newElement->count(); $i++) {
+ $newElement->push($this->deepCopy->copy($newElement->shift()));
+ }
+ }
+
+ return $newElement;
+
+ }
+}
+<?php
+
+namespace DeepCopy\TypeFilter;
+
+interface TypeFilter
+{
+ /**
+ * Apply the filter to the object.
+ * @param mixed $element
+ */
+ public function apply($element);
+}
+<?php
+
+namespace DeepCopy\TypeMatcher;
+
+/**
+ * TypeMatcher class
+ */
+class TypeMatcher
+{
+ /**
+ * @var string
+ */
+ private $type;
+
+ /**
+ * @param string $type
+ */
+ public function __construct($type)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * @param $element
+ * @return boolean
+ */
+ public function matches($element)
+ {
+ return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type;
+ }
+}
+The MIT License (MIT)
+
+Copyright (c) 2014 Bernhard Schussek
+
+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.
+<?php
+
+/*
+ * This file is part of the webmozart/assert package.
+ *
+ * (c) Bernhard Schussek <bschussek@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Webmozart\Assert;
+
+use BadMethodCallException;
+use InvalidArgumentException;
+use Traversable;
+use Exception;
+use Throwable;
+use Closure;
+
+/**
+ * Efficient assertions to validate the input/output of your methods.
+ *
+ * @method static void nullOrString($value, $message = '')
+ * @method static void nullOrStringNotEmpty($value, $message = '')
+ * @method static void nullOrInteger($value, $message = '')
+ * @method static void nullOrIntegerish($value, $message = '')
+ * @method static void nullOrFloat($value, $message = '')
+ * @method static void nullOrNumeric($value, $message = '')
+ * @method static void nullOrBoolean($value, $message = '')
+ * @method static void nullOrScalar($value, $message = '')
+ * @method static void nullOrObject($value, $message = '')
+ * @method static void nullOrResource($value, $type = null, $message = '')
+ * @method static void nullOrIsCallable($value, $message = '')
+ * @method static void nullOrIsArray($value, $message = '')
+ * @method static void nullOrIsTraversable($value, $message = '')
+ * @method static void nullOrIsInstanceOf($value, $class, $message = '')
+ * @method static void nullOrNotInstanceOf($value, $class, $message = '')
+ * @method static void nullOrIsEmpty($value, $message = '')
+ * @method static void nullOrNotEmpty($value, $message = '')
+ * @method static void nullOrTrue($value, $message = '')
+ * @method static void nullOrFalse($value, $message = '')
+ * @method static void nullOrEq($value, $value2, $message = '')
+ * @method static void nullOrNotEq($value,$value2, $message = '')
+ * @method static void nullOrSame($value, $value2, $message = '')
+ * @method static void nullOrNotSame($value, $value2, $message = '')
+ * @method static void nullOrGreaterThan($value, $value2, $message = '')
+ * @method static void nullOrGreaterThanEq($value, $value2, $message = '')
+ * @method static void nullOrLessThan($value, $value2, $message = '')
+ * @method static void nullOrLessThanEq($value, $value2, $message = '')
+ * @method static void nullOrRange($value, $min, $max, $message = '')
+ * @method static void nullOrOneOf($value, $values, $message = '')
+ * @method static void nullOrContains($value, $subString, $message = '')
+ * @method static void nullOrStartsWith($value, $prefix, $message = '')
+ * @method static void nullOrStartsWithLetter($value, $message = '')
+ * @method static void nullOrEndsWith($value, $suffix, $message = '')
+ * @method static void nullOrRegex($value, $pattern, $message = '')
+ * @method static void nullOrAlpha($value, $message = '')
+ * @method static void nullOrDigits($value, $message = '')
+ * @method static void nullOrAlnum($value, $message = '')
+ * @method static void nullOrLower($value, $message = '')
+ * @method static void nullOrUpper($value, $message = '')
+ * @method static void nullOrLength($value, $length, $message = '')
+ * @method static void nullOrMinLength($value, $min, $message = '')
+ * @method static void nullOrMaxLength($value, $max, $message = '')
+ * @method static void nullOrLengthBetween($value, $min, $max, $message = '')
+ * @method static void nullOrFileExists($value, $message = '')
+ * @method static void nullOrFile($value, $message = '')
+ * @method static void nullOrDirectory($value, $message = '')
+ * @method static void nullOrReadable($value, $message = '')
+ * @method static void nullOrWritable($value, $message = '')
+ * @method static void nullOrClassExists($value, $message = '')
+ * @method static void nullOrSubclassOf($value, $class, $message = '')
+ * @method static void nullOrImplementsInterface($value, $interface, $message = '')
+ * @method static void nullOrPropertyExists($value, $property, $message = '')
+ * @method static void nullOrPropertyNotExists($value, $property, $message = '')
+ * @method static void nullOrMethodExists($value, $method, $message = '')
+ * @method static void nullOrMethodNotExists($value, $method, $message = '')
+ * @method static void nullOrKeyExists($value, $key, $message = '')
+ * @method static void nullOrKeyNotExists($value, $key, $message = '')
+ * @method static void nullOrCount($value, $key, $message = '')
+ * @method static void nullOrUuid($values, $message = '')
+ * @method static void allString($values, $message = '')
+ * @method static void allStringNotEmpty($values, $message = '')
+ * @method static void allInteger($values, $message = '')
+ * @method static void allIntegerish($values, $message = '')
+ * @method static void allFloat($values, $message = '')
+ * @method static void allNumeric($values, $message = '')
+ * @method static void allBoolean($values, $message = '')
+ * @method static void allScalar($values, $message = '')
+ * @method static void allObject($values, $message = '')
+ * @method static void allResource($values, $type = null, $message = '')
+ * @method static void allIsCallable($values, $message = '')
+ * @method static void allIsArray($values, $message = '')
+ * @method static void allIsTraversable($values, $message = '')
+ * @method static void allIsInstanceOf($values, $class, $message = '')
+ * @method static void allNotInstanceOf($values, $class, $message = '')
+ * @method static void allNull($values, $message = '')
+ * @method static void allNotNull($values, $message = '')
+ * @method static void allIsEmpty($values, $message = '')
+ * @method static void allNotEmpty($values, $message = '')
+ * @method static void allTrue($values, $message = '')
+ * @method static void allFalse($values, $message = '')
+ * @method static void allEq($values, $value2, $message = '')
+ * @method static void allNotEq($values,$value2, $message = '')
+ * @method static void allSame($values, $value2, $message = '')
+ * @method static void allNotSame($values, $value2, $message = '')
+ * @method static void allGreaterThan($values, $value2, $message = '')
+ * @method static void allGreaterThanEq($values, $value2, $message = '')
+ * @method static void allLessThan($values, $value2, $message = '')
+ * @method static void allLessThanEq($values, $value2, $message = '')
+ * @method static void allRange($values, $min, $max, $message = '')
+ * @method static void allOneOf($values, $values, $message = '')
+ * @method static void allContains($values, $subString, $message = '')
+ * @method static void allStartsWith($values, $prefix, $message = '')
+ * @method static void allStartsWithLetter($values, $message = '')
+ * @method static void allEndsWith($values, $suffix, $message = '')
+ * @method static void allRegex($values, $pattern, $message = '')
+ * @method static void allAlpha($values, $message = '')
+ * @method static void allDigits($values, $message = '')
+ * @method static void allAlnum($values, $message = '')
+ * @method static void allLower($values, $message = '')
+ * @method static void allUpper($values, $message = '')
+ * @method static void allLength($values, $length, $message = '')
+ * @method static void allMinLength($values, $min, $message = '')
+ * @method static void allMaxLength($values, $max, $message = '')
+ * @method static void allLengthBetween($values, $min, $max, $message = '')
+ * @method static void allFileExists($values, $message = '')
+ * @method static void allFile($values, $message = '')
+ * @method static void allDirectory($values, $message = '')
+ * @method static void allReadable($values, $message = '')
+ * @method static void allWritable($values, $message = '')
+ * @method static void allClassExists($values, $message = '')
+ * @method static void allSubclassOf($values, $class, $message = '')
+ * @method static void allImplementsInterface($values, $interface, $message = '')
+ * @method static void allPropertyExists($values, $property, $message = '')
+ * @method static void allPropertyNotExists($values, $property, $message = '')
+ * @method static void allMethodExists($values, $method, $message = '')
+ * @method static void allMethodNotExists($values, $method, $message = '')
+ * @method static void allKeyExists($values, $key, $message = '')
+ * @method static void allKeyNotExists($values, $key, $message = '')
+ * @method static void allCount($values, $key, $message = '')
+ * @method static void allUuid($values, $message = '')
+ *
+ * @since 1.0
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class Assert
+{
+ public static function string($value, $message = '')
+ {
+ if (!is_string($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a string. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function stringNotEmpty($value, $message = '')
+ {
+ static::string($value, $message);
+ static::notEmpty($value, $message);
+ }
+
+ public static function integer($value, $message = '')
+ {
+ if (!is_int($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an integer. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function integerish($value, $message = '')
+ {
+ if (!is_numeric($value) || $value != (int) $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an integerish value. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function float($value, $message = '')
+ {
+ if (!is_float($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a float. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function numeric($value, $message = '')
+ {
+ if (!is_numeric($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a numeric. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function boolean($value, $message = '')
+ {
+ if (!is_bool($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a boolean. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function scalar($value, $message = '')
+ {
+ if (!is_scalar($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a scalar. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function object($value, $message = '')
+ {
+ if (!is_object($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an object. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function resource($value, $type = null, $message = '')
+ {
+ if (!is_resource($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a resource. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+
+ if ($type && $type !== get_resource_type($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a resource of type %2$s. Got: %s',
+ static::typeToString($value),
+ $type
+ ));
+ }
+ }
+
+ public static function isCallable($value, $message = '')
+ {
+ if (!is_callable($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a callable. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function isArray($value, $message = '')
+ {
+ if (!is_array($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an array. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function isTraversable($value, $message = '')
+ {
+ if (!is_array($value) && !($value instanceof Traversable)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a traversable. Got: %s',
+ static::typeToString($value)
+ ));
+ }
+ }
+
+ public static function isInstanceOf($value, $class, $message = '')
+ {
+ if (!($value instanceof $class)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an instance of %2$s. Got: %s',
+ static::typeToString($value),
+ $class
+ ));
+ }
+ }
+
+ public static function notInstanceOf($value, $class, $message = '')
+ {
+ if ($value instanceof $class) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an instance other than %2$s. Got: %s',
+ static::typeToString($value),
+ $class
+ ));
+ }
+ }
+
+ public static function isEmpty($value, $message = '')
+ {
+ if (!empty($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an empty value. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function notEmpty($value, $message = '')
+ {
+ if (empty($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a non-empty value. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function null($value, $message = '')
+ {
+ if (null !== $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected null. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function notNull($value, $message = '')
+ {
+ if (null === $value) {
+ static::reportInvalidArgument(
+ $message ?: 'Expected a value other than null.'
+ );
+ }
+ }
+
+ public static function true($value, $message = '')
+ {
+ if (true !== $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to be true. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function false($value, $message = '')
+ {
+ if (false !== $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to be false. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function eq($value, $value2, $message = '')
+ {
+ if ($value2 != $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value equal to %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($value2)
+ ));
+ }
+ }
+
+ public static function notEq($value, $value2, $message = '')
+ {
+ if ($value2 == $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a different value than %s.',
+ static::valueToString($value2)
+ ));
+ }
+ }
+
+ public static function same($value, $value2, $message = '')
+ {
+ if ($value2 !== $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value identical to %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($value2)
+ ));
+ }
+ }
+
+ public static function notSame($value, $value2, $message = '')
+ {
+ if ($value2 === $value) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value not identical to %s.',
+ static::valueToString($value2)
+ ));
+ }
+ }
+
+ public static function greaterThan($value, $limit, $message = '')
+ {
+ if ($value <= $limit) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value greater than %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($limit)
+ ));
+ }
+ }
+
+ public static function greaterThanEq($value, $limit, $message = '')
+ {
+ if ($value < $limit) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($limit)
+ ));
+ }
+ }
+
+ public static function lessThan($value, $limit, $message = '')
+ {
+ if ($value >= $limit) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value less than %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($limit)
+ ));
+ }
+ }
+
+ public static function lessThanEq($value, $limit, $message = '')
+ {
+ if ($value > $limit) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($limit)
+ ));
+ }
+ }
+
+ public static function range($value, $min, $max, $message = '')
+ {
+ if ($value < $min || $value > $max) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($min),
+ static::valueToString($max)
+ ));
+ }
+ }
+
+ public static function oneOf($value, array $values, $message = '')
+ {
+ if (!in_array($value, $values, true)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected one of: %2$s. Got: %s',
+ static::valueToString($value),
+ implode(', ', array_map(array('static', 'valueToString'), $values))
+ ));
+ }
+ }
+
+ public static function contains($value, $subString, $message = '')
+ {
+ if (false === strpos($value, $subString)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($subString)
+ ));
+ }
+ }
+
+ public static function startsWith($value, $prefix, $message = '')
+ {
+ if (0 !== strpos($value, $prefix)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to start with %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($prefix)
+ ));
+ }
+ }
+
+ public static function startsWithLetter($value, $message = '')
+ {
+ $valid = isset($value[0]);
+
+ if ($valid) {
+ $locale = setlocale(LC_CTYPE, 0);
+ setlocale(LC_CTYPE, 'C');
+ $valid = ctype_alpha($value[0]);
+ setlocale(LC_CTYPE, $locale);
+ }
+
+ if (!$valid) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to start with a letter. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function endsWith($value, $suffix, $message = '')
+ {
+ if ($suffix !== substr($value, -static::strlen($suffix))) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to end with %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($suffix)
+ ));
+ }
+ }
+
+ public static function regex($value, $pattern, $message = '')
+ {
+ if (!preg_match($pattern, $value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'The value %s does not match the expected pattern.',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function alpha($value, $message = '')
+ {
+ $locale = setlocale(LC_CTYPE, 0);
+ setlocale(LC_CTYPE, 'C');
+ $valid = !ctype_alpha($value);
+ setlocale(LC_CTYPE, $locale);
+
+ if ($valid) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain only letters. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function digits($value, $message = '')
+ {
+ $locale = setlocale(LC_CTYPE, 0);
+ setlocale(LC_CTYPE, 'C');
+ $valid = !ctype_digit($value);
+ setlocale(LC_CTYPE, $locale);
+
+ if ($valid) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain digits only. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function alnum($value, $message = '')
+ {
+ $locale = setlocale(LC_CTYPE, 0);
+ setlocale(LC_CTYPE, 'C');
+ $valid = !ctype_alnum($value);
+ setlocale(LC_CTYPE, $locale);
+
+ if ($valid) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain letters and digits only. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function lower($value, $message = '')
+ {
+ $locale = setlocale(LC_CTYPE, 0);
+ setlocale(LC_CTYPE, 'C');
+ $valid = !ctype_lower($value);
+ setlocale(LC_CTYPE, $locale);
+
+ if ($valid) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function upper($value, $message = '')
+ {
+ $locale = setlocale(LC_CTYPE, 0);
+ setlocale(LC_CTYPE, 'C');
+ $valid = !ctype_upper($value);
+ setlocale(LC_CTYPE, $locale);
+
+ if ($valid) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function length($value, $length, $message = '')
+ {
+ if ($length !== static::strlen($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain %2$s characters. Got: %s',
+ static::valueToString($value),
+ $length
+ ));
+ }
+ }
+
+ public static function minLength($value, $min, $message = '')
+ {
+ if (static::strlen($value) < $min) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
+ static::valueToString($value),
+ $min
+ ));
+ }
+ }
+
+ public static function maxLength($value, $max, $message = '')
+ {
+ if (static::strlen($value) > $max) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
+ static::valueToString($value),
+ $max
+ ));
+ }
+ }
+
+ public static function lengthBetween($value, $min, $max, $message = '')
+ {
+ $length = static::strlen($value);
+
+ if ($length < $min || $length > $max) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
+ static::valueToString($value),
+ $min,
+ $max
+ ));
+ }
+ }
+
+ public static function fileExists($value, $message = '')
+ {
+ static::string($value);
+
+ if (!file_exists($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'The file %s does not exist.',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function file($value, $message = '')
+ {
+ static::fileExists($value, $message);
+
+ if (!is_file($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'The path %s is not a file.',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function directory($value, $message = '')
+ {
+ static::fileExists($value, $message);
+
+ if (!is_dir($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'The path %s is no directory.',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function readable($value, $message = '')
+ {
+ if (!is_readable($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'The path %s is not readable.',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function writable($value, $message = '')
+ {
+ if (!is_writable($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'The path %s is not writable.',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function classExists($value, $message = '')
+ {
+ if (!class_exists($value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an existing class name. Got: %s',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function subclassOf($value, $class, $message = '')
+ {
+ if (!is_subclass_of($value, $class)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected a sub-class of %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($class)
+ ));
+ }
+ }
+
+ public static function implementsInterface($value, $interface, $message = '')
+ {
+ if (!in_array($interface, class_implements($value))) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected an implementation of %2$s. Got: %s',
+ static::valueToString($value),
+ static::valueToString($interface)
+ ));
+ }
+ }
+
+ public static function propertyExists($classOrObject, $property, $message = '')
+ {
+ if (!property_exists($classOrObject, $property)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected the property %s to exist.',
+ static::valueToString($property)
+ ));
+ }
+ }
+
+ public static function propertyNotExists($classOrObject, $property, $message = '')
+ {
+ if (property_exists($classOrObject, $property)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected the property %s to not exist.',
+ static::valueToString($property)
+ ));
+ }
+ }
+
+ public static function methodExists($classOrObject, $method, $message = '')
+ {
+ if (!method_exists($classOrObject, $method)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected the method %s to exist.',
+ static::valueToString($method)
+ ));
+ }
+ }
+
+ public static function methodNotExists($classOrObject, $method, $message = '')
+ {
+ if (method_exists($classOrObject, $method)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected the method %s to not exist.',
+ static::valueToString($method)
+ ));
+ }
+ }
+
+ public static function keyExists($array, $key, $message = '')
+ {
+ if (!array_key_exists($key, $array)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected the key %s to exist.',
+ static::valueToString($key)
+ ));
+ }
+ }
+
+ public static function keyNotExists($array, $key, $message = '')
+ {
+ if (array_key_exists($key, $array)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Expected the key %s to not exist.',
+ static::valueToString($key)
+ ));
+ }
+ }
+
+ public static function count($array, $number, $message = '')
+ {
+ static::eq(
+ count($array),
+ $number,
+ $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array))
+ );
+ }
+
+ public static function uuid($value, $message = '')
+ {
+ $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value);
+
+ // The nil UUID is special form of UUID that is specified to have all
+ // 128 bits set to zero.
+ if ('00000000-0000-0000-0000-000000000000' === $value) {
+ return;
+ }
+
+ if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) {
+ static::reportInvalidArgument(sprintf(
+ $message ?: 'Value %s is not a valid UUID.',
+ static::valueToString($value)
+ ));
+ }
+ }
+
+ public static function throws(Closure $expression, $class = 'Exception', $message = '')
+ {
+ static::string($class);
+
+ $actual = 'none';
+ try {
+ $expression();
+ } catch (Exception $e) {
+ $actual = get_class($e);
+ if ($e instanceof $class) {
+ return;
+ }
+ } catch (Throwable $e) {
+ $actual = get_class($e);
+ if ($e instanceof $class) {
+ return;
+ }
+ }
+
+ static::reportInvalidArgument($message ?: sprintf(
+ 'Expected to throw "%s", got "%s"',
+ $class,
+ $actual
+ ));
+ }
+
+ public static function __callStatic($name, $arguments)
+ {
+ if ('nullOr' === substr($name, 0, 6)) {
+ if (null !== $arguments[0]) {
+ $method = lcfirst(substr($name, 6));
+ call_user_func_array(array('static', $method), $arguments);
+ }
+
+ return;
+ }
+
+ if ('all' === substr($name, 0, 3)) {
+ static::isTraversable($arguments[0]);
+
+ $method = lcfirst(substr($name, 3));
+ $args = $arguments;
+
+ foreach ($arguments[0] as $entry) {
+ $args[0] = $entry;
+
+ call_user_func_array(array('static', $method), $args);
+ }
+
+ return;
+ }
+
+ throw new BadMethodCallException('No such method: '.$name);
+ }
+
+ protected static function valueToString($value)
+ {
+ if (null === $value) {
+ return 'null';
+ }
+
+ if (true === $value) {
+ return 'true';
+ }
+
+ if (false === $value) {
+ return 'false';
+ }
+
+ if (is_array($value)) {
+ return 'array';
+ }
+
+ if (is_object($value)) {
+ return get_class($value);
+ }
+
+ if (is_resource($value)) {
+ return 'resource';
+ }
+
+ if (is_string($value)) {
+ return '"'.$value.'"';
+ }
+
+ return (string) $value;
+ }
+
+ protected static function typeToString($value)
+ {
+ return is_object($value) ? get_class($value) : gettype($value);
+ }
+
+ protected static function strlen($value)
+ {
+ if (!function_exists('mb_detect_encoding')) {
+ return strlen($value);
+ }
+
+ if (false === $encoding = mb_detect_encoding($value)) {
+ return strlen($value);
+ }
+
+ return mb_strwidth($value, $encoding);
+ }
+
+ protected static function reportInvalidArgument($message)
+ {
+ throw new InvalidArgumentException($message);
+ }
+
+ private function __construct()
+ {
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Marker interface for PHPUnit exceptions.
+ */
+interface PHPUnit_Exception
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * We have a TestSuite object A.
+ * In TestSuite object A we have Tests tagged with @group.
+ * We want a TestSuite object B that contains TestSuite objects C, D, ...
+ * for the Tests tagged with @group C, @group D, ...
+ * Running the Tests from TestSuite object B results in Tests tagged with both
+ *
+ * @group C and @group D in TestSuite object A to be run twice .
+ *
+ * <code>
+ * $suite = new PHPUnit_Extensions_GroupTestSuite($A, array('C', 'D'));
+ * </code>
+ */
+class PHPUnit_Extensions_GroupTestSuite extends PHPUnit_Framework_TestSuite
+{
+ public function __construct(PHPUnit_Framework_TestSuite $suite, array $groups)
+ {
+ $groupSuites = [];
+ $name = $suite->getName();
+
+ foreach ($groups as $group) {
+ $groupSuites[$group] = new PHPUnit_Framework_TestSuite($name . ' - ' . $group);
+ $this->addTest($groupSuites[$group]);
+ }
+
+ $tests = new RecursiveIteratorIterator(
+ new PHPUnit_Util_TestSuiteIterator($suite),
+ RecursiveIteratorIterator::LEAVES_ONLY
+ );
+
+ foreach ($tests as $test) {
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $testGroups = PHPUnit_Util_Test::getGroups(
+ get_class($test),
+ $test->getName(false)
+ );
+
+ foreach ($groups as $group) {
+ foreach ($testGroups as $testGroup) {
+ if ($group == $testGroup) {
+ $groupSuites[$group]->addTest($test);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Runner for PHPT test cases.
+ */
+class PHPUnit_Extensions_PhptTestCase implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * @var string
+ */
+ private $filename;
+
+ /**
+ * @var PHPUnit_Util_PHP
+ */
+ private $phpUtil;
+
+ /**
+ * @var array
+ */
+ private $settings = [
+ 'allow_url_fopen=1',
+ 'auto_append_file=',
+ 'auto_prepend_file=',
+ 'disable_functions=',
+ 'display_errors=1',
+ 'docref_root=',
+ 'docref_ext=.html',
+ 'error_append_string=',
+ 'error_prepend_string=',
+ 'error_reporting=-1',
+ 'html_errors=0',
+ 'log_errors=0',
+ 'magic_quotes_runtime=0',
+ 'output_handler=',
+ 'open_basedir=',
+ 'output_buffering=Off',
+ 'report_memleaks=0',
+ 'report_zend_debug=0',
+ 'safe_mode=0',
+ 'xdebug.default_enable=0'
+ ];
+
+ /**
+ * Constructs a test case with the given filename.
+ *
+ * @param string $filename
+ * @param PHPUnit_Util_PHP $phpUtil
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($filename, $phpUtil = null)
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_file($filename)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'File "%s" does not exist.',
+ $filename
+ )
+ );
+ }
+
+ $this->filename = $filename;
+ $this->phpUtil = $phpUtil ?: PHPUnit_Util_PHP::factory();
+ }
+
+ /**
+ * Counts the number of test cases executed by run(TestResult result).
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return 1;
+ }
+
+ /**
+ * @param array $sections
+ * @param string $output
+ */
+ private function assertPhptExpectation(array $sections, $output)
+ {
+ $assertions = [
+ 'EXPECT' => 'assertEquals',
+ 'EXPECTF' => 'assertStringMatchesFormat',
+ 'EXPECTREGEX' => 'assertRegExp',
+ ];
+
+ $actual = preg_replace('/\r\n/', "\n", trim($output));
+
+ foreach ($assertions as $sectionName => $sectionAssertion) {
+ if (isset($sections[$sectionName])) {
+ $sectionContent = preg_replace('/\r\n/', "\n", trim($sections[$sectionName]));
+ $assertion = $sectionAssertion;
+ $expected = $sectionName == 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent;
+
+ break;
+ }
+ }
+
+ PHPUnit_Framework_Assert::$assertion($expected, $actual);
+ }
+
+ /**
+ * Runs a test and collects its result in a TestResult instance.
+ *
+ * @param PHPUnit_Framework_TestResult $result
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ public function run(PHPUnit_Framework_TestResult $result = null)
+ {
+ $sections = $this->parse();
+ $code = $this->render($sections['FILE']);
+
+ if ($result === null) {
+ $result = new PHPUnit_Framework_TestResult;
+ }
+
+ $skip = false;
+ $xfail = false;
+ $time = 0;
+ $settings = $this->settings;
+
+ $result->startTest($this);
+
+ if (isset($sections['INI'])) {
+ $settings = array_merge($settings, $this->parseIniSection($sections['INI']));
+ }
+
+ if (isset($sections['ENV'])) {
+ $env = $this->parseEnvSection($sections['ENV']);
+ $this->phpUtil->setEnv($env);
+ }
+
+ // Redirects STDERR to STDOUT
+ $this->phpUtil->setUseStderrRedirection(true);
+
+ if ($result->enforcesTimeLimit()) {
+ $this->phpUtil->setTimeout($result->getTimeoutForLargeTests());
+ }
+
+ if (isset($sections['SKIPIF'])) {
+ $jobResult = $this->phpUtil->runJob($sections['SKIPIF'], $settings);
+
+ if (!strncasecmp('skip', ltrim($jobResult['stdout']), 4)) {
+ if (preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $message)) {
+ $message = substr($message[1], 2);
+ } else {
+ $message = '';
+ }
+
+ $result->addFailure($this, new PHPUnit_Framework_SkippedTestError($message), 0);
+
+ $skip = true;
+ }
+ }
+
+ if (isset($sections['XFAIL'])) {
+ $xfail = trim($sections['XFAIL']);
+ }
+
+ if (!$skip) {
+ if (isset($sections['STDIN'])) {
+ $this->phpUtil->setStdin($sections['STDIN']);
+ }
+
+ if (isset($sections['ARGS'])) {
+ $this->phpUtil->setArgs($sections['ARGS']);
+ }
+
+ PHP_Timer::start();
+
+ $jobResult = $this->phpUtil->runJob($code, $settings);
+ $time = PHP_Timer::stop();
+
+ try {
+ $this->assertPhptExpectation($sections, $jobResult['stdout']);
+ } catch (PHPUnit_Framework_AssertionFailedError $e) {
+ if ($xfail !== false) {
+ $result->addFailure(
+ $this,
+ new PHPUnit_Framework_IncompleteTestError(
+ $xfail,
+ 0,
+ $e
+ ),
+ $time
+ );
+ } else {
+ $result->addFailure($this, $e, $time);
+ }
+ } catch (Throwable $t) {
+ $result->addError($this, $t, $time);
+ } catch (Exception $e) {
+ $result->addError($this, $e, $time);
+ }
+
+ if ($result->allCompletelyImplemented() && $xfail !== false) {
+ $result->addFailure(
+ $this,
+ new PHPUnit_Framework_IncompleteTestError(
+ 'XFAIL section but test passes'
+ ),
+ $time
+ );
+ }
+
+ $this->phpUtil->setStdin('');
+ $this->phpUtil->setArgs('');
+
+ if (isset($sections['CLEAN'])) {
+ $cleanCode = $this->render($sections['CLEAN']);
+
+ $this->phpUtil->runJob($cleanCode, $this->settings);
+ }
+ }
+
+ $result->endTest($this, $time);
+
+ return $result;
+ }
+
+ /**
+ * Returns the name of the test case.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * Returns a string representation of the test case.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->filename;
+ }
+
+ /**
+ * @return array
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ private function parse()
+ {
+ $sections = [];
+ $section = '';
+
+ $allowExternalSections = [
+ 'FILE',
+ 'EXPECT',
+ 'EXPECTF',
+ 'EXPECTREGEX'
+ ];
+
+ $requiredSections = [
+ 'FILE',
+ [
+ 'EXPECT',
+ 'EXPECTF',
+ 'EXPECTREGEX'
+ ]
+ ];
+
+ $unsupportedSections = [
+ 'REDIRECTTEST',
+ 'REQUEST',
+ 'POST',
+ 'PUT',
+ 'POST_RAW',
+ 'GZIP_POST',
+ 'DEFLATE_POST',
+ 'GET',
+ 'COOKIE',
+ 'HEADERS',
+ 'CGI',
+ 'EXPECTHEADERS',
+ 'EXTENSIONS',
+ 'PHPDBG'
+ ];
+
+ foreach (file($this->filename) as $line) {
+ if (preg_match('/^--([_A-Z]+)--/', $line, $result)) {
+ $section = $result[1];
+ $sections[$section] = '';
+
+ continue;
+ } elseif (empty($section)) {
+ throw new PHPUnit_Framework_Exception('Invalid PHPT file');
+ }
+
+ $sections[$section] .= $line;
+ }
+
+ if (isset($sections['FILEEOF'])) {
+ $sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n");
+ unset($sections['FILEEOF']);
+ }
+
+ $testDirectory = dirname($this->filename) . DIRECTORY_SEPARATOR;
+
+ foreach ($allowExternalSections as $section) {
+ if (isset($sections[$section . '_EXTERNAL'])) {
+ // do not allow directory traversal
+ $externalFilename = str_replace('..', '', trim($sections[$section . '_EXTERNAL']));
+
+ // only allow files from the test directory
+ if (!is_file($testDirectory . $externalFilename) || !is_readable($testDirectory . $externalFilename)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Could not load --%s-- %s for PHPT file',
+ $section . '_EXTERNAL',
+ $testDirectory . $externalFilename
+ )
+ );
+ }
+
+ $sections[$section] = file_get_contents($testDirectory . $externalFilename);
+
+ unset($sections[$section . '_EXTERNAL']);
+ }
+ }
+
+ $isValid = true;
+
+ foreach ($requiredSections as $section) {
+ if (is_array($section)) {
+ $foundSection = false;
+
+ foreach ($section as $anySection) {
+ if (isset($sections[$anySection])) {
+ $foundSection = true;
+
+ break;
+ }
+ }
+
+ if (!$foundSection) {
+ $isValid = false;
+
+ break;
+ }
+ } else {
+ if (!isset($sections[$section])) {
+ $isValid = false;
+
+ break;
+ }
+ }
+ }
+
+ if (!$isValid) {
+ throw new PHPUnit_Framework_Exception('Invalid PHPT file');
+ }
+
+ foreach ($unsupportedSections as $section) {
+ if (isset($sections[$section])) {
+ throw new PHPUnit_Framework_Exception(
+ 'PHPUnit does not support this PHPT file'
+ );
+ }
+ }
+
+ return $sections;
+ }
+
+ /**
+ * @param string $code
+ *
+ * @return string
+ */
+ private function render($code)
+ {
+ return str_replace(
+ [
+ '__DIR__',
+ '__FILE__'
+ ],
+ [
+ "'" . dirname($this->filename) . "'",
+ "'" . $this->filename . "'"
+ ],
+ $code
+ );
+ }
+
+ /**
+ * Parse --INI-- section key value pairs and return as array.
+ *
+ * @param string
+ *
+ * @return array
+ */
+ protected function parseIniSection($content)
+ {
+ return preg_split('/\n|\r/', $content, -1, PREG_SPLIT_NO_EMPTY);
+ }
+
+ protected function parseEnvSection($content)
+ {
+ $env = [];
+
+ foreach (explode("\n", trim($content)) as $e) {
+ $e = explode('=', trim($e), 2);
+
+ if (!empty($e[0]) && isset($e[1])) {
+ $env[$e[0]] = $e[1];
+ }
+ }
+
+ return $env;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Suite for .phpt test cases.
+ */
+class PHPUnit_Extensions_PhptTestSuite extends PHPUnit_Framework_TestSuite
+{
+ /**
+ * Constructs a new TestSuite for .phpt test cases.
+ *
+ * @param string $directory
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($directory)
+ {
+ if (is_string($directory) && is_dir($directory)) {
+ $this->setName($directory);
+
+ $facade = new File_Iterator_Facade;
+ $files = $facade->getFilesAsArray($directory, '.phpt');
+
+ foreach ($files as $file) {
+ $this->addTestFile($file);
+ }
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'directory name');
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A Decorator that runs a test repeatedly.
+ */
+class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
+{
+ /**
+ * @var bool
+ */
+ protected $processIsolation = false;
+
+ /**
+ * @var int
+ */
+ protected $timesRepeat = 1;
+
+ /**
+ * @param PHPUnit_Framework_Test $test
+ * @param int $timesRepeat
+ * @param bool $processIsolation
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $processIsolation = false)
+ {
+ parent::__construct($test);
+
+ if (is_int($timesRepeat) &&
+ $timesRepeat >= 0) {
+ $this->timesRepeat = $timesRepeat;
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'positive integer'
+ );
+ }
+
+ $this->processIsolation = $processIsolation;
+ }
+
+ /**
+ * Counts the number of test cases that
+ * will be run by this test.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->timesRepeat * count($this->test);
+ }
+
+ /**
+ * Runs the decorated test and collects the
+ * result in a TestResult.
+ *
+ * @param PHPUnit_Framework_TestResult $result
+ *
+ * @return PHPUnit_Framework_TestResult
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function run(PHPUnit_Framework_TestResult $result = null)
+ {
+ if ($result === null) {
+ $result = $this->createResult();
+ }
+
+ //@codingStandardsIgnoreStart
+ for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
+ //@codingStandardsIgnoreEnd
+ if ($this->test instanceof PHPUnit_Framework_TestSuite) {
+ $this->test->setRunTestInSeparateProcess($this->processIsolation);
+ }
+ $this->test->run($result);
+ }
+
+ return $result;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A Decorator for Tests.
+ *
+ * Use TestDecorator as the base class for defining new
+ * test decorators. Test decorator subclasses can be introduced
+ * to add behaviour before or after a test is run.
+ */
+class PHPUnit_Extensions_TestDecorator extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * The Test to be decorated.
+ *
+ * @var object
+ */
+ protected $test = null;
+
+ /**
+ * Constructor.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function __construct(PHPUnit_Framework_Test $test)
+ {
+ $this->test = $test;
+ }
+
+ /**
+ * Returns a string representation of the test.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->test->toString();
+ }
+
+ /**
+ * Runs the test and collects the
+ * result in a TestResult.
+ *
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ public function basicRun(PHPUnit_Framework_TestResult $result)
+ {
+ $this->test->run($result);
+ }
+
+ /**
+ * Counts the number of test cases that
+ * will be run by this test.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->test);
+ }
+
+ /**
+ * Creates a default TestResult object.
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ protected function createResult()
+ {
+ return new PHPUnit_Framework_TestResult;
+ }
+
+ /**
+ * Returns the test to be run.
+ *
+ * @return PHPUnit_Framework_Test
+ */
+ public function getTest()
+ {
+ return $this->test;
+ }
+
+ /**
+ * Runs the decorated test and collects the
+ * result in a TestResult.
+ *
+ * @param PHPUnit_Framework_TestResult $result
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ public function run(PHPUnit_Framework_TestResult $result = null)
+ {
+ if ($result === null) {
+ $result = $this->createResult();
+ }
+
+ $this->basicRun($result);
+
+ return $result;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Base class for test listeners that interact with an issue tracker.
+ */
+abstract class PHPUnit_Extensions_TicketListener implements PHPUnit_Framework_TestListener
+{
+ /**
+ * @var array
+ */
+ protected $ticketCounts = [];
+
+ /**
+ * @var bool
+ */
+ protected $ran = false;
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ /**
+ * A test suite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ /**
+ * A test suite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ if (!$test instanceof PHPUnit_Framework_WarningTestCase) {
+ if ($this->ran) {
+ return;
+ }
+
+ $name = $test->getName(false);
+ $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name);
+
+ foreach ($tickets as $ticket) {
+ $this->ticketCounts[$ticket][$name] = 1;
+ }
+
+ $this->ran = true;
+ }
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ if (!$test instanceof PHPUnit_Framework_WarningTestCase) {
+ if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
+ $ifStatus = ['assigned', 'new', 'reopened'];
+ $newStatus = 'closed';
+ $message = 'Automatically closed by PHPUnit (test passed).';
+ $resolution = 'fixed';
+ $cumulative = true;
+ } elseif ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
+ $ifStatus = ['closed'];
+ $newStatus = 'reopened';
+ $message = 'Automatically reopened by PHPUnit (test failed).';
+ $resolution = '';
+ $cumulative = false;
+ } else {
+ return;
+ }
+
+ $name = $test->getName(false);
+ $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name);
+
+ foreach ($tickets as $ticket) {
+ // Remove this test from the totals (if it passed).
+ if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
+ unset($this->ticketCounts[$ticket][$name]);
+ }
+
+ // Only close tickets if ALL referenced cases pass
+ // but reopen tickets if a single test fails.
+ if ($cumulative) {
+ // Determine number of to-pass tests:
+ if (count($this->ticketCounts[$ticket]) > 0) {
+ // There exist remaining test cases with this reference.
+ $adjustTicket = false;
+ } else {
+ // No remaining tickets, go ahead and adjust.
+ $adjustTicket = true;
+ }
+ } else {
+ $adjustTicket = true;
+ }
+
+ $ticketInfo = $this->getTicketInfo($ticket);
+
+ if ($adjustTicket && in_array($ticketInfo['status'], $ifStatus)) {
+ $this->updateTicket($ticket, $newStatus, $message, $resolution);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param mixed $ticketId
+ *
+ * @return mixed
+ */
+ abstract protected function getTicketInfo($ticketId = null);
+
+ /**
+ * @param string $ticketId
+ * @param string $newStatus
+ * @param string $message
+ * @param string $resolution
+ */
+ abstract protected function updateTicket($ticketId, $newStatus, $message, $resolution);
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace PHPUnit\Framework;
+
+use PHPUnit_Framework_Assert;
+
+abstract class Assert extends PHPUnit_Framework_Assert
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace PHPUnit\Framework;
+
+use PHPUnit_Framework_BaseTestListener;
+
+abstract class BaseTestListener extends PHPUnit_Framework_BaseTestListener
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace PHPUnit\Framework;
+
+use PHPUnit_Framework_TestCase;
+
+abstract class TestCase extends PHPUnit_Framework_TestCase
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace PHPUnit\Framework;
+
+use PHPUnit_Framework_TestListener;
+
+interface TestListener extends PHPUnit_Framework_TestListener
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A set of assertion methods.
+ */
+abstract class PHPUnit_Framework_Assert
+{
+ /**
+ * @var int
+ */
+ private static $count = 0;
+
+ /**
+ * Asserts that an array has a specified key.
+ *
+ * @param mixed $key
+ * @param array|ArrayAccess $array
+ * @param string $message
+ */
+ public static function assertArrayHasKey($key, $array, $message = '')
+ {
+ if (!(is_int($key) || is_string($key))) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'integer or string'
+ );
+ }
+
+ if (!(is_array($array) || $array instanceof ArrayAccess)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array or ArrayAccess'
+ );
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_ArrayHasKey($key);
+
+ static::assertThat($array, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an array has a specified subset.
+ *
+ * @param array|ArrayAccess $subset
+ * @param array|ArrayAccess $array
+ * @param bool $strict Check for object identity
+ * @param string $message
+ */
+ public static function assertArraySubset($subset, $array, $strict = false, $message = '')
+ {
+ if (!(is_array($subset) || $subset instanceof ArrayAccess)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'array or ArrayAccess'
+ );
+ }
+
+ if (!(is_array($array) || $array instanceof ArrayAccess)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array or ArrayAccess'
+ );
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_ArraySubset($subset, $strict);
+
+ static::assertThat($array, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an array does not have a specified key.
+ *
+ * @param mixed $key
+ * @param array|ArrayAccess $array
+ * @param string $message
+ */
+ public static function assertArrayNotHasKey($key, $array, $message = '')
+ {
+ if (!(is_int($key) || is_string($key))) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'integer or string'
+ );
+ }
+
+ if (!(is_array($array) || $array instanceof ArrayAccess)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array or ArrayAccess'
+ );
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_ArrayHasKey($key)
+ );
+
+ static::assertThat($array, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a haystack contains a needle.
+ *
+ * @param mixed $needle
+ * @param mixed $haystack
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+ public static function assertContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+ {
+ if (is_array($haystack) ||
+ is_object($haystack) && $haystack instanceof Traversable) {
+ $constraint = new PHPUnit_Framework_Constraint_TraversableContains(
+ $needle,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ );
+ } elseif (is_string($haystack)) {
+ if (!is_string($needle)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'string'
+ );
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_StringContains(
+ $needle,
+ $ignoreCase
+ );
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array, traversable or string'
+ );
+ }
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object contains a needle.
+ *
+ * @param mixed $needle
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+ public static function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+ {
+ static::assertContains(
+ $needle,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message,
+ $ignoreCase,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ );
+ }
+
+ /**
+ * Asserts that a haystack does not contain a needle.
+ *
+ * @param mixed $needle
+ * @param mixed $haystack
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+ public static function assertNotContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+ {
+ if (is_array($haystack) ||
+ is_object($haystack) && $haystack instanceof Traversable) {
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_TraversableContains(
+ $needle,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ )
+ );
+ } elseif (is_string($haystack)) {
+ if (!is_string($needle)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'string'
+ );
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_StringContains(
+ $needle,
+ $ignoreCase
+ )
+ );
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array, traversable or string'
+ );
+ }
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object does not contain a needle.
+ *
+ * @param mixed $needle
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+ public static function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+ {
+ static::assertNotContains(
+ $needle,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message,
+ $ignoreCase,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ );
+ }
+
+ /**
+ * Asserts that a haystack contains only values of a given type.
+ *
+ * @param string $type
+ * @param mixed $haystack
+ * @param bool $isNativeType
+ * @param string $message
+ */
+ public static function assertContainsOnly($type, $haystack, $isNativeType = null, $message = '')
+ {
+ if (!(is_array($haystack) ||
+ is_object($haystack) && $haystack instanceof Traversable)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array or traversable'
+ );
+ }
+
+ if ($isNativeType == null) {
+ $isNativeType = PHPUnit_Util_Type::isType($type);
+ }
+
+ static::assertThat(
+ $haystack,
+ new PHPUnit_Framework_Constraint_TraversableContainsOnly(
+ $type,
+ $isNativeType
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack contains only instances of a given classname
+ *
+ * @param string $classname
+ * @param array|Traversable $haystack
+ * @param string $message
+ */
+ public static function assertContainsOnlyInstancesOf($classname, $haystack, $message = '')
+ {
+ if (!(is_array($haystack) ||
+ is_object($haystack) && $haystack instanceof Traversable)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array or traversable'
+ );
+ }
+
+ static::assertThat(
+ $haystack,
+ new PHPUnit_Framework_Constraint_TraversableContainsOnly(
+ $classname,
+ false
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object contains only values of a given type.
+ *
+ * @param string $type
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param bool $isNativeType
+ * @param string $message
+ */
+ public static function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
+ {
+ static::assertContainsOnly(
+ $type,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $isNativeType,
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack does not contain only values of a given type.
+ *
+ * @param string $type
+ * @param mixed $haystack
+ * @param bool $isNativeType
+ * @param string $message
+ */
+ public static function assertNotContainsOnly($type, $haystack, $isNativeType = null, $message = '')
+ {
+ if (!(is_array($haystack) ||
+ is_object($haystack) && $haystack instanceof Traversable)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 2,
+ 'array or traversable'
+ );
+ }
+
+ if ($isNativeType == null) {
+ $isNativeType = PHPUnit_Util_Type::isType($type);
+ }
+
+ static::assertThat(
+ $haystack,
+ new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_TraversableContainsOnly(
+ $type,
+ $isNativeType
+ )
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object does not contain only values of a given
+ * type.
+ *
+ * @param string $type
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param bool $isNativeType
+ * @param string $message
+ */
+ public static function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
+ {
+ static::assertNotContainsOnly(
+ $type,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $isNativeType,
+ $message
+ );
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable.
+ *
+ * @param int $expectedCount
+ * @param mixed $haystack
+ * @param string $message
+ */
+ public static function assertCount($expectedCount, $haystack, $message = '')
+ {
+ if (!is_int($expectedCount)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
+ }
+
+ if (!$haystack instanceof Countable &&
+ !$haystack instanceof Traversable &&
+ !is_array($haystack)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
+ }
+
+ static::assertThat(
+ $haystack,
+ new PHPUnit_Framework_Constraint_Count($expectedCount),
+ $message
+ );
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable
+ * that is stored in an attribute.
+ *
+ * @param int $expectedCount
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
+ {
+ static::assertCount(
+ $expectedCount,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable.
+ *
+ * @param int $expectedCount
+ * @param mixed $haystack
+ * @param string $message
+ */
+ public static function assertNotCount($expectedCount, $haystack, $message = '')
+ {
+ if (!is_int($expectedCount)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
+ }
+
+ if (!$haystack instanceof Countable &&
+ !$haystack instanceof Traversable &&
+ !is_array($haystack)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_Count($expectedCount)
+ );
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable
+ * that is stored in an attribute.
+ *
+ * @param int $expectedCount
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
+ {
+ static::assertNotCount(
+ $expectedCount,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that two variables are equal.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+ {
+ $constraint = new PHPUnit_Framework_Constraint_IsEqual(
+ $expected,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a variable is equal to an attribute of an object.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+ {
+ static::assertEquals(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that two variables are not equal.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertNotEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+ {
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_IsEqual(
+ $expected,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ )
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a variable is not equal to an attribute of an object.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+ {
+ static::assertNotEquals(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that a variable is empty.
+ *
+ * @param mixed $actual
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+ public static function assertEmpty($actual, $message = '')
+ {
+ static::assertThat($actual, static::isEmpty(), $message);
+ }
+
+ /**
+ * Asserts that a static attribute of a class or an attribute of an object
+ * is empty.
+ *
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
+ {
+ static::assertEmpty(
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not empty.
+ *
+ * @param mixed $actual
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+ public static function assertNotEmpty($actual, $message = '')
+ {
+ static::assertThat($actual, static::logicalNot(static::isEmpty()), $message);
+ }
+
+ /**
+ * Asserts that a static attribute of a class or an attribute of an object
+ * is not empty.
+ *
+ * @param string $haystackAttributeName
+ * @param string|object $haystackClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
+ {
+ static::assertNotEmpty(
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is greater than another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertGreaterThan($expected, $actual, $message = '')
+ {
+ static::assertThat($actual, static::greaterThan($expected), $message);
+ }
+
+ /**
+ * Asserts that an attribute is greater than another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+ {
+ static::assertGreaterThan(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is greater than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertGreaterThanOrEqual($expected, $actual, $message = '')
+ {
+ static::assertThat(
+ $actual,
+ static::greaterThanOrEqual($expected),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an attribute is greater than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+ {
+ static::assertGreaterThanOrEqual(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is smaller than another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertLessThan($expected, $actual, $message = '')
+ {
+ static::assertThat($actual, static::lessThan($expected), $message);
+ }
+
+ /**
+ * Asserts that an attribute is smaller than another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+ {
+ static::assertLessThan(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is smaller than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertLessThanOrEqual($expected, $actual, $message = '')
+ {
+ static::assertThat($actual, static::lessThanOrEqual($expected), $message);
+ }
+
+ /**
+ * Asserts that an attribute is smaller than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+ {
+ static::assertLessThanOrEqual(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that the contents of one file is equal to the contents of another
+ * file.
+ *
+ * @param string $expected
+ * @param string $actual
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertFileEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
+ {
+ static::assertFileExists($expected, $message);
+ static::assertFileExists($actual, $message);
+
+ static::assertEquals(
+ file_get_contents($expected),
+ file_get_contents($actual),
+ $message,
+ 0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that the contents of one file is not equal to the contents of
+ * another file.
+ *
+ * @param string $expected
+ * @param string $actual
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
+ {
+ static::assertFileExists($expected, $message);
+ static::assertFileExists($actual, $message);
+
+ static::assertNotEquals(
+ file_get_contents($expected),
+ file_get_contents($actual),
+ $message,
+ 0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that the contents of a string is equal
+ * to the contents of a file.
+ *
+ * @param string $expectedFile
+ * @param string $actualString
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
+ {
+ static::assertFileExists($expectedFile, $message);
+
+ static::assertEquals(
+ file_get_contents($expectedFile),
+ $actualString,
+ $message,
+ 0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that the contents of a string is not equal
+ * to the contents of a file.
+ *
+ * @param string $expectedFile
+ * @param string $actualString
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+ public static function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
+ {
+ static::assertFileExists($expectedFile, $message);
+
+ static::assertNotEquals(
+ file_get_contents($expectedFile),
+ $actualString,
+ $message,
+ 0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that a file/dir is readable.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+ public static function assertIsReadable($filename, $message = '')
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_IsReadable;
+
+ static::assertThat($filename, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a file/dir exists and is not readable.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+ public static function assertNotIsReadable($filename, $message = '')
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_IsReadable
+ );
+
+ static::assertThat($filename, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a file/dir exists and is writable.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+ public static function assertIsWritable($filename, $message = '')
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_IsWritable;
+
+ static::assertThat($filename, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a file/dir exists and is not writable.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+ public static function assertNotIsWritable($filename, $message = '')
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_IsWritable
+ );
+
+ static::assertThat($filename, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a directory exists.
+ *
+ * @param string $directory
+ * @param string $message
+ */
+ public static function assertDirectoryExists($directory, $message = '')
+ {
+ if (!is_string($directory)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_DirectoryExists;
+
+ static::assertThat($directory, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a directory does not exist.
+ *
+ * @param string $directory
+ * @param string $message
+ */
+ public static function assertDirectoryNotExists($directory, $message = '')
+ {
+ if (!is_string($directory)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_DirectoryExists
+ );
+
+ static::assertThat($directory, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is readable.
+ *
+ * @param string $directory
+ * @param string $message
+ */
+ public static function assertDirectoryIsReadable($directory, $message = '')
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertIsReadable($directory, $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is not readable.
+ *
+ * @param string $directory
+ * @param string $message
+ */
+ public static function assertDirectoryNotIsReadable($directory, $message = '')
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertNotIsReadable($directory, $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is writable.
+ *
+ * @param string $directory
+ * @param string $message
+ */
+ public static function assertDirectoryIsWritable($directory, $message = '')
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertIsWritable($directory, $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is not writable.
+ *
+ * @param string $directory
+ * @param string $message
+ */
+ public static function assertDirectoryNotIsWritable($directory, $message = '')
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertNotIsWritable($directory, $message);
+ }
+
+ /**
+ * Asserts that a file exists.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+ public static function assertFileExists($filename, $message = '')
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_FileExists;
+
+ static::assertThat($filename, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a file does not exist.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+ public static function assertFileNotExists($filename, $message = '')
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_FileExists
+ );
+
+ static::assertThat($filename, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a file exists and is readable.
+ *
+ * @param string $file
+ * @param string $message
+ */
+ public static function assertFileIsReadable($file, $message = '')
+ {
+ self::assertFileExists($file, $message);
+ self::assertIsReadable($file, $message);
+ }
+
+ /**
+ * Asserts that a file exists and is not readable.
+ *
+ * @param string $file
+ * @param string $message
+ */
+ public static function assertFileNotIsReadable($file, $message = '')
+ {
+ self::assertFileExists($file, $message);
+ self::assertNotIsReadable($file, $message);
+ }
+
+ /**
+ * Asserts that a file exists and is writable.
+ *
+ * @param string $file
+ * @param string $message
+ */
+ public static function assertFileIsWritable($file, $message = '')
+ {
+ self::assertFileExists($file, $message);
+ self::assertIsWritable($file, $message);
+ }
+
+ /**
+ * Asserts that a file exists and is not writable.
+ *
+ * @param string $file
+ * @param string $message
+ */
+ public static function assertFileNotIsWritable($file, $message = '')
+ {
+ self::assertFileExists($file, $message);
+ self::assertNotIsWritable($file, $message);
+ }
+
+ /**
+ * Asserts that a condition is true.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+ public static function assertTrue($condition, $message = '')
+ {
+ static::assertThat($condition, static::isTrue(), $message);
+ }
+
+ /**
+ * Asserts that a condition is not true.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+ public static function assertNotTrue($condition, $message = '')
+ {
+ static::assertThat($condition, static::logicalNot(static::isTrue()), $message);
+ }
+
+ /**
+ * Asserts that a condition is false.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+ public static function assertFalse($condition, $message = '')
+ {
+ static::assertThat($condition, static::isFalse(), $message);
+ }
+
+ /**
+ * Asserts that a condition is not false.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+ public static function assertNotFalse($condition, $message = '')
+ {
+ static::assertThat($condition, static::logicalNot(static::isFalse()), $message);
+ }
+
+ /**
+ * Asserts that a variable is null.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertNull($actual, $message = '')
+ {
+ static::assertThat($actual, static::isNull(), $message);
+ }
+
+ /**
+ * Asserts that a variable is not null.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertNotNull($actual, $message = '')
+ {
+ static::assertThat($actual, static::logicalNot(static::isNull()), $message);
+ }
+
+ /**
+ * Asserts that a variable is finite.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertFinite($actual, $message = '')
+ {
+ static::assertThat($actual, static::isFinite(), $message);
+ }
+
+ /**
+ * Asserts that a variable is infinite.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertInfinite($actual, $message = '')
+ {
+ static::assertThat($actual, static::isInfinite(), $message);
+ }
+
+ /**
+ * Asserts that a variable is nan.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertNan($actual, $message = '')
+ {
+ static::assertThat($actual, static::isNan(), $message);
+ }
+
+ /**
+ * Asserts that a class has a specified attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+ public static function assertClassHasAttribute($attributeName, $className, $message = '')
+ {
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!is_string($className) || !class_exists($className)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_ClassHasAttribute(
+ $attributeName
+ );
+
+ static::assertThat($className, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a class does not have a specified attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+ public static function assertClassNotHasAttribute($attributeName, $className, $message = '')
+ {
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!is_string($className) || !class_exists($className)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_ClassHasAttribute($attributeName)
+ );
+
+ static::assertThat($className, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a class has a specified static attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+ public static function assertClassHasStaticAttribute($attributeName, $className, $message = '')
+ {
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!is_string($className) || !class_exists($className)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_ClassHasStaticAttribute(
+ $attributeName
+ );
+
+ static::assertThat($className, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a class does not have a specified static attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+ public static function assertClassNotHasStaticAttribute($attributeName, $className, $message = '')
+ {
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!is_string($className) || !class_exists($className)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_ClassHasStaticAttribute(
+ $attributeName
+ )
+ );
+
+ static::assertThat($className, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an object has a specified attribute.
+ *
+ * @param string $attributeName
+ * @param object $object
+ * @param string $message
+ */
+ public static function assertObjectHasAttribute($attributeName, $object, $message = '')
+ {
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!is_object($object)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_ObjectHasAttribute(
+ $attributeName
+ );
+
+ static::assertThat($object, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an object does not have a specified attribute.
+ *
+ * @param string $attributeName
+ * @param object $object
+ * @param string $message
+ */
+ public static function assertObjectNotHasAttribute($attributeName, $object, $message = '')
+ {
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!is_object($object)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_ObjectHasAttribute($attributeName)
+ );
+
+ static::assertThat($object, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two variables have the same type and value.
+ * Used on objects, it asserts that two variables reference
+ * the same object.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertSame($expected, $actual, $message = '')
+ {
+ if (is_bool($expected) && is_bool($actual)) {
+ static::assertEquals($expected, $actual, $message);
+ } else {
+ $constraint = new PHPUnit_Framework_Constraint_IsIdentical(
+ $expected
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+ }
+
+ /**
+ * Asserts that a variable and an attribute of an object have the same type
+ * and value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+ {
+ static::assertSame(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that two variables do not have the same type and value.
+ * Used on objects, it asserts that two variables do not reference
+ * the same object.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertNotSame($expected, $actual, $message = '')
+ {
+ if (is_bool($expected) && is_bool($actual)) {
+ static::assertNotEquals($expected, $actual, $message);
+ } else {
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_IsIdentical($expected)
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+ }
+
+ /**
+ * Asserts that a variable and an attribute of an object do not have the
+ * same type and value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string|object $actualClassOrObject
+ * @param string $message
+ */
+ public static function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+ {
+ static::assertNotSame(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertInstanceOf($expected, $actual, $message = '')
+ {
+ if (!(is_string($expected) && (class_exists($expected) || interface_exists($expected)))) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class or interface name');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_IsInstanceOf(
+ $expected
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param string|object $classOrObject
+ * @param string $message
+ */
+ public static function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '')
+ {
+ static::assertInstanceOf(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertNotInstanceOf($expected, $actual, $message = '')
+ {
+ if (!(is_string($expected) && (class_exists($expected) || interface_exists($expected)))) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class or interface name');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_IsInstanceOf($expected)
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param string|object $classOrObject
+ * @param string $message
+ */
+ public static function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '')
+ {
+ static::assertNotInstanceOf(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertInternalType($expected, $actual, $message = '')
+ {
+ if (!is_string($expected)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_IsType(
+ $expected
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param string|object $classOrObject
+ * @param string $message
+ */
+ public static function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '')
+ {
+ static::assertInternalType(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+ public static function assertNotInternalType($expected, $actual, $message = '')
+ {
+ if (!is_string($expected)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_IsType($expected)
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param string|object $classOrObject
+ * @param string $message
+ */
+ public static function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '')
+ {
+ static::assertNotInternalType(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a string matches a given regular expression.
+ *
+ * @param string $pattern
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertRegExp($pattern, $string, $message = '')
+ {
+ if (!is_string($pattern)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_PCREMatch($pattern);
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string does not match a given regular expression.
+ *
+ * @param string $pattern
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertNotRegExp($pattern, $string, $message = '')
+ {
+ if (!is_string($pattern)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_PCREMatch($pattern)
+ );
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
+ * is the same.
+ *
+ * @param array|Countable|Traversable $expected
+ * @param array|Countable|Traversable $actual
+ * @param string $message
+ */
+ public static function assertSameSize($expected, $actual, $message = '')
+ {
+ if (!$expected instanceof Countable &&
+ !$expected instanceof Traversable &&
+ !is_array($expected)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable or traversable');
+ }
+
+ if (!$actual instanceof Countable &&
+ !$actual instanceof Traversable &&
+ !is_array($actual)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
+ }
+
+ static::assertThat(
+ $actual,
+ new PHPUnit_Framework_Constraint_SameSize($expected),
+ $message
+ );
+ }
+
+ /**
+ * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
+ * is not the same.
+ *
+ * @param array|Countable|Traversable $expected
+ * @param array|Countable|Traversable $actual
+ * @param string $message
+ */
+ public static function assertNotSameSize($expected, $actual, $message = '')
+ {
+ if (!$expected instanceof Countable &&
+ !$expected instanceof Traversable &&
+ !is_array($expected)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable or traversable');
+ }
+
+ if (!$actual instanceof Countable &&
+ !$actual instanceof Traversable &&
+ !is_array($actual)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_SameSize($expected)
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string matches a given format string.
+ *
+ * @param string $format
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringMatchesFormat($format, $string, $message = '')
+ {
+ if (!is_string($format)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_StringMatches($format);
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string does not match a given format string.
+ *
+ * @param string $format
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringNotMatchesFormat($format, $string, $message = '')
+ {
+ if (!is_string($format)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_StringMatches($format)
+ );
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string matches a given format file.
+ *
+ * @param string $formatFile
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringMatchesFormatFile($formatFile, $string, $message = '')
+ {
+ static::assertFileExists($formatFile, $message);
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_StringMatches(
+ file_get_contents($formatFile)
+ );
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string does not match a given format string.
+ *
+ * @param string $formatFile
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringNotMatchesFormatFile($formatFile, $string, $message = '')
+ {
+ static::assertFileExists($formatFile, $message);
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_StringMatches(
+ file_get_contents($formatFile)
+ )
+ );
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string starts with a given prefix.
+ *
+ * @param string $prefix
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringStartsWith($prefix, $string, $message = '')
+ {
+ if (!is_string($prefix)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_StringStartsWith(
+ $prefix
+ );
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string starts not with a given prefix.
+ *
+ * @param string $prefix
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringStartsNotWith($prefix, $string, $message = '')
+ {
+ if (!is_string($prefix)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_StringStartsWith($prefix)
+ );
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string ends with a given suffix.
+ *
+ * @param string $suffix
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringEndsWith($suffix, $string, $message = '')
+ {
+ if (!is_string($suffix)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_StringEndsWith($suffix);
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string ends not with a given suffix.
+ *
+ * @param string $suffix
+ * @param string $string
+ * @param string $message
+ */
+ public static function assertStringEndsNotWith($suffix, $string, $message = '')
+ {
+ if (!is_string($suffix)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($string)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $constraint = new PHPUnit_Framework_Constraint_Not(
+ new PHPUnit_Framework_Constraint_StringEndsWith($suffix)
+ );
+
+ static::assertThat($string, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two XML files are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+ public static function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '')
+ {
+ $expected = PHPUnit_Util_XML::loadFile($expectedFile);
+ $actual = PHPUnit_Util_XML::loadFile($actualFile);
+
+ static::assertEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML files are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+ public static function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '')
+ {
+ $expected = PHPUnit_Util_XML::loadFile($expectedFile);
+ $actual = PHPUnit_Util_XML::loadFile($actualFile);
+
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualXml
+ * @param string $message
+ */
+ public static function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '')
+ {
+ $expected = PHPUnit_Util_XML::loadFile($expectedFile);
+ $actual = PHPUnit_Util_XML::load($actualXml);
+
+ static::assertEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualXml
+ * @param string $message
+ */
+ public static function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '')
+ {
+ $expected = PHPUnit_Util_XML::loadFile($expectedFile);
+ $actual = PHPUnit_Util_XML::load($actualXml);
+
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are equal.
+ *
+ * @param string $expectedXml
+ * @param string $actualXml
+ * @param string $message
+ */
+ public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '')
+ {
+ $expected = PHPUnit_Util_XML::load($expectedXml);
+ $actual = PHPUnit_Util_XML::load($actualXml);
+
+ static::assertEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are not equal.
+ *
+ * @param string $expectedXml
+ * @param string $actualXml
+ * @param string $message
+ */
+ public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '')
+ {
+ $expected = PHPUnit_Util_XML::load($expectedXml);
+ $actual = PHPUnit_Util_XML::load($actualXml);
+
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that a hierarchy of DOMElements matches.
+ *
+ * @param DOMElement $expectedElement
+ * @param DOMElement $actualElement
+ * @param bool $checkAttributes
+ * @param string $message
+ */
+ public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = false, $message = '')
+ {
+ $tmp = new DOMDocument;
+ $expectedElement = $tmp->importNode($expectedElement, true);
+
+ $tmp = new DOMDocument;
+ $actualElement = $tmp->importNode($actualElement, true);
+
+ unset($tmp);
+
+ static::assertEquals(
+ $expectedElement->tagName,
+ $actualElement->tagName,
+ $message
+ );
+
+ if ($checkAttributes) {
+ static::assertEquals(
+ $expectedElement->attributes->length,
+ $actualElement->attributes->length,
+ sprintf(
+ '%s%sNumber of attributes on node "%s" does not match',
+ $message,
+ !empty($message) ? "\n" : '',
+ $expectedElement->tagName
+ )
+ );
+
+ for ($i = 0; $i < $expectedElement->attributes->length; $i++) {
+ $expectedAttribute = $expectedElement->attributes->item($i);
+ $actualAttribute = $actualElement->attributes->getNamedItem(
+ $expectedAttribute->name
+ );
+
+ if (!$actualAttribute) {
+ static::fail(
+ sprintf(
+ '%s%sCould not find attribute "%s" on node "%s"',
+ $message,
+ !empty($message) ? "\n" : '',
+ $expectedAttribute->name,
+ $expectedElement->tagName
+ )
+ );
+ }
+ }
+ }
+
+ PHPUnit_Util_XML::removeCharacterDataNodes($expectedElement);
+ PHPUnit_Util_XML::removeCharacterDataNodes($actualElement);
+
+ static::assertEquals(
+ $expectedElement->childNodes->length,
+ $actualElement->childNodes->length,
+ sprintf(
+ '%s%sNumber of child nodes of "%s" differs',
+ $message,
+ !empty($message) ? "\n" : '',
+ $expectedElement->tagName
+ )
+ );
+
+ for ($i = 0; $i < $expectedElement->childNodes->length; $i++) {
+ static::assertEqualXMLStructure(
+ $expectedElement->childNodes->item($i),
+ $actualElement->childNodes->item($i),
+ $checkAttributes,
+ $message
+ );
+ }
+ }
+
+ /**
+ * Evaluates a PHPUnit_Framework_Constraint matcher object.
+ *
+ * @param mixed $value
+ * @param PHPUnit_Framework_Constraint $constraint
+ * @param string $message
+ */
+ public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
+ {
+ self::$count += count($constraint);
+
+ $constraint->evaluate($value, $message);
+ }
+
+ /**
+ * Asserts that a string is a valid JSON string.
+ *
+ * @param string $actualJson
+ * @param string $message
+ */
+ public static function assertJson($actualJson, $message = '')
+ {
+ if (!is_string($actualJson)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ static::assertThat($actualJson, static::isJson(), $message);
+ }
+
+ /**
+ * Asserts that two given JSON encoded objects or arrays are equal.
+ *
+ * @param string $expectedJson
+ * @param string $actualJson
+ * @param string $message
+ */
+ public static function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '')
+ {
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ $expected = json_decode($expectedJson);
+ $actual = json_decode($actualJson);
+
+ static::assertEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two given JSON encoded objects or arrays are not equal.
+ *
+ * @param string $expectedJson
+ * @param string $actualJson
+ * @param string $message
+ */
+ public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '')
+ {
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ $expected = json_decode($expectedJson);
+ $actual = json_decode($actualJson);
+
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that the generated JSON encoded object and the content of the given file are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualJson
+ * @param string $message
+ */
+ public static function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '')
+ {
+ static::assertFileExists($expectedFile, $message);
+ $expectedJson = file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ // call constraint
+ $constraint = new PHPUnit_Framework_Constraint_JsonMatches(
+ $expectedJson
+ );
+
+ static::assertThat($actualJson, $constraint, $message);
+ }
+
+ /**
+ * Asserts that the generated JSON encoded object and the content of the given file are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualJson
+ * @param string $message
+ */
+ public static function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '')
+ {
+ static::assertFileExists($expectedFile, $message);
+ $expectedJson = file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ // call constraint
+ $constraint = new PHPUnit_Framework_Constraint_JsonMatches(
+ $expectedJson
+ );
+
+ static::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraint), $message);
+ }
+
+ /**
+ * Asserts that two JSON files are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+ public static function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '')
+ {
+ static::assertFileExists($expectedFile, $message);
+ static::assertFileExists($actualFile, $message);
+
+ $actualJson = file_get_contents($actualFile);
+ $expectedJson = file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ // call constraint
+ $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches(
+ $expectedJson
+ );
+
+ $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson);
+
+ static::assertThat($expectedJson, $constraintActual, $message);
+ static::assertThat($actualJson, $constraintExpected, $message);
+ }
+
+ /**
+ * Asserts that two JSON files are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+ public static function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '')
+ {
+ static::assertFileExists($expectedFile, $message);
+ static::assertFileExists($actualFile, $message);
+
+ $actualJson = file_get_contents($actualFile);
+ $expectedJson = file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ // call constraint
+ $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches(
+ $expectedJson
+ );
+
+ $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson);
+
+ static::assertThat($expectedJson, new PHPUnit_Framework_Constraint_Not($constraintActual), $message);
+ static::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraintExpected), $message);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_And matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_And
+ */
+ public static function logicalAnd()
+ {
+ $constraints = func_get_args();
+
+ $constraint = new PHPUnit_Framework_Constraint_And;
+ $constraint->setConstraints($constraints);
+
+ return $constraint;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Or matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_Or
+ */
+ public static function logicalOr()
+ {
+ $constraints = func_get_args();
+
+ $constraint = new PHPUnit_Framework_Constraint_Or;
+ $constraint->setConstraints($constraints);
+
+ return $constraint;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Not matcher object.
+ *
+ * @param PHPUnit_Framework_Constraint $constraint
+ *
+ * @return PHPUnit_Framework_Constraint_Not
+ */
+ public static function logicalNot(PHPUnit_Framework_Constraint $constraint)
+ {
+ return new PHPUnit_Framework_Constraint_Not($constraint);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Xor matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_Xor
+ */
+ public static function logicalXor()
+ {
+ $constraints = func_get_args();
+
+ $constraint = new PHPUnit_Framework_Constraint_Xor;
+ $constraint->setConstraints($constraints);
+
+ return $constraint;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsAnything
+ */
+ public static function anything()
+ {
+ return new PHPUnit_Framework_Constraint_IsAnything;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsTrue
+ */
+ public static function isTrue()
+ {
+ return new PHPUnit_Framework_Constraint_IsTrue;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Callback matcher object.
+ *
+ * @param callable $callback
+ *
+ * @return PHPUnit_Framework_Constraint_Callback
+ */
+ public static function callback($callback)
+ {
+ return new PHPUnit_Framework_Constraint_Callback($callback);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsFalse
+ */
+ public static function isFalse()
+ {
+ return new PHPUnit_Framework_Constraint_IsFalse;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsJson matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsJson
+ */
+ public static function isJson()
+ {
+ return new PHPUnit_Framework_Constraint_IsJson;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsNull matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsNull
+ */
+ public static function isNull()
+ {
+ return new PHPUnit_Framework_Constraint_IsNull;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsFinite matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsFinite
+ */
+ public static function isFinite()
+ {
+ return new PHPUnit_Framework_Constraint_IsFinite;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsInfinite matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsInfinite
+ */
+ public static function isInfinite()
+ {
+ return new PHPUnit_Framework_Constraint_IsInfinite;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsNan matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsNan
+ */
+ public static function isNan()
+ {
+ return new PHPUnit_Framework_Constraint_IsNan;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Attribute matcher object.
+ *
+ * @param PHPUnit_Framework_Constraint $constraint
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_Attribute
+ */
+ public static function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName)
+ {
+ return new PHPUnit_Framework_Constraint_Attribute(
+ $constraint,
+ $attributeName
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher
+ * object.
+ *
+ * @param mixed $value
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ *
+ * @return PHPUnit_Framework_Constraint_TraversableContains
+ */
+ public static function contains($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+ {
+ return new PHPUnit_Framework_Constraint_TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
+ * object.
+ *
+ * @param string $type
+ *
+ * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
+ */
+ public static function containsOnly($type)
+ {
+ return new PHPUnit_Framework_Constraint_TraversableContainsOnly($type);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
+ * object.
+ *
+ * @param string $classname
+ *
+ * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
+ */
+ public static function containsOnlyInstancesOf($classname)
+ {
+ return new PHPUnit_Framework_Constraint_TraversableContainsOnly($classname, false);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object.
+ *
+ * @param mixed $key
+ *
+ * @return PHPUnit_Framework_Constraint_ArrayHasKey
+ */
+ public static function arrayHasKey($key)
+ {
+ return new PHPUnit_Framework_Constraint_ArrayHasKey($key);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object.
+ *
+ * @param mixed $value
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ *
+ * @return PHPUnit_Framework_Constraint_IsEqual
+ */
+ public static function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+ {
+ return new PHPUnit_Framework_Constraint_IsEqual(
+ $value,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object
+ * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher
+ * object.
+ *
+ * @param string $attributeName
+ * @param mixed $value
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ *
+ * @return PHPUnit_Framework_Constraint_Attribute
+ */
+ public static function attributeEqualTo($attributeName, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+ {
+ return static::attribute(
+ static::equalTo(
+ $value,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ ),
+ $attributeName
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsEmpty
+ */
+ public static function isEmpty()
+ {
+ return new PHPUnit_Framework_Constraint_IsEmpty;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsWritable matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsWritable
+ */
+ public static function isWritable()
+ {
+ return new PHPUnit_Framework_Constraint_IsWritable;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsReadable matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsReadable
+ */
+ public static function isReadable()
+ {
+ return new PHPUnit_Framework_Constraint_IsReadable;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_DirectoryExists matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_DirectoryExists
+ */
+ public static function directoryExists()
+ {
+ return new PHPUnit_Framework_Constraint_DirectoryExists;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_FileExists matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_FileExists
+ */
+ public static function fileExists()
+ {
+ return new PHPUnit_Framework_Constraint_FileExists;
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_GreaterThan
+ */
+ public static function greaterThan($value)
+ {
+ return new PHPUnit_Framework_Constraint_GreaterThan($value);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
+ * a PHPUnit_Framework_Constraint_IsEqual and a
+ * PHPUnit_Framework_Constraint_GreaterThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_Or
+ */
+ public static function greaterThanOrEqual($value)
+ {
+ return static::logicalOr(
+ new PHPUnit_Framework_Constraint_IsEqual($value),
+ new PHPUnit_Framework_Constraint_GreaterThan($value)
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object.
+ *
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_ClassHasAttribute
+ */
+ public static function classHasAttribute($attributeName)
+ {
+ return new PHPUnit_Framework_Constraint_ClassHasAttribute(
+ $attributeName
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher
+ * object.
+ *
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute
+ */
+ public static function classHasStaticAttribute($attributeName)
+ {
+ return new PHPUnit_Framework_Constraint_ClassHasStaticAttribute(
+ $attributeName
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object.
+ *
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_ObjectHasAttribute
+ */
+ public static function objectHasAttribute($attributeName)
+ {
+ return new PHPUnit_Framework_Constraint_ObjectHasAttribute(
+ $attributeName
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_IsIdentical
+ */
+ public static function identicalTo($value)
+ {
+ return new PHPUnit_Framework_Constraint_IsIdentical($value);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object.
+ *
+ * @param string $className
+ *
+ * @return PHPUnit_Framework_Constraint_IsInstanceOf
+ */
+ public static function isInstanceOf($className)
+ {
+ return new PHPUnit_Framework_Constraint_IsInstanceOf($className);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_IsType matcher object.
+ *
+ * @param string $type
+ *
+ * @return PHPUnit_Framework_Constraint_IsType
+ */
+ public static function isType($type)
+ {
+ return new PHPUnit_Framework_Constraint_IsType($type);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_LessThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_LessThan
+ */
+ public static function lessThan($value)
+ {
+ return new PHPUnit_Framework_Constraint_LessThan($value);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
+ * a PHPUnit_Framework_Constraint_IsEqual and a
+ * PHPUnit_Framework_Constraint_LessThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_Or
+ */
+ public static function lessThanOrEqual($value)
+ {
+ return static::logicalOr(
+ new PHPUnit_Framework_Constraint_IsEqual($value),
+ new PHPUnit_Framework_Constraint_LessThan($value)
+ );
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object.
+ *
+ * @param string $pattern
+ *
+ * @return PHPUnit_Framework_Constraint_PCREMatch
+ */
+ public static function matchesRegularExpression($pattern)
+ {
+ return new PHPUnit_Framework_Constraint_PCREMatch($pattern);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object.
+ *
+ * @param string $string
+ *
+ * @return PHPUnit_Framework_Constraint_StringMatches
+ */
+ public static function matches($string)
+ {
+ return new PHPUnit_Framework_Constraint_StringMatches($string);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object.
+ *
+ * @param mixed $prefix
+ *
+ * @return PHPUnit_Framework_Constraint_StringStartsWith
+ */
+ public static function stringStartsWith($prefix)
+ {
+ return new PHPUnit_Framework_Constraint_StringStartsWith($prefix);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_StringContains matcher object.
+ *
+ * @param string $string
+ * @param bool $case
+ *
+ * @return PHPUnit_Framework_Constraint_StringContains
+ */
+ public static function stringContains($string, $case = true)
+ {
+ return new PHPUnit_Framework_Constraint_StringContains($string, $case);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object.
+ *
+ * @param mixed $suffix
+ *
+ * @return PHPUnit_Framework_Constraint_StringEndsWith
+ */
+ public static function stringEndsWith($suffix)
+ {
+ return new PHPUnit_Framework_Constraint_StringEndsWith($suffix);
+ }
+
+ /**
+ * Returns a PHPUnit_Framework_Constraint_Count matcher object.
+ *
+ * @param int $count
+ *
+ * @return PHPUnit_Framework_Constraint_Count
+ */
+ public static function countOf($count)
+ {
+ return new PHPUnit_Framework_Constraint_Count($count);
+ }
+ /**
+ * Fails a test with the given message.
+ *
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+ public static function fail($message = '')
+ {
+ throw new PHPUnit_Framework_AssertionFailedError($message);
+ }
+
+ /**
+ * Returns the value of an attribute of a class or an object.
+ * This also works for attributes that are declared protected or private.
+ *
+ * @param string|object $classOrObject
+ * @param string $attributeName
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public static function readAttribute($classOrObject, $attributeName)
+ {
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name');
+ }
+
+ if (is_string($classOrObject)) {
+ if (!class_exists($classOrObject)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'class name'
+ );
+ }
+
+ return static::getStaticAttribute(
+ $classOrObject,
+ $attributeName
+ );
+ } elseif (is_object($classOrObject)) {
+ return static::getObjectAttribute(
+ $classOrObject,
+ $attributeName
+ );
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'class name or object'
+ );
+ }
+ }
+
+ /**
+ * Returns the value of a static attribute.
+ * This also works for attributes that are declared protected or private.
+ *
+ * @param string $className
+ * @param string $attributeName
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public static function getStaticAttribute($className, $attributeName)
+ {
+ if (!is_string($className)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!class_exists($className)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class name');
+ }
+
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name');
+ }
+
+ $class = new ReflectionClass($className);
+
+ while ($class) {
+ $attributes = $class->getStaticProperties();
+
+ if (array_key_exists($attributeName, $attributes)) {
+ return $attributes[$attributeName];
+ }
+
+ $class = $class->getParentClass();
+ }
+
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Attribute "%s" not found in class.',
+ $attributeName
+ )
+ );
+ }
+
+ /**
+ * Returns the value of an object's attribute.
+ * This also works for attributes that are declared protected or private.
+ *
+ * @param object $object
+ * @param string $attributeName
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public static function getObjectAttribute($object, $attributeName)
+ {
+ if (!is_object($object)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'object');
+ }
+
+ if (!is_string($attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name');
+ }
+
+ try {
+ $attribute = new ReflectionProperty($object, $attributeName);
+ } catch (ReflectionException $e) {
+ $reflector = new ReflectionObject($object);
+
+ while ($reflector = $reflector->getParentClass()) {
+ try {
+ $attribute = $reflector->getProperty($attributeName);
+ break;
+ } catch (ReflectionException $e) {
+ }
+ }
+ }
+
+ if (isset($attribute)) {
+ if (!$attribute || $attribute->isPublic()) {
+ return $object->$attributeName;
+ }
+
+ $attribute->setAccessible(true);
+ $value = $attribute->getValue($object);
+ $attribute->setAccessible(false);
+
+ return $value;
+ }
+
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Attribute "%s" not found in object.',
+ $attributeName
+ )
+ );
+ }
+
+ /**
+ * Mark the test as incomplete.
+ *
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_IncompleteTestError
+ */
+ public static function markTestIncomplete($message = '')
+ {
+ throw new PHPUnit_Framework_IncompleteTestError($message);
+ }
+
+ /**
+ * Mark the test as skipped.
+ *
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_SkippedTestError
+ */
+ public static function markTestSkipped($message = '')
+ {
+ throw new PHPUnit_Framework_SkippedTestError($message);
+ }
+
+ /**
+ * Return the current assertion count.
+ *
+ * @return int
+ */
+ public static function getCount()
+ {
+ return self::$count;
+ }
+
+ /**
+ * Reset the assertion counter.
+ */
+ public static function resetCount()
+ {
+ self::$count = 0;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Returns a matcher that matches when the method is executed
+ * zero or more times.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
+ */
+function any()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::any',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsAnything
+ */
+function anything()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::anything',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object.
+ *
+ * @param mixed $key
+ *
+ * @return PHPUnit_Framework_Constraint_ArrayHasKey
+ */
+function arrayHasKey($key)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::arrayHasKey',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an array has a specified key.
+ *
+ * @param mixed $key
+ * @param array|ArrayAccess $array
+ * @param string $message
+ */
+function assertArrayHasKey($key, $array, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertArrayHasKey',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an array has a specified subset.
+ *
+ * @param array|ArrayAccess $subset
+ * @param array|ArrayAccess $array
+ * @param bool $strict Check for object identity
+ * @param string $message
+ */
+function assertArraySubset($subset, $array, $strict = false, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertArraySubset',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an array does not have a specified key.
+ *
+ * @param mixed $key
+ * @param array|ArrayAccess $array
+ * @param string $message
+ */
+function assertArrayNotHasKey($key, $array, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertArrayNotHasKey',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object contains a needle.
+ *
+ * @param mixed $needle
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeContains',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object contains only values of a given type.
+ *
+ * @param string $type
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param bool $isNativeType
+ * @param string $message
+ */
+function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeContainsOnly',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts the number of elements of an array, Countable or Traversable
+ * that is stored in an attribute.
+ *
+ * @param int $expectedCount
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param string $message
+ */
+function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeCount',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a static attribute of a class or an attribute of an object
+ * is empty.
+ *
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param string $message
+ */
+function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeEmpty',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is equal to an attribute of an object.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string $actualClassOrObject
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeEquals',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is greater than another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string $actualClassOrObject
+ * @param string $message
+ */
+function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeGreaterThan',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is greater than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string $actualClassOrObject
+ * @param string $message
+ */
+function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeGreaterThanOrEqual',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param mixed $classOrObject
+ * @param string $message
+ */
+function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeInstanceOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param mixed $classOrObject
+ * @param string $message
+ */
+function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeInternalType',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is smaller than another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string $actualClassOrObject
+ * @param string $message
+ */
+function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeLessThan',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is smaller than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string $actualClassOrObject
+ * @param string $message
+ */
+function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeLessThanOrEqual',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object does not contain a needle.
+ *
+ * @param mixed $needle
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotContains',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object does not contain only values of a given
+ * type.
+ *
+ * @param string $type
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param bool $isNativeType
+ * @param string $message
+ */
+function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotContainsOnly',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts the number of elements of an array, Countable or Traversable
+ * that is stored in an attribute.
+ *
+ * @param int $expectedCount
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param string $message
+ */
+function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotCount',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a static attribute of a class or an attribute of an object
+ * is not empty.
+ *
+ * @param string $haystackAttributeName
+ * @param mixed $haystackClassOrObject
+ * @param string $message
+ */
+function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotEmpty',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is not equal to an attribute of an object.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param string $actualClassOrObject
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotEquals',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param mixed $classOrObject
+ * @param string $message
+ */
+function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotInstanceOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param string $expected
+ * @param string $attributeName
+ * @param mixed $classOrObject
+ * @param string $message
+ */
+function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotInternalType',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable and an attribute of an object do not have the
+ * same type and value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param object $actualClassOrObject
+ * @param string $message
+ */
+function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeNotSame',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable and an attribute of an object have the same type
+ * and value.
+ *
+ * @param mixed $expected
+ * @param string $actualAttributeName
+ * @param object $actualClassOrObject
+ * @param string $message
+ */
+function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertAttributeSame',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a class has a specified attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+function assertClassHasAttribute($attributeName, $className, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertClassHasAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a class has a specified static attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+function assertClassHasStaticAttribute($attributeName, $className, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertClassHasStaticAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a class does not have a specified attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+function assertClassNotHasAttribute($attributeName, $className, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertClassNotHasAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a class does not have a specified static attribute.
+ *
+ * @param string $attributeName
+ * @param string $className
+ * @param string $message
+ */
+function assertClassNotHasStaticAttribute($attributeName, $className, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertClassNotHasStaticAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack contains a needle.
+ *
+ * @param mixed $needle
+ * @param mixed $haystack
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+function assertContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertContains',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack contains only values of a given type.
+ *
+ * @param string $type
+ * @param mixed $haystack
+ * @param bool $isNativeType
+ * @param string $message
+ */
+function assertContainsOnly($type, $haystack, $isNativeType = null, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertContainsOnly',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack contains only instances of a given classname
+ *
+ * @param string $classname
+ * @param array|Traversable $haystack
+ * @param string $message
+ */
+function assertContainsOnlyInstancesOf($classname, $haystack, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertContainsOnlyInstancesOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts the number of elements of an array, Countable or Traversable.
+ *
+ * @param int $expectedCount
+ * @param mixed $haystack
+ * @param string $message
+ */
+function assertCount($expectedCount, $haystack, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertCount',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is empty.
+ *
+ * @param mixed $actual
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+function assertEmpty($actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertEmpty',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a hierarchy of DOMElements matches.
+ *
+ * @param DOMElement $expectedElement
+ * @param DOMElement $actualElement
+ * @param bool $checkAttributes
+ * @param string $message
+ */
+function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = false, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertEqualXMLStructure',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two variables are equal.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertEquals',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a condition is not true.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+function assertNotTrue($condition, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotTrue',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a condition is false.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+function assertFalse($condition, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertFalse',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that the contents of one file is equal to the contents of another
+ * file.
+ *
+ * @param string $expected
+ * @param string $actual
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertFileEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertFileEquals',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a file exists.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+function assertFileExists($filename, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertFileExists',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that the contents of one file is not equal to the contents of
+ * another file.
+ *
+ * @param string $expected
+ * @param string $actual
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertFileNotEquals',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a file does not exist.
+ *
+ * @param string $filename
+ * @param string $message
+ */
+function assertFileNotExists($filename, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertFileNotExists',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a value is greater than another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertGreaterThan($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertGreaterThan',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a value is greater than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertGreaterThanOrEqual($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertGreaterThanOrEqual',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertInstanceOf($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertInstanceOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertInternalType($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertInternalType',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string is a valid JSON string.
+ *
+ * @param string $actualJson
+ * @param string $message
+ */
+function assertJson($actualJson, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertJson',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two JSON files are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertJsonFileEqualsJsonFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two JSON files are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertJsonFileNotEqualsJsonFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that the generated JSON encoded object and the content of the given file are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualJson
+ * @param string $message
+ */
+function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertJsonStringEqualsJsonFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two given JSON encoded objects or arrays are equal.
+ *
+ * @param string $expectedJson
+ * @param string $actualJson
+ * @param string $message
+ */
+function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertJsonStringEqualsJsonString',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that the generated JSON encoded object and the content of the given file are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualJson
+ * @param string $message
+ */
+function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two given JSON encoded objects or arrays are not equal.
+ *
+ * @param string $expectedJson
+ * @param string $actualJson
+ * @param string $message
+ */
+function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonString',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a value is smaller than another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertLessThan($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertLessThan',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a value is smaller than or equal to another value.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertLessThanOrEqual($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertLessThanOrEqual',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is finite.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertFinite($actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertFinite',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is infinite.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertInfinite($actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertInfinite',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is nan.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertNan($actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNan',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack does not contain a needle.
+ *
+ * @param mixed $needle
+ * @param mixed $haystack
+ * @param string $message
+ * @param bool $ignoreCase
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ */
+function assertNotContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotContains',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a haystack does not contain only values of a given type.
+ *
+ * @param string $type
+ * @param mixed $haystack
+ * @param bool $isNativeType
+ * @param string $message
+ */
+function assertNotContainsOnly($type, $haystack, $isNativeType = null, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotContainsOnly',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts the number of elements of an array, Countable or Traversable.
+ *
+ * @param int $expectedCount
+ * @param mixed $haystack
+ * @param string $message
+ */
+function assertNotCount($expectedCount, $haystack, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotCount',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is not empty.
+ *
+ * @param mixed $actual
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+function assertNotEmpty($actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotEmpty',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two variables are not equal.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertNotEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotEquals',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is not of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertNotInstanceOf($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotInstanceOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is not of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertNotInternalType($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotInternalType',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a condition is not false.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+function assertNotFalse($condition, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotFalse',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is not null.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertNotNull($actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotNull',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string does not match a given regular expression.
+ *
+ * @param string $pattern
+ * @param string $string
+ * @param string $message
+ */
+function assertNotRegExp($pattern, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotRegExp',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two variables do not have the same type and value.
+ * Used on objects, it asserts that two variables do not reference
+ * the same object.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertNotSame($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotSame',
+ func_get_args()
+ );
+}
+
+/**
+ * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
+ * is not the same.
+ *
+ * @param array|Countable|Traversable $expected
+ * @param array|Countable|Traversable $actual
+ * @param string $message
+ */
+function assertNotSameSize($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNotSameSize',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a variable is null.
+ *
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertNull($actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertNull',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an object has a specified attribute.
+ *
+ * @param string $attributeName
+ * @param object $object
+ * @param string $message
+ */
+function assertObjectHasAttribute($attributeName, $object, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertObjectHasAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that an object does not have a specified attribute.
+ *
+ * @param string $attributeName
+ * @param object $object
+ * @param string $message
+ */
+function assertObjectNotHasAttribute($attributeName, $object, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertObjectNotHasAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string matches a given regular expression.
+ *
+ * @param string $pattern
+ * @param string $string
+ * @param string $message
+ */
+function assertRegExp($pattern, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertRegExp',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two variables have the same type and value.
+ * Used on objects, it asserts that two variables reference
+ * the same object.
+ *
+ * @param mixed $expected
+ * @param mixed $actual
+ * @param string $message
+ */
+function assertSame($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertSame',
+ func_get_args()
+ );
+}
+
+/**
+ * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
+ * is the same.
+ *
+ * @param array|Countable|Traversable $expected
+ * @param array|Countable|Traversable $actual
+ * @param string $message
+ */
+function assertSameSize($expected, $actual, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertSameSize',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string ends not with a given prefix.
+ *
+ * @param string $suffix
+ * @param string $string
+ * @param string $message
+ */
+function assertStringEndsNotWith($suffix, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringEndsNotWith',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string ends with a given prefix.
+ *
+ * @param string $suffix
+ * @param string $string
+ * @param string $message
+ */
+function assertStringEndsWith($suffix, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringEndsWith',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that the contents of a string is equal
+ * to the contents of a file.
+ *
+ * @param string $expectedFile
+ * @param string $actualString
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringEqualsFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string matches a given format string.
+ *
+ * @param string $format
+ * @param string $string
+ * @param string $message
+ */
+function assertStringMatchesFormat($format, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringMatchesFormat',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string matches a given format file.
+ *
+ * @param string $formatFile
+ * @param string $string
+ * @param string $message
+ */
+function assertStringMatchesFormatFile($formatFile, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringMatchesFormatFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that the contents of a string is not equal
+ * to the contents of a file.
+ *
+ * @param string $expectedFile
+ * @param string $actualString
+ * @param string $message
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ */
+function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringNotEqualsFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string does not match a given format string.
+ *
+ * @param string $format
+ * @param string $string
+ * @param string $message
+ */
+function assertStringNotMatchesFormat($format, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringNotMatchesFormat',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string does not match a given format string.
+ *
+ * @param string $formatFile
+ * @param string $string
+ * @param string $message
+ */
+function assertStringNotMatchesFormatFile($formatFile, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringNotMatchesFormatFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string starts not with a given prefix.
+ *
+ * @param string $prefix
+ * @param string $string
+ * @param string $message
+ */
+function assertStringStartsNotWith($prefix, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringStartsNotWith',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a string starts with a given prefix.
+ *
+ * @param string $prefix
+ * @param string $string
+ * @param string $message
+ */
+function assertStringStartsWith($prefix, $string, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertStringStartsWith',
+ func_get_args()
+ );
+}
+
+/**
+ * Evaluates a PHPUnit_Framework_Constraint matcher object.
+ *
+ * @param mixed $value
+ * @param PHPUnit_Framework_Constraint $constraint
+ * @param string $message
+ */
+function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertThat',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that a condition is true.
+ *
+ * @param bool $condition
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_AssertionFailedError
+ */
+function assertTrue($condition, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertTrue',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two XML files are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertXmlFileEqualsXmlFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two XML files are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualFile
+ * @param string $message
+ */
+function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertXmlFileNotEqualsXmlFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two XML documents are equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualXml
+ * @param string $message
+ */
+function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertXmlStringEqualsXmlFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two XML documents are equal.
+ *
+ * @param string $expectedXml
+ * @param string $actualXml
+ * @param string $message
+ */
+function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertXmlStringEqualsXmlString',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two XML documents are not equal.
+ *
+ * @param string $expectedFile
+ * @param string $actualXml
+ * @param string $message
+ */
+function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlFile',
+ func_get_args()
+ );
+}
+
+/**
+ * Asserts that two XML documents are not equal.
+ *
+ * @param string $expectedXml
+ * @param string $actualXml
+ * @param string $message
+ */
+function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '')
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlString',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a matcher that matches when the method is executed
+ * at the given $index.
+ *
+ * @param int $index
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex
+ */
+function at($index)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::at',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a matcher that matches when the method is executed at least once.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce
+ */
+function atLeastOnce()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::atLeastOnce',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Attribute matcher object.
+ *
+ * @param PHPUnit_Framework_Constraint $constraint
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_Attribute
+ */
+function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::attribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object
+ * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher
+ * object.
+ *
+ * @param string $attributeName
+ * @param mixed $value
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ *
+ * @return PHPUnit_Framework_Constraint_Attribute
+ */
+function attributeEqualTo($attributeName, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::attributeEqualTo',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Callback matcher object.
+ *
+ * @param callable $callback
+ *
+ * @return PHPUnit_Framework_Constraint_Callback
+ */
+function callback($callback)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::callback',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object.
+ *
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_ClassHasAttribute
+ */
+function classHasAttribute($attributeName)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::classHasAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher
+ * object.
+ *
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute
+ */
+function classHasStaticAttribute($attributeName)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::classHasStaticAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher
+ * object.
+ *
+ * @param mixed $value
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ *
+ * @return PHPUnit_Framework_Constraint_TraversableContains
+ */
+function contains($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::contains',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
+ * object.
+ *
+ * @param string $type
+ *
+ * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
+ */
+function containsOnly($type)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::containsOnly',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher
+ * object.
+ *
+ * @param string $classname
+ *
+ * @return PHPUnit_Framework_Constraint_TraversableContainsOnly
+ */
+function containsOnlyInstancesOf($classname)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::containsOnlyInstancesOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Count matcher object.
+ *
+ * @param int $count
+ *
+ * @return Count
+ */
+function countOf($count)
+{
+ return call_user_func_array(
+ 'PHPUnit\Framework\Assert::countOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object.
+ *
+ * @param mixed $value
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ *
+ * @return PHPUnit_Framework_Constraint_IsEqual
+ */
+function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::equalTo',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a matcher that matches when the method is executed
+ * exactly $count times.
+ *
+ * @param int $count
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
+ */
+function exactly($count)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::exactly',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_FileExists matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_FileExists
+ */
+function fileExists()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::fileExists',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_GreaterThan
+ */
+function greaterThan($value)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::greaterThan',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
+ * a PHPUnit_Framework_Constraint_IsEqual and a
+ * PHPUnit_Framework_Constraint_GreaterThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_Or
+ */
+function greaterThanOrEqual($value)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::greaterThanOrEqual',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_IsIdentical
+ */
+function identicalTo($value)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::identicalTo',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsEmpty
+ */
+function isEmpty()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::isEmpty',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsFalse
+ */
+function isFalse()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::isFalse',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object.
+ *
+ * @param string $className
+ *
+ * @return PHPUnit_Framework_Constraint_IsInstanceOf
+ */
+function isInstanceOf($className)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::isInstanceOf',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsJson matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsJson
+ */
+function isJson()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::isJson',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsNull matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsNull
+ */
+function isNull()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::isNull',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_IsTrue
+ */
+function isTrue()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::isTrue',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_IsType matcher object.
+ *
+ * @param string $type
+ *
+ * @return PHPUnit_Framework_Constraint_IsType
+ */
+function isType($type)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::isType',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_LessThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_LessThan
+ */
+function lessThan($value)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::lessThan',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps
+ * a PHPUnit_Framework_Constraint_IsEqual and a
+ * PHPUnit_Framework_Constraint_LessThan matcher object.
+ *
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Constraint_Or
+ */
+function lessThanOrEqual($value)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::lessThanOrEqual',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_And matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_And
+ */
+function logicalAnd()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::logicalAnd',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Not matcher object.
+ *
+ * @param PHPUnit_Framework_Constraint $constraint
+ *
+ * @return PHPUnit_Framework_Constraint_Not
+ */
+function logicalNot(PHPUnit_Framework_Constraint $constraint)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::logicalNot',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Or matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_Or
+ */
+function logicalOr()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::logicalOr',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_Xor matcher object.
+ *
+ * @return PHPUnit_Framework_Constraint_Xor
+ */
+function logicalXor()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::logicalXor',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object.
+ *
+ * @param string $string
+ *
+ * @return PHPUnit_Framework_Constraint_StringMatches
+ */
+function matches($string)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::matches',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object.
+ *
+ * @param string $pattern
+ *
+ * @return PHPUnit_Framework_Constraint_PCREMatch
+ */
+function matchesRegularExpression($pattern)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::matchesRegularExpression',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a matcher that matches when the method is never executed.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
+ */
+function never()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::never',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object.
+ *
+ * @param string $attributeName
+ *
+ * @return PHPUnit_Framework_Constraint_ObjectHasAttribute
+ */
+function objectHasAttribute($attributeName)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::objectHasAttribute',
+ func_get_args()
+ );
+}
+
+/**
+ * @param mixed $value, ...
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls
+ */
+function onConsecutiveCalls()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::onConsecutiveCalls',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a matcher that matches when the method is executed exactly once.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
+ */
+function once()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::once',
+ func_get_args()
+ );
+}
+
+/**
+ * @param int $argumentIndex
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument
+ */
+function returnArgument($argumentIndex)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::returnArgument',
+ func_get_args()
+ );
+}
+
+/**
+ * @param mixed $callback
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback
+ */
+function returnCallback($callback)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::returnCallback',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns the current object.
+ *
+ * This method is useful when mocking a fluent interface.
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf
+ */
+function returnSelf()
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::returnSelf',
+ func_get_args()
+ );
+}
+
+/**
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_Return
+ */
+function returnValue($value)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::returnValue',
+ func_get_args()
+ );
+}
+
+/**
+ * @param array $valueMap
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap
+ */
+function returnValueMap(array $valueMap)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::returnValueMap',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_StringContains matcher object.
+ *
+ * @param string $string
+ * @param bool $case
+ *
+ * @return PHPUnit_Framework_Constraint_StringContains
+ */
+function stringContains($string, $case = true)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::stringContains',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object.
+ *
+ * @param mixed $suffix
+ *
+ * @return PHPUnit_Framework_Constraint_StringEndsWith
+ */
+function stringEndsWith($suffix)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::stringEndsWith',
+ func_get_args()
+ );
+}
+
+/**
+ * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object.
+ *
+ * @param mixed $prefix
+ *
+ * @return PHPUnit_Framework_Constraint_StringStartsWith
+ */
+function stringStartsWith($prefix)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_Assert::stringStartsWith',
+ func_get_args()
+ );
+}
+
+/**
+ * @param Exception $exception
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_Exception
+ */
+function throwException(Exception $exception)
+{
+ return call_user_func_array(
+ 'PHPUnit_Framework_TestCase::throwException',
+ func_get_args()
+ );
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Thrown when an assertion failed.
+ */
+class PHPUnit_Framework_AssertionFailedError extends PHPUnit_Framework_Exception implements PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * Wrapper for getMessage() which is declared as final.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->getMessage();
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An empty Listener that can be extended to implement TestListener
+ * with just a few lines of code.
+ *
+ * @see PHPUnit_Framework_TestListener for documentation on the API methods.
+ */
+abstract class PHPUnit_Framework_BaseTestListener implements PHPUnit_Framework_TestListener
+{
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ }
+
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ }
+
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ }
+
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_CodeCoverageException extends PHPUnit_Framework_Exception
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Exporter\Exporter;
+
+/**
+ * Abstract base class for constraints which can be applied to any value.
+ */
+abstract class PHPUnit_Framework_Constraint implements Countable, PHPUnit_Framework_SelfDescribing
+{
+ protected $exporter;
+
+ public function __construct()
+ {
+ $this->exporter = new Exporter;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ $success = false;
+
+ if ($this->matches($other)) {
+ $success = true;
+ }
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (!$success) {
+ $this->fail($other, $description);
+ }
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * This method can be overridden to implement the evaluation algorithm.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return false;
+ }
+
+ /**
+ * Counts the number of constraint elements.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return 1;
+ }
+
+ /**
+ * Throws an exception for the given compared value and test description
+ *
+ * @param mixed $other Evaluated value or object.
+ * @param string $description Additional information about the test
+ * @param SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ protected function fail($other, $description, SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null)
+ {
+ $failureDescription = sprintf(
+ 'Failed asserting that %s.',
+ $this->failureDescription($other)
+ );
+
+ $additionalFailureDescription = $this->additionalFailureDescription($other);
+
+ if ($additionalFailureDescription) {
+ $failureDescription .= "\n" . $additionalFailureDescription;
+ }
+
+ if (!empty($description)) {
+ $failureDescription = $description . "\n" . $failureDescription;
+ }
+
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ $failureDescription,
+ $comparisonFailure
+ );
+ }
+
+ /**
+ * Return additional failure description where needed
+ *
+ * The function can be overridden to provide additional failure
+ * information like a diff
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function additionalFailureDescription($other)
+ {
+ return '';
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * To provide additional failure information additionalFailureDescription
+ * can be used.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return $this->exporter->export($other) . ' ' . $this->toString();
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Logical AND.
+ */
+class PHPUnit_Framework_Constraint_And extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Framework_Constraint[]
+ */
+ protected $constraints = [];
+
+ /**
+ * @var PHPUnit_Framework_Constraint
+ */
+ protected $lastConstraint = null;
+
+ /**
+ * @param PHPUnit_Framework_Constraint[] $constraints
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setConstraints(array $constraints)
+ {
+ $this->constraints = [];
+
+ foreach ($constraints as $constraint) {
+ if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
+ throw new PHPUnit_Framework_Exception(
+ 'All parameters to ' . __CLASS__ .
+ ' must be a constraint object.'
+ );
+ }
+
+ $this->constraints[] = $constraint;
+ }
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ $success = true;
+ $constraint = null;
+
+ foreach ($this->constraints as $constraint) {
+ if (!$constraint->evaluate($other, $description, true)) {
+ $success = false;
+ break;
+ }
+ }
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (!$success) {
+ $this->fail($other, $description);
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $text = '';
+
+ foreach ($this->constraints as $key => $constraint) {
+ if ($key > 0) {
+ $text .= ' and ';
+ }
+
+ $text .= $constraint->toString();
+ }
+
+ return $text;
+ }
+
+ /**
+ * Counts the number of constraint elements.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ $count = 0;
+
+ foreach ($this->constraints as $constraint) {
+ $count += count($constraint);
+ }
+
+ return $count;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the array it is evaluated for has a given key.
+ *
+ * Uses array_key_exists() to check if the key is found in the input array, if
+ * not found the evaluation fails.
+ *
+ * The array key is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_ArrayHasKey extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var int|string
+ */
+ protected $key;
+
+ /**
+ * @param int|string $key
+ */
+ public function __construct($key)
+ {
+ parent::__construct();
+ $this->key = $key;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ if (is_array($other)) {
+ return array_key_exists($this->key, $other);
+ }
+
+ if ($other instanceof ArrayAccess) {
+ return $other->offsetExists($this->key);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'has the key ' . $this->exporter->export($this->key);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return 'an array ' . $this->toString();
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the array it is evaluated for has a specified subset.
+ *
+ * Uses array_replace_recursive() to check if a key value subset is part of the
+ * subject array.
+ */
+class PHPUnit_Framework_Constraint_ArraySubset extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var array|Traversable
+ */
+ protected $subset;
+
+ /**
+ * @var bool
+ */
+ protected $strict;
+
+ /**
+ * @param array|Traversable $subset
+ * @param bool $strict Check for object identity
+ */
+ public function __construct($subset, $strict = false)
+ {
+ parent::__construct();
+ $this->strict = $strict;
+ $this->subset = $subset;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param array|Traversable $other Array or Traversable object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ //type cast $other & $this->subset as an array to allow
+ //support in standard array functions.
+ $other = $this->toArray($other);
+ $this->subset = $this->toArray($this->subset);
+
+ $patched = array_replace_recursive($other, $this->subset);
+
+ if ($this->strict) {
+ return $other === $patched;
+ } else {
+ return $other == $patched;
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'has the subset ' . $this->exporter->export($this->subset);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return 'an array ' . $this->toString();
+ }
+
+ /**
+ * @param array|Traversable $other
+ *
+ * @return array
+ */
+ private function toArray($other)
+ {
+ if (is_array($other)) {
+ return $other;
+ } elseif ($other instanceof ArrayObject) {
+ return $other->getArrayCopy();
+ } elseif ($other instanceof Traversable) {
+ return iterator_to_array($other);
+ }
+
+ // Keep BC even if we know that array would not be the expected one
+ return (array) $other;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_Constraint_Attribute extends PHPUnit_Framework_Constraint_Composite
+{
+ /**
+ * @var string
+ */
+ protected $attributeName;
+
+ /**
+ * @param PHPUnit_Framework_Constraint $constraint
+ * @param string $attributeName
+ */
+ public function __construct(PHPUnit_Framework_Constraint $constraint, $attributeName)
+ {
+ parent::__construct($constraint);
+
+ $this->attributeName = $attributeName;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ return parent::evaluate(
+ PHPUnit_Framework_Assert::readAttribute(
+ $other,
+ $this->attributeName
+ ),
+ $description,
+ $returnResult
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'attribute "' . $this->attributeName . '" ' .
+ $this->innerConstraint->toString();
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return $this->toString();
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that evaluates against a specified closure.
+ */
+class PHPUnit_Framework_Constraint_Callback extends PHPUnit_Framework_Constraint
+{
+ private $callback;
+
+ /**
+ * @param callable $callback
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($callback)
+ {
+ if (!is_callable($callback)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'callable'
+ );
+ }
+
+ parent::__construct();
+
+ $this->callback = $callback;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $value. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return call_user_func($this->callback, $other);
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is accepted by specified callback';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the class it is evaluated for has a given
+ * attribute.
+ *
+ * The attribute name is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_ClassHasAttribute extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $attributeName;
+
+ /**
+ * @param string $attributeName
+ */
+ public function __construct($attributeName)
+ {
+ parent::__construct();
+ $this->attributeName = $attributeName;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ $class = new ReflectionClass($other);
+
+ return $class->hasProperty($this->attributeName);
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'has attribute "%s"',
+ $this->attributeName
+ );
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ '%sclass "%s" %s',
+ is_object($other) ? 'object of ' : '',
+ is_object($other) ? get_class($other) : $other,
+ $this->toString()
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the class it is evaluated for has a given
+ * static attribute.
+ *
+ * The attribute name is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_ClassHasStaticAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ $class = new ReflectionClass($other);
+
+ if ($class->hasProperty($this->attributeName)) {
+ $attribute = $class->getProperty($this->attributeName);
+
+ return $attribute->isStatic();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'has static attribute "%s"',
+ $this->attributeName
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+abstract class PHPUnit_Framework_Constraint_Composite extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Framework_Constraint
+ */
+ protected $innerConstraint;
+
+ /**
+ * @param PHPUnit_Framework_Constraint $innerConstraint
+ */
+ public function __construct(PHPUnit_Framework_Constraint $innerConstraint)
+ {
+ parent::__construct();
+ $this->innerConstraint = $innerConstraint;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ try {
+ return $this->innerConstraint->evaluate(
+ $other,
+ $description,
+ $returnResult
+ );
+ } catch (PHPUnit_Framework_ExpectationFailedException $e) {
+ $this->fail($other, $description);
+ }
+ }
+
+ /**
+ * Counts the number of constraint elements.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->innerConstraint);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_Constraint_Count extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var int
+ */
+ protected $expectedCount = 0;
+
+ /**
+ * @param int $expected
+ */
+ public function __construct($expected)
+ {
+ parent::__construct();
+ $this->expectedCount = $expected;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $this->expectedCount === $this->getCountOf($other);
+ }
+
+ /**
+ * @param mixed $other
+ *
+ * @return bool
+ */
+ protected function getCountOf($other)
+ {
+ if ($other instanceof Countable || is_array($other)) {
+ return count($other);
+ } elseif ($other instanceof Traversable) {
+ if ($other instanceof IteratorAggregate) {
+ $iterator = $other->getIterator();
+ } else {
+ $iterator = $other;
+ }
+
+ if ($iterator instanceof Generator) {
+ return $this->getCountOfGenerator($iterator);
+ }
+
+ $key = $iterator->key();
+ $count = iterator_count($iterator);
+
+ // Manually rewind $iterator to previous key, since iterator_count
+ // moves pointer.
+ if ($key !== null) {
+ $iterator->rewind();
+ while ($iterator->valid() && $key !== $iterator->key()) {
+ $iterator->next();
+ }
+ }
+
+ return $count;
+ }
+ }
+
+ /**
+ * Returns the total number of iterations from a generator.
+ * This will fully exhaust the generator.
+ *
+ * @param Generator $generator
+ *
+ * @return int
+ */
+ protected function getCountOfGenerator(Generator $generator)
+ {
+ for ($count = 0; $generator->valid(); $generator->next()) {
+ $count += 1;
+ }
+
+ return $count;
+ }
+
+ /**
+ * Returns the description of the failure.
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ 'actual size %d matches expected size %d',
+ $this->getCountOf($other),
+ $this->expectedCount
+ );
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'count matches %d',
+ $this->expectedCount
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that checks if the directory(name) that it is evaluated for exists.
+ *
+ * The file path to check is passed as $other in evaluate().
+ */
+class PHPUnit_Framework_Constraint_DirectoryExists extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return is_dir($other);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ 'directory "%s" exists',
+ $other
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'directory exists';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_Constraint_Exception extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $className;
+
+ /**
+ * @param string $className
+ */
+ public function __construct($className)
+ {
+ parent::__construct();
+ $this->className = $className;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $other instanceof $this->className;
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ if ($other !== null) {
+ $message = '';
+ if ($other instanceof Exception || $other instanceof Throwable) {
+ $message = '. Message was: "' . $other->getMessage() . '" at'
+ . "\n" . PHPUnit_Util_Filter::getFilteredStacktrace($other);
+ }
+
+ return sprintf(
+ 'exception of type "%s" matches expected exception "%s"%s',
+ get_class($other),
+ $this->className,
+ $message
+ );
+ }
+
+ return sprintf(
+ 'exception of type "%s" is thrown',
+ $this->className
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'exception of type "%s"',
+ $this->className
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_Constraint_ExceptionCode extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var int
+ */
+ protected $expectedCode;
+
+ /**
+ * @param int $expected
+ */
+ public function __construct($expected)
+ {
+ parent::__construct();
+ $this->expectedCode = $expected;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param Exception $other
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return (string) $other->getCode() == (string) $this->expectedCode;
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ '%s is equal to expected exception code %s',
+ $this->exporter->export($other->getCode()),
+ $this->exporter->export($this->expectedCode)
+ );
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'exception code is ';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_Constraint_ExceptionMessage extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var int
+ */
+ protected $expectedMessage;
+
+ /**
+ * @param string $expected
+ */
+ public function __construct($expected)
+ {
+ parent::__construct();
+ $this->expectedMessage = $expected;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param Exception $other
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return strpos($other->getMessage(), $this->expectedMessage) !== false;
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ "exception message '%s' contains '%s'",
+ $other->getMessage(),
+ $this->expectedMessage
+ );
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'exception message contains ';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_Constraint_ExceptionMessageRegExp extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var int
+ */
+ protected $expectedMessageRegExp;
+
+ /**
+ * @param string $expected
+ */
+ public function __construct($expected)
+ {
+ parent::__construct();
+ $this->expectedMessageRegExp = $expected;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param Exception $other
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ $match = PHPUnit_Util_Regex::pregMatchSafe($this->expectedMessageRegExp, $other->getMessage());
+
+ if (false === $match) {
+ throw new PHPUnit_Framework_Exception(
+ "Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'"
+ );
+ }
+
+ return 1 === $match;
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ "exception message '%s' matches '%s'",
+ $other->getMessage(),
+ $this->expectedMessageRegExp
+ );
+ }
+
+ /**
+ * @return string
+ */
+ public function toString()
+ {
+ return 'exception message matches ';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that checks if the file(name) that it is evaluated for exists.
+ *
+ * The file path to check is passed as $other in evaluate().
+ */
+class PHPUnit_Framework_Constraint_FileExists extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return file_exists($other);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ 'file "%s" exists',
+ $other
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'file exists';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the value it is evaluated for is greater
+ * than a given value.
+ */
+class PHPUnit_Framework_Constraint_GreaterThan extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var numeric
+ */
+ protected $value;
+
+ /**
+ * @param numeric $value
+ */
+ public function __construct($value)
+ {
+ parent::__construct();
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $this->value < $other;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is greater than ' . $this->exporter->export($this->value);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that accepts any input value.
+ */
+class PHPUnit_Framework_Constraint_IsAnything extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ return $returnResult ? true : null;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is anything';
+ }
+
+ /**
+ * Counts the number of constraint elements.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return 0;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that checks whether a variable is empty().
+ */
+class PHPUnit_Framework_Constraint_IsEmpty extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ if ($other instanceof Countable) {
+ return count($other) === 0;
+ }
+
+ return empty($other);
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is empty';
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ $type = gettype($other);
+
+ return sprintf(
+ '%s %s %s',
+ $type[0] == 'a' || $type[0] == 'o' ? 'an' : 'a',
+ $type,
+ $this->toString()
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that checks if one value is equal to another.
+ *
+ * Equality is checked with PHP's == operator, the operator is explained in
+ * detail at {@url http://www.php.net/manual/en/types.comparisons.php}.
+ * Two values are equal if they have the same value disregarding type.
+ *
+ * The expected value is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_IsEqual extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var mixed
+ */
+ protected $value;
+
+ /**
+ * @var float
+ */
+ protected $delta = 0.0;
+
+ /**
+ * @var int
+ */
+ protected $maxDepth = 10;
+
+ /**
+ * @var bool
+ */
+ protected $canonicalize = false;
+
+ /**
+ * @var bool
+ */
+ protected $ignoreCase = false;
+
+ /**
+ * @var SebastianBergmann\Comparator\ComparisonFailure
+ */
+ protected $lastFailure;
+
+ /**
+ * @param mixed $value
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
+ {
+ parent::__construct();
+
+ if (!is_numeric($delta)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'numeric');
+ }
+
+ if (!is_int($maxDepth)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'integer');
+ }
+
+ if (!is_bool($canonicalize)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean');
+ }
+
+ if (!is_bool($ignoreCase)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'boolean');
+ }
+
+ $this->value = $value;
+ $this->delta = $delta;
+ $this->maxDepth = $maxDepth;
+ $this->canonicalize = $canonicalize;
+ $this->ignoreCase = $ignoreCase;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ // If $this->value and $other are identical, they are also equal.
+ // This is the most common path and will allow us to skip
+ // initialization of all the comparators.
+ if ($this->value === $other) {
+ return true;
+ }
+
+ $comparatorFactory = SebastianBergmann\Comparator\Factory::getInstance();
+
+ try {
+ $comparator = $comparatorFactory->getComparatorFor(
+ $this->value,
+ $other
+ );
+
+ $comparator->assertEquals(
+ $this->value,
+ $other,
+ $this->delta,
+ $this->canonicalize,
+ $this->ignoreCase
+ );
+ } catch (SebastianBergmann\Comparator\ComparisonFailure $f) {
+ if ($returnResult) {
+ return false;
+ }
+
+ throw new PHPUnit_Framework_ExpectationFailedException(
+ trim($description . "\n" . $f->getMessage()),
+ $f
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $delta = '';
+
+ if (is_string($this->value)) {
+ if (strpos($this->value, "\n") !== false) {
+ return 'is equal to <text>';
+ } else {
+ return sprintf(
+ 'is equal to <string:%s>',
+ $this->value
+ );
+ }
+ } else {
+ if ($this->delta != 0) {
+ $delta = sprintf(
+ ' with delta <%F>',
+ $this->delta
+ );
+ }
+
+ return sprintf(
+ 'is equal to %s%s',
+ $this->exporter->export($this->value),
+ $delta
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that accepts false.
+ */
+class PHPUnit_Framework_Constraint_IsFalse extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $other === false;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is false';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that accepts finite.
+ */
+class PHPUnit_Framework_Constraint_IsFinite extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return is_finite($other);
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is finite';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that one value is identical to another.
+ *
+ * Identical check is performed with PHP's === operator, the operator is
+ * explained in detail at
+ * {@url http://www.php.net/manual/en/types.comparisons.php}.
+ * Two values are identical if they have the same value and are of the same
+ * type.
+ *
+ * The expected value is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_IsIdentical extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var float
+ */
+ const EPSILON = 0.0000000001;
+
+ /**
+ * @var mixed
+ */
+ protected $value;
+
+ /**
+ * @param mixed $value
+ */
+ public function __construct($value)
+ {
+ parent::__construct();
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ if (is_float($this->value) && is_float($other) &&
+ !is_infinite($this->value) && !is_infinite($other) &&
+ !is_nan($this->value) && !is_nan($other)) {
+ $success = abs($this->value - $other) < self::EPSILON;
+ } else {
+ $success = $this->value === $other;
+ }
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (!$success) {
+ $f = null;
+
+ // if both values are strings, make sure a diff is generated
+ if (is_string($this->value) && is_string($other)) {
+ $f = new SebastianBergmann\Comparator\ComparisonFailure(
+ $this->value,
+ $other,
+ $this->value,
+ $other
+ );
+ }
+
+ $this->fail($other, $description, $f);
+ }
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ if (is_object($this->value) && is_object($other)) {
+ return 'two variables reference the same object';
+ }
+
+ if (is_string($this->value) && is_string($other)) {
+ return 'two strings are identical';
+ }
+
+ return parent::failureDescription($other);
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ if (is_object($this->value)) {
+ return 'is identical to an object of class "' .
+ get_class($this->value) . '"';
+ } else {
+ return 'is identical to ' .
+ $this->exporter->export($this->value);
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that accepts infinite.
+ */
+class PHPUnit_Framework_Constraint_IsInfinite extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return is_infinite($other);
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is infinite';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the object it is evaluated for is an instance
+ * of a given class.
+ *
+ * The expected class name is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_IsInstanceOf extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $className;
+
+ /**
+ * @param string $className
+ */
+ public function __construct($className)
+ {
+ parent::__construct();
+ $this->className = $className;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return ($other instanceof $this->className);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ '%s is an instance of %s "%s"',
+ $this->exporter->shortenedExport($other),
+ $this->getType(),
+ $this->className
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'is instance of %s "%s"',
+ $this->getType(),
+ $this->className
+ );
+ }
+
+ private function getType()
+ {
+ try {
+ $reflection = new ReflectionClass($this->className);
+ if ($reflection->isInterface()) {
+ return 'interface';
+ }
+ } catch (ReflectionException $e) {
+ }
+
+ return 'class';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that a string is valid JSON.
+ */
+class PHPUnit_Framework_Constraint_IsJson extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ if ($other === '') {
+ return false;
+ }
+
+ json_decode($other);
+ if (json_last_error()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ if ($other === '') {
+ return 'an empty string is valid JSON';
+ }
+
+ json_decode($other);
+ $error = PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError(
+ json_last_error()
+ );
+
+ return sprintf(
+ '%s is valid JSON (%s)',
+ $this->exporter->shortenedExport($other),
+ $error
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is valid JSON';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that accepts nan.
+ */
+class PHPUnit_Framework_Constraint_IsNan extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return is_nan($other);
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is nan';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that accepts null.
+ */
+class PHPUnit_Framework_Constraint_IsNull extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $other === null;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is null';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that checks if the file/dir(name) that it is evaluated for is readable.
+ *
+ * The file path to check is passed as $other in evaluate().
+ */
+class PHPUnit_Framework_Constraint_IsReadable extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return is_readable($other);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ '"%s" is readable',
+ $other
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is readable';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that accepts true.
+ */
+class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $other === true;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is true';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the value it is evaluated for is of a
+ * specified type.
+ *
+ * The expected value is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_IsType extends PHPUnit_Framework_Constraint
+{
+ const TYPE_ARRAY = 'array';
+ const TYPE_BOOL = 'bool';
+ const TYPE_FLOAT = 'float';
+ const TYPE_INT = 'int';
+ const TYPE_NULL = 'null';
+ const TYPE_NUMERIC = 'numeric';
+ const TYPE_OBJECT = 'object';
+ const TYPE_RESOURCE = 'resource';
+ const TYPE_STRING = 'string';
+ const TYPE_SCALAR = 'scalar';
+ const TYPE_CALLABLE = 'callable';
+
+ /**
+ * @var array
+ */
+ protected $types = [
+ 'array' => true,
+ 'boolean' => true,
+ 'bool' => true,
+ 'double' => true,
+ 'float' => true,
+ 'integer' => true,
+ 'int' => true,
+ 'null' => true,
+ 'numeric' => true,
+ 'object' => true,
+ 'real' => true,
+ 'resource' => true,
+ 'string' => true,
+ 'scalar' => true,
+ 'callable' => true
+ ];
+
+ /**
+ * @var string
+ */
+ protected $type;
+
+ /**
+ * @param string $type
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($type)
+ {
+ parent::__construct();
+
+ if (!isset($this->types[$type])) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Type specified for PHPUnit_Framework_Constraint_IsType <%s> ' .
+ 'is not a valid type.',
+ $type
+ )
+ );
+ }
+
+ $this->type = $type;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ switch ($this->type) {
+ case 'numeric':
+ return is_numeric($other);
+
+ case 'integer':
+ case 'int':
+ return is_int($other);
+
+ case 'double':
+ case 'float':
+ case 'real':
+ return is_float($other);
+
+ case 'string':
+ return is_string($other);
+
+ case 'boolean':
+ case 'bool':
+ return is_bool($other);
+
+ case 'null':
+ return is_null($other);
+
+ case 'array':
+ return is_array($other);
+
+ case 'object':
+ return is_object($other);
+
+ case 'resource':
+ return is_resource($other) || is_string(@get_resource_type($other));
+
+ case 'scalar':
+ return is_scalar($other);
+
+ case 'callable':
+ return is_callable($other);
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'is of type "%s"',
+ $this->type
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that checks if the file/dir(name) that it is evaluated for is writable.
+ *
+ * The file path to check is passed as $other in evaluate().
+ */
+class PHPUnit_Framework_Constraint_IsWritable extends PHPUnit_Framework_Constraint
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return is_writable($other);
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ '"%s" is writable',
+ $other
+ );
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is writable';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Asserts whether or not two JSON objects are equal.
+ */
+class PHPUnit_Framework_Constraint_JsonMatches extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Creates a new constraint.
+ *
+ * @param string $value
+ */
+ public function __construct($value)
+ {
+ parent::__construct();
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * This method can be overridden to implement the evaluation algorithm.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ $decodedOther = json_decode($other);
+ if (json_last_error()) {
+ return false;
+ }
+
+ $decodedValue = json_decode($this->value);
+ if (json_last_error()) {
+ return false;
+ }
+
+ return $decodedOther == $decodedValue;
+ }
+
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'matches JSON string "%s"',
+ $this->value
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Provides human readable messages for each JSON error.
+ */
+class PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider
+{
+ /**
+ * Translates JSON error to a human readable string.
+ *
+ * @param string $error
+ * @param string $prefix
+ *
+ * @return string
+ */
+ public static function determineJsonError($error, $prefix = '')
+ {
+ switch ($error) {
+ case JSON_ERROR_NONE:
+ return;
+ case JSON_ERROR_DEPTH:
+ return $prefix . 'Maximum stack depth exceeded';
+ case JSON_ERROR_STATE_MISMATCH:
+ return $prefix . 'Underflow or the modes mismatch';
+ case JSON_ERROR_CTRL_CHAR:
+ return $prefix . 'Unexpected control character found';
+ case JSON_ERROR_SYNTAX:
+ return $prefix . 'Syntax error, malformed JSON';
+ case JSON_ERROR_UTF8:
+ return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded';
+ default:
+ return $prefix . 'Unknown error';
+ }
+ }
+
+ /**
+ * Translates a given type to a human readable message prefix.
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function translateTypeToPrefix($type)
+ {
+ switch (strtolower($type)) {
+ case 'expected':
+ $prefix = 'Expected value JSON decode error - ';
+ break;
+ case 'actual':
+ $prefix = 'Actual value JSON decode error - ';
+ break;
+ default:
+ $prefix = '';
+ break;
+ }
+
+ return $prefix;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the value it is evaluated for is less than
+ * a given value.
+ */
+class PHPUnit_Framework_Constraint_LessThan extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var numeric
+ */
+ protected $value;
+
+ /**
+ * @param numeric $value
+ */
+ public function __construct($value)
+ {
+ parent::__construct();
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return $this->value > $other;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'is less than ' . $this->exporter->export($this->value);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Logical NOT.
+ */
+class PHPUnit_Framework_Constraint_Not extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Framework_Constraint
+ */
+ protected $constraint;
+
+ /**
+ * @param PHPUnit_Framework_Constraint $constraint
+ */
+ public function __construct($constraint)
+ {
+ parent::__construct();
+
+ if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
+ $constraint = new PHPUnit_Framework_Constraint_IsEqual($constraint);
+ }
+
+ $this->constraint = $constraint;
+ }
+
+ /**
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function negate($string)
+ {
+ return str_replace(
+ [
+ 'contains ',
+ 'exists',
+ 'has ',
+ 'is ',
+ 'are ',
+ 'matches ',
+ 'starts with ',
+ 'ends with ',
+ 'reference ',
+ 'not not '
+ ],
+ [
+ 'does not contain ',
+ 'does not exist',
+ 'does not have ',
+ 'is not ',
+ 'are not ',
+ 'does not match ',
+ 'starts not with ',
+ 'ends not with ',
+ 'don\'t reference ',
+ 'not '
+ ],
+ $string
+ );
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ $success = !$this->constraint->evaluate($other, $description, true);
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (!$success) {
+ $this->fail($other, $description);
+ }
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ switch (get_class($this->constraint)) {
+ case 'PHPUnit_Framework_Constraint_And':
+ case 'PHPUnit_Framework_Constraint_Not':
+ case 'PHPUnit_Framework_Constraint_Or':
+ return 'not( ' . $this->constraint->failureDescription($other) . ' )';
+
+ default:
+ return self::negate(
+ $this->constraint->failureDescription($other)
+ );
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ switch (get_class($this->constraint)) {
+ case 'PHPUnit_Framework_Constraint_And':
+ case 'PHPUnit_Framework_Constraint_Not':
+ case 'PHPUnit_Framework_Constraint_Or':
+ return 'not( ' . $this->constraint->toString() . ' )';
+
+ default:
+ return self::negate(
+ $this->constraint->toString()
+ );
+ }
+ }
+
+ /**
+ * Counts the number of constraint elements.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->constraint);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the object it is evaluated for has a given
+ * attribute.
+ *
+ * The attribute name is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_ObjectHasAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute
+{
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ $object = new ReflectionObject($other);
+
+ return $object->hasProperty($this->attributeName);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Logical OR.
+ */
+class PHPUnit_Framework_Constraint_Or extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Framework_Constraint[]
+ */
+ protected $constraints = [];
+
+ /**
+ * @param PHPUnit_Framework_Constraint[] $constraints
+ */
+ public function setConstraints(array $constraints)
+ {
+ $this->constraints = [];
+
+ foreach ($constraints as $constraint) {
+ if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
+ $constraint = new PHPUnit_Framework_Constraint_IsEqual(
+ $constraint
+ );
+ }
+
+ $this->constraints[] = $constraint;
+ }
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ $success = false;
+ $constraint = null;
+
+ foreach ($this->constraints as $constraint) {
+ if ($constraint->evaluate($other, $description, true)) {
+ $success = true;
+ break;
+ }
+ }
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (!$success) {
+ $this->fail($other, $description);
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $text = '';
+
+ foreach ($this->constraints as $key => $constraint) {
+ if ($key > 0) {
+ $text .= ' or ';
+ }
+
+ $text .= $constraint->toString();
+ }
+
+ return $text;
+ }
+
+ /**
+ * Counts the number of constraint elements.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ $count = 0;
+
+ foreach ($this->constraints as $constraint) {
+ $count += count($constraint);
+ }
+
+ return $count;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the string it is evaluated for matches
+ * a regular expression.
+ *
+ * Checks a given value using the Perl Compatible Regular Expression extension
+ * in PHP. The pattern is matched by executing preg_match().
+ *
+ * The pattern string passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_PCREMatch extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $pattern;
+
+ /**
+ * @param string $pattern
+ */
+ public function __construct($pattern)
+ {
+ parent::__construct();
+ $this->pattern = $pattern;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return preg_match($this->pattern, $other) > 0;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ 'matches PCRE pattern "%s"',
+ $this->pattern
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_Constraint_SameSize extends PHPUnit_Framework_Constraint_Count
+{
+ /**
+ * @var int
+ */
+ protected $expectedCount;
+
+ /**
+ * @param int $expected
+ */
+ public function __construct($expected)
+ {
+ parent::__construct($this->getCountOf($expected));
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the string it is evaluated for contains
+ * a given string.
+ *
+ * Uses mb_strpos() to find the position of the string in the input, if not
+ * found the evaluation fails.
+ *
+ * The sub-string is passed in the constructor.
+ */
+class PHPUnit_Framework_Constraint_StringContains extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $string;
+
+ /**
+ * @var bool
+ */
+ protected $ignoreCase;
+
+ /**
+ * @param string $string
+ * @param bool $ignoreCase
+ */
+ public function __construct($string, $ignoreCase = false)
+ {
+ parent::__construct();
+
+ $this->string = $string;
+ $this->ignoreCase = $ignoreCase;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ if ($this->ignoreCase) {
+ return mb_stripos($other, $this->string) !== false;
+ } else {
+ return mb_strpos($other, $this->string) !== false;
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ if ($this->ignoreCase) {
+ $string = mb_strtolower($this->string);
+ } else {
+ $string = $this->string;
+ }
+
+ return sprintf(
+ 'contains "%s"',
+ $string
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the string it is evaluated for ends with a given
+ * suffix.
+ */
+class PHPUnit_Framework_Constraint_StringEndsWith extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $suffix;
+
+ /**
+ * @param string $suffix
+ */
+ public function __construct($suffix)
+ {
+ parent::__construct();
+ $this->suffix = $suffix;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return substr($other, 0 - strlen($this->suffix)) == $this->suffix;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'ends with "' . $this->suffix . '"';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Diff\Differ;
+
+/**
+ * ...
+ */
+class PHPUnit_Framework_Constraint_StringMatches extends PHPUnit_Framework_Constraint_PCREMatch
+{
+ /**
+ * @var string
+ */
+ protected $string;
+
+ /**
+ * @param string $string
+ */
+ public function __construct($string)
+ {
+ parent::__construct($string);
+
+ $this->pattern = $this->createPatternFromFormat(
+ preg_replace('/\r\n/', "\n", $string)
+ );
+
+ $this->string = $string;
+ }
+
+ protected function failureDescription($other)
+ {
+ return 'format description matches text';
+ }
+
+ protected function additionalFailureDescription($other)
+ {
+ $from = preg_split('(\r\n|\r|\n)', $this->string);
+ $to = preg_split('(\r\n|\r|\n)', $other);
+
+ foreach ($from as $index => $line) {
+ if (isset($to[$index]) && $line !== $to[$index]) {
+ $line = $this->createPatternFromFormat($line);
+
+ if (preg_match($line, $to[$index]) > 0) {
+ $from[$index] = $to[$index];
+ }
+ }
+ }
+
+ $this->string = implode("\n", $from);
+ $other = implode("\n", $to);
+
+ $differ = new Differ("--- Expected\n+++ Actual\n");
+
+ return $differ->diff($this->string, $other);
+ }
+
+ protected function createPatternFromFormat($string)
+ {
+ $string = str_replace(
+ [
+ '%e',
+ '%s',
+ '%S',
+ '%a',
+ '%A',
+ '%w',
+ '%i',
+ '%d',
+ '%x',
+ '%f',
+ '%c'
+ ],
+ [
+ '\\' . DIRECTORY_SEPARATOR,
+ '[^\r\n]+',
+ '[^\r\n]*',
+ '.+',
+ '.*',
+ '\s*',
+ '[+-]?\d+',
+ '\d+',
+ '[0-9a-fA-F]+',
+ '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?',
+ '.'
+ ],
+ preg_quote($string, '/')
+ );
+
+ return '/^' . $string . '$/s';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the string it is evaluated for begins with a
+ * given prefix.
+ */
+class PHPUnit_Framework_Constraint_StringStartsWith extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var string
+ */
+ protected $prefix;
+
+ /**
+ * @param string $prefix
+ */
+ public function __construct($prefix)
+ {
+ parent::__construct();
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ return strpos($other, $this->prefix) === 0;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'starts with "' . $this->prefix . '"';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the Traversable it is applied to contains
+ * a given value.
+ */
+class PHPUnit_Framework_Constraint_TraversableContains extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var bool
+ */
+ protected $checkForObjectIdentity;
+
+ /**
+ * @var bool
+ */
+ protected $checkForNonObjectIdentity;
+
+ /**
+ * @var mixed
+ */
+ protected $value;
+
+ /**
+ * @param mixed $value
+ * @param bool $checkForObjectIdentity
+ * @param bool $checkForNonObjectIdentity
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false)
+ {
+ parent::__construct();
+
+ if (!is_bool($checkForObjectIdentity)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean');
+ }
+
+ if (!is_bool($checkForNonObjectIdentity)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'boolean');
+ }
+
+ $this->checkForObjectIdentity = $checkForObjectIdentity;
+ $this->checkForNonObjectIdentity = $checkForNonObjectIdentity;
+ $this->value = $value;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns true if the
+ * constraint is met, false otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ *
+ * @return bool
+ */
+ protected function matches($other)
+ {
+ if ($other instanceof SplObjectStorage) {
+ return $other->contains($this->value);
+ }
+
+ if (is_object($this->value)) {
+ foreach ($other as $element) {
+ if ($this->checkForObjectIdentity && $element === $this->value) {
+ return true;
+ } elseif (!$this->checkForObjectIdentity && $element == $this->value) {
+ return true;
+ }
+ }
+ } else {
+ foreach ($other as $element) {
+ if ($this->checkForNonObjectIdentity && $element === $this->value) {
+ return true;
+ } elseif (!$this->checkForNonObjectIdentity && $element == $this->value) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ if (is_string($this->value) && strpos($this->value, "\n") !== false) {
+ return 'contains "' . $this->value . '"';
+ } else {
+ return 'contains ' . $this->exporter->export($this->value);
+ }
+ }
+
+ /**
+ * Returns the description of the failure
+ *
+ * The beginning of failure messages is "Failed asserting that" in most
+ * cases. This method should return the second part of that sentence.
+ *
+ * @param mixed $other Evaluated value or object.
+ *
+ * @return string
+ */
+ protected function failureDescription($other)
+ {
+ return sprintf(
+ '%s %s',
+ is_array($other) ? 'an array' : 'a traversable',
+ $this->toString()
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Constraint that asserts that the Traversable it is applied to contains
+ * only values of a given type.
+ */
+class PHPUnit_Framework_Constraint_TraversableContainsOnly extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Framework_Constraint
+ */
+ protected $constraint;
+
+ /**
+ * @var string
+ */
+ protected $type;
+
+ /**
+ * @param string $type
+ * @param bool $isNativeType
+ */
+ public function __construct($type, $isNativeType = true)
+ {
+ parent::__construct();
+
+ if ($isNativeType) {
+ $this->constraint = new PHPUnit_Framework_Constraint_IsType($type);
+ } else {
+ $this->constraint = new PHPUnit_Framework_Constraint_IsInstanceOf(
+ $type
+ );
+ }
+
+ $this->type = $type;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ $success = true;
+
+ foreach ($other as $item) {
+ if (!$this->constraint->evaluate($item, '', true)) {
+ $success = false;
+ break;
+ }
+ }
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (!$success) {
+ $this->fail($other, $description);
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'contains only values of type "' . $this->type . '"';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Logical XOR.
+ */
+class PHPUnit_Framework_Constraint_Xor extends PHPUnit_Framework_Constraint
+{
+ /**
+ * @var PHPUnit_Framework_Constraint[]
+ */
+ protected $constraints = [];
+
+ /**
+ * @param PHPUnit_Framework_Constraint[] $constraints
+ */
+ public function setConstraints(array $constraints)
+ {
+ $this->constraints = [];
+
+ foreach ($constraints as $constraint) {
+ if (!($constraint instanceof PHPUnit_Framework_Constraint)) {
+ $constraint = new PHPUnit_Framework_Constraint_IsEqual(
+ $constraint
+ );
+ }
+
+ $this->constraints[] = $constraint;
+ }
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other
+ *
+ * If $returnResult is set to false (the default), an exception is thrown
+ * in case of a failure. null is returned otherwise.
+ *
+ * If $returnResult is true, the result of the evaluation is returned as
+ * a boolean value instead: true in case of success, false in case of a
+ * failure.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @param string $description Additional information about the test
+ * @param bool $returnResult Whether to return a result or throw an exception
+ *
+ * @return mixed
+ *
+ * @throws PHPUnit_Framework_ExpectationFailedException
+ */
+ public function evaluate($other, $description = '', $returnResult = false)
+ {
+ $success = true;
+ $lastResult = null;
+ $constraint = null;
+
+ foreach ($this->constraints as $constraint) {
+ $result = $constraint->evaluate($other, $description, true);
+
+ if ($result === $lastResult) {
+ $success = false;
+ break;
+ }
+
+ $lastResult = $result;
+ }
+
+ if ($returnResult) {
+ return $success;
+ }
+
+ if (!$success) {
+ $this->fail($other, $description);
+ }
+ }
+
+ /**
+ * Returns a string representation of the constraint.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $text = '';
+
+ foreach ($this->constraints as $key => $constraint) {
+ if ($key > 0) {
+ $text .= ' xor ';
+ }
+
+ $text .= $constraint->toString();
+ }
+
+ return $text;
+ }
+
+ /**
+ * Counts the number of constraint elements.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ $count = 0;
+
+ foreach ($this->constraints as $constraint) {
+ $count += count($constraint);
+ }
+
+ return $count;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
+ * case of a test that does not execute the code it wants to cover.
+ */
+class PHPUnit_Framework_CoveredCodeNotExecutedException extends PHPUnit_Framework_RiskyTestError
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Wrapper for PHP errors.
+ */
+class PHPUnit_Framework_Error extends PHPUnit_Framework_Exception
+{
+ /**
+ * Constructor.
+ *
+ * @param string $message
+ * @param int $code
+ * @param string $file
+ * @param int $line
+ * @param Exception $previous
+ */
+ public function __construct($message, $code, $file, $line, Exception $previous = null)
+ {
+ parent::__construct($message, $code, $previous);
+
+ $this->file = $file;
+ $this->line = $line;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Wrapper for PHP deprecated errors.
+ * You can disable deprecated-to-exception conversion by setting
+ *
+ * <code>
+ * PHPUnit_Framework_Error_Deprecated::$enabled = false;
+ * </code>
+ */
+class PHPUnit_Framework_Error_Deprecated extends PHPUnit_Framework_Error
+{
+ public static $enabled = true;
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Wrapper for PHP notices.
+ * You can disable notice-to-exception conversion by setting
+ *
+ * <code>
+ * PHPUnit_Framework_Error_Notice::$enabled = false;
+ * </code>
+ */
+class PHPUnit_Framework_Error_Notice extends PHPUnit_Framework_Error
+{
+ public static $enabled = true;
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Wrapper for PHP warnings.
+ * You can disable notice-to-exception conversion by setting
+ *
+ * <code>
+ * PHPUnit_Framework_Error_Warning::$enabled = false;
+ * </code>
+ */
+class PHPUnit_Framework_Error_Warning extends PHPUnit_Framework_Error
+{
+ public static $enabled = true;
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Base class for all PHPUnit Framework exceptions.
+ *
+ * Ensures that exceptions thrown during a test run do not leave stray
+ * references behind.
+ *
+ * Every Exception contains a stack trace. Each stack frame contains the 'args'
+ * of the called function. The function arguments can contain references to
+ * instantiated objects. The references prevent the objects from being
+ * destructed (until test results are eventually printed), so memory cannot be
+ * freed up.
+ *
+ * With enabled process isolation, test results are serialized in the child
+ * process and unserialized in the parent process. The stack trace of Exceptions
+ * may contain objects that cannot be serialized or unserialized (e.g., PDO
+ * connections). Unserializing user-space objects from the child process into
+ * the parent would break the intended encapsulation of process isolation.
+ *
+ * @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions
+ */
+class PHPUnit_Framework_Exception extends RuntimeException implements PHPUnit_Exception
+{
+ /**
+ * @var array
+ */
+ protected $serializableTrace;
+
+ public function __construct($message = '', $code = 0, Exception $previous = null)
+ {
+ parent::__construct($message, $code, $previous);
+
+ $this->serializableTrace = $this->getTrace();
+ foreach ($this->serializableTrace as $i => $call) {
+ unset($this->serializableTrace[$i]['args']);
+ }
+ }
+
+ /**
+ * Returns the serializable trace (without 'args').
+ *
+ * @return array
+ */
+ public function getSerializableTrace()
+ {
+ return $this->serializableTrace;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ $string = PHPUnit_Framework_TestFailure::exceptionToString($this);
+
+ if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) {
+ $string .= "\n" . $trace;
+ }
+
+ return $string;
+ }
+
+ public function __sleep()
+ {
+ return array_keys(get_object_vars($this));
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Wraps Exceptions thrown by code under test.
+ *
+ * Re-instantiates Exceptions thrown by user-space code to retain their original
+ * class names, properties, and stack traces (but without arguments).
+ *
+ * Unlike PHPUnit_Framework_Exception, the complete stack of previous Exceptions
+ * is processed.
+ */
+class PHPUnit_Framework_ExceptionWrapper extends PHPUnit_Framework_Exception
+{
+ /**
+ * @var string
+ */
+ protected $className;
+
+ /**
+ * @var PHPUnit_Framework_ExceptionWrapper|null
+ */
+ protected $previous;
+
+ /**
+ * @param Throwable|Exception $e
+ */
+ public function __construct($e)
+ {
+ // PDOException::getCode() is a string.
+ // @see http://php.net/manual/en/class.pdoexception.php#95812
+ parent::__construct($e->getMessage(), (int) $e->getCode());
+
+ $this->className = get_class($e);
+ $this->file = $e->getFile();
+ $this->line = $e->getLine();
+
+ $this->serializableTrace = $e->getTrace();
+
+ foreach ($this->serializableTrace as $i => $call) {
+ unset($this->serializableTrace[$i]['args']);
+ }
+
+ if ($e->getPrevious()) {
+ $this->previous = new self($e->getPrevious());
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getClassName()
+ {
+ return $this->className;
+ }
+
+ /**
+ * @return PHPUnit_Framework_ExceptionWrapper
+ */
+ public function getPreviousWrapped()
+ {
+ return $this->previous;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ $string = PHPUnit_Framework_TestFailure::exceptionToString($this);
+
+ if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) {
+ $string .= "\n" . $trace;
+ }
+
+ if ($this->previous) {
+ $string .= "\nCaused by\n" . $this->previous;
+ }
+
+ return $string;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Exception for expectations which failed their check.
+ *
+ * The exception contains the error message and optionally a
+ * SebastianBergmann\Comparator\ComparisonFailure which is used to
+ * generate diff output of the failed expectations.
+ */
+class PHPUnit_Framework_ExpectationFailedException extends PHPUnit_Framework_AssertionFailedError
+{
+ /**
+ * @var SebastianBergmann\Comparator\ComparisonFailure
+ */
+ protected $comparisonFailure;
+
+ public function __construct($message, SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null, Exception $previous = null)
+ {
+ $this->comparisonFailure = $comparisonFailure;
+
+ parent::__construct($message, 0, $previous);
+ }
+
+ /**
+ * @return SebastianBergmann\Comparator\ComparisonFailure
+ */
+ public function getComparisonFailure()
+ {
+ return $this->comparisonFailure;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A marker interface for marking any exception/error as result of an unit
+ * test as incomplete implementation or currently not implemented.
+ */
+interface PHPUnit_Framework_IncompleteTest
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An incomplete test case
+ */
+class PHPUnit_Framework_IncompleteTestCase extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @var string
+ */
+ protected $message = '';
+
+ /**
+ * @var bool
+ */
+ protected $backupGlobals = false;
+
+ /**
+ * @var bool
+ */
+ protected $backupStaticAttributes = false;
+
+ /**
+ * @var bool
+ */
+ protected $runTestInSeparateProcess = false;
+
+ /**
+ * @var bool
+ */
+ protected $useErrorHandler = false;
+
+ /**
+ * @var bool
+ */
+ protected $useOutputBuffering = false;
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ * @param string $message
+ */
+ public function __construct($className, $methodName, $message = '')
+ {
+ $this->message = $message;
+ parent::__construct($className . '::' . $methodName);
+ }
+
+ /**
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function runTest()
+ {
+ $this->markTestIncomplete($this->message);
+ }
+
+ /**
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Returns a string representation of the test case.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->getName();
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
+ * case of an incomplete test.
+ */
+class PHPUnit_Framework_IncompleteTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_IncompleteTest
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_InvalidCoversTargetException extends PHPUnit_Framework_CodeCoverageException
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark a test as risky
+ * when it does not have a @covers annotation but is expected to have one.
+ */
+class PHPUnit_Framework_MissingCoversAnnotationException extends PHPUnit_Framework_RiskyTestError
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
+ * case of a test that printed output.
+ */
+class PHPUnit_Framework_OutputError extends PHPUnit_Framework_AssertionFailedError
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A marker interface for marking any exception/error as result of an unit
+ * test as risky.
+ */
+interface PHPUnit_Framework_RiskyTest
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
+ * case of a risky test.
+ */
+class PHPUnit_Framework_RiskyTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_RiskyTest
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface for classes that can return a description of itself.
+ */
+interface PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return string
+ */
+ public function toString();
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A marker interface for marking a unit test as being skipped.
+ */
+interface PHPUnit_Framework_SkippedTest
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A skipped test case
+ */
+class PHPUnit_Framework_SkippedTestCase extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @var string
+ */
+ protected $message = '';
+
+ /**
+ * @var bool
+ */
+ protected $backupGlobals = false;
+
+ /**
+ * @var bool
+ */
+ protected $backupStaticAttributes = false;
+
+ /**
+ * @var bool
+ */
+ protected $runTestInSeparateProcess = false;
+
+ /**
+ * @var bool
+ */
+ protected $useErrorHandler = false;
+
+ /**
+ * @var bool
+ */
+ protected $useOutputBuffering = false;
+
+ /**
+ * @param string $message
+ */
+ public function __construct($className, $methodName, $message = '')
+ {
+ $this->message = $message;
+ parent::__construct($className . '::' . $methodName);
+ }
+
+ /**
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function runTest()
+ {
+ $this->markTestSkipped($this->message);
+ }
+
+ /**
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Returns a string representation of the test case.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->getName();
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
+ * case of a skipped test.
+ */
+class PHPUnit_Framework_SkippedTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
+ * case of a skipped test suite.
+ */
+class PHPUnit_Framework_SkippedTestSuiteError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Creates a synthetic failed assertion.
+ */
+class PHPUnit_Framework_SyntheticError extends PHPUnit_Framework_AssertionFailedError
+{
+ /**
+ * The synthetic file.
+ *
+ * @var string
+ */
+ protected $syntheticFile = '';
+
+ /**
+ * The synthetic line number.
+ *
+ * @var int
+ */
+ protected $syntheticLine = 0;
+
+ /**
+ * The synthetic trace.
+ *
+ * @var array
+ */
+ protected $syntheticTrace = [];
+
+ /**
+ * Constructor.
+ *
+ * @param string $message
+ * @param int $code
+ * @param string $file
+ * @param int $line
+ * @param array $trace
+ */
+ public function __construct($message, $code, $file, $line, $trace)
+ {
+ parent::__construct($message, $code);
+
+ $this->syntheticFile = $file;
+ $this->syntheticLine = $line;
+ $this->syntheticTrace = $trace;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSyntheticFile()
+ {
+ return $this->syntheticFile;
+ }
+
+ /**
+ * @return int
+ */
+ public function getSyntheticLine()
+ {
+ return $this->syntheticLine;
+ }
+
+ /**
+ * @return array
+ */
+ public function getSyntheticTrace()
+ {
+ return $this->syntheticTrace;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A Test can be run and collect its results.
+ */
+interface PHPUnit_Framework_Test extends Countable
+{
+ /**
+ * Runs a test and collects its result in a TestResult instance.
+ *
+ * @param PHPUnit_Framework_TestResult $result
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ public function run(PHPUnit_Framework_TestResult $result = null);
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\GlobalState\Snapshot;
+use SebastianBergmann\GlobalState\Restorer;
+use SebastianBergmann\GlobalState\Blacklist;
+use SebastianBergmann\Diff\Differ;
+use SebastianBergmann\Exporter\Exporter;
+use SebastianBergmann\ObjectEnumerator\Enumerator;
+use Prophecy\Exception\Prediction\PredictionException;
+use Prophecy\Prophet;
+use DeepCopy\DeepCopy;
+
+/**
+ * A TestCase defines the fixture to run multiple tests.
+ *
+ * To define a TestCase
+ *
+ * 1) Implement a subclass of PHPUnit_Framework_TestCase.
+ * 2) Define instance variables that store the state of the fixture.
+ * 3) Initialize the fixture state by overriding setUp().
+ * 4) Clean-up after a test by overriding tearDown().
+ *
+ * Each test runs in its own fixture so there can be no side effects
+ * among test runs.
+ *
+ * Here is an example:
+ *
+ * <code>
+ * <?php
+ * class MathTest extends PHPUnit_Framework_TestCase
+ * {
+ * public $value1;
+ * public $value2;
+ *
+ * protected function setUp()
+ * {
+ * $this->value1 = 2;
+ * $this->value2 = 3;
+ * }
+ * }
+ * ?>
+ * </code>
+ *
+ * For each test implement a method which interacts with the fixture.
+ * Verify the expected results with assertions specified by calling
+ * assert with a boolean.
+ *
+ * <code>
+ * <?php
+ * public function testPass()
+ * {
+ * $this->assertTrue($this->value1 + $this->value2 == 5);
+ * }
+ * ?>
+ * </code>
+ */
+abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * Enable or disable the backup and restoration of the $GLOBALS array.
+ * Overwrite this attribute in a child class of TestCase.
+ * Setting this attribute in setUp() has no effect!
+ *
+ * @var bool
+ */
+ protected $backupGlobals = null;
+
+ /**
+ * @var array
+ */
+ protected $backupGlobalsBlacklist = [];
+
+ /**
+ * Enable or disable the backup and restoration of static attributes.
+ * Overwrite this attribute in a child class of TestCase.
+ * Setting this attribute in setUp() has no effect!
+ *
+ * @var bool
+ */
+ protected $backupStaticAttributes = null;
+
+ /**
+ * @var array
+ */
+ protected $backupStaticAttributesBlacklist = [];
+
+ /**
+ * Whether or not this test is to be run in a separate PHP process.
+ *
+ * @var bool
+ */
+ protected $runTestInSeparateProcess = null;
+
+ /**
+ * Whether or not this test should preserve the global state when
+ * running in a separate PHP process.
+ *
+ * @var bool
+ */
+ protected $preserveGlobalState = true;
+
+ /**
+ * Whether or not this test is running in a separate PHP process.
+ *
+ * @var bool
+ */
+ private $inIsolation = false;
+
+ /**
+ * @var array
+ */
+ private $data = [];
+
+ /**
+ * @var string
+ */
+ private $dataName = '';
+
+ /**
+ * @var bool
+ */
+ private $useErrorHandler = null;
+
+ /**
+ * The name of the expected Exception.
+ *
+ * @var string
+ */
+ private $expectedException = null;
+
+ /**
+ * The message of the expected Exception.
+ *
+ * @var string
+ */
+ private $expectedExceptionMessage = '';
+
+ /**
+ * The regex pattern to validate the expected Exception message.
+ *
+ * @var string
+ */
+ private $expectedExceptionMessageRegExp = '';
+
+ /**
+ * The code of the expected Exception.
+ *
+ * @var int|string
+ */
+ private $expectedExceptionCode;
+
+ /**
+ * The name of the test case.
+ *
+ * @var string
+ */
+ private $name = null;
+
+ /**
+ * @var array
+ */
+ private $dependencies = [];
+
+ /**
+ * @var array
+ */
+ private $dependencyInput = [];
+
+ /**
+ * @var array
+ */
+ private $iniSettings = [];
+
+ /**
+ * @var array
+ */
+ private $locale = [];
+
+ /**
+ * @var array
+ */
+ private $mockObjects = [];
+
+ /**
+ * @var array
+ */
+ private $mockObjectGenerator = null;
+
+ /**
+ * @var int
+ */
+ private $status;
+
+ /**
+ * @var string
+ */
+ private $statusMessage = '';
+
+ /**
+ * @var int
+ */
+ private $numAssertions = 0;
+
+ /**
+ * @var PHPUnit_Framework_TestResult
+ */
+ private $result;
+
+ /**
+ * @var mixed
+ */
+ private $testResult;
+
+ /**
+ * @var string
+ */
+ private $output = '';
+
+ /**
+ * @var string
+ */
+ private $outputExpectedRegex = null;
+
+ /**
+ * @var string
+ */
+ private $outputExpectedString = null;
+
+ /**
+ * @var mixed
+ */
+ private $outputCallback = false;
+
+ /**
+ * @var bool
+ */
+ private $outputBufferingActive = false;
+
+ /**
+ * @var int
+ */
+ private $outputBufferingLevel;
+
+ /**
+ * @var SebastianBergmann\GlobalState\Snapshot
+ */
+ private $snapshot;
+
+ /**
+ * @var Prophecy\Prophet
+ */
+ private $prophet;
+
+ /**
+ * @var bool
+ */
+ private $beStrictAboutChangesToGlobalState = false;
+
+ /**
+ * @var bool
+ */
+ private $registerMockObjectsFromTestArgumentsRecursively = false;
+
+ /**
+ * @var string[]
+ */
+ private $warnings = [];
+
+ /**
+ * @var array
+ */
+ private $groups = [];
+
+ /**
+ * @var bool
+ */
+ private $doesNotPerformAssertions = false;
+
+ /**
+ * Constructs a test case with the given name.
+ *
+ * @param string $name
+ * @param array $data
+ * @param string $dataName
+ */
+ public function __construct($name = null, array $data = [], $dataName = '')
+ {
+ if ($name !== null) {
+ $this->setName($name);
+ }
+
+ $this->data = $data;
+ $this->dataName = $dataName;
+ }
+
+ /**
+ * Returns a string representation of the test case.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $class = new ReflectionClass($this);
+
+ $buffer = sprintf(
+ '%s::%s',
+ $class->name,
+ $this->getName(false)
+ );
+
+ return $buffer . $this->getDataSetAsString();
+ }
+
+ /**
+ * Counts the number of test cases executed by run(TestResult result).
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return 1;
+ }
+
+ public function getGroups()
+ {
+ return $this->groups;
+ }
+
+ /**
+ * @param array $groups
+ */
+ public function setGroups(array $groups)
+ {
+ $this->groups = $groups;
+ }
+
+ /**
+ * Returns the annotations for this test.
+ *
+ * @return array
+ */
+ public function getAnnotations()
+ {
+ return PHPUnit_Util_Test::parseTestMethodAnnotations(
+ get_class($this),
+ $this->name
+ );
+ }
+
+ /**
+ * Gets the name of a TestCase.
+ *
+ * @param bool $withDataSet
+ *
+ * @return string
+ */
+ public function getName($withDataSet = true)
+ {
+ if ($withDataSet) {
+ return $this->name . $this->getDataSetAsString(false);
+ } else {
+ return $this->name;
+ }
+ }
+
+ /**
+ * Returns the size of the test.
+ *
+ * @return int
+ */
+ public function getSize()
+ {
+ return PHPUnit_Util_Test::getSize(
+ get_class($this),
+ $this->getName(false)
+ );
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasSize()
+ {
+ return $this->getSize() !== PHPUnit_Util_Test::UNKNOWN;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isSmall()
+ {
+ return $this->getSize() === PHPUnit_Util_Test::SMALL;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isMedium()
+ {
+ return $this->getSize() === PHPUnit_Util_Test::MEDIUM;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isLarge()
+ {
+ return $this->getSize() === PHPUnit_Util_Test::LARGE;
+ }
+
+ /**
+ * @return string
+ */
+ public function getActualOutput()
+ {
+ if (!$this->outputBufferingActive) {
+ return $this->output;
+ } else {
+ return ob_get_contents();
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasOutput()
+ {
+ if (strlen($this->output) === 0) {
+ return false;
+ }
+
+ if ($this->hasExpectationOnOutput()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function doesNotPerformAssertions()
+ {
+ return $this->doesNotPerformAssertions;
+ }
+
+ /**
+ * @param string $expectedRegex
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function expectOutputRegex($expectedRegex)
+ {
+ if ($this->outputExpectedString !== null) {
+ throw new PHPUnit_Framework_Exception;
+ }
+
+ if (is_string($expectedRegex) || is_null($expectedRegex)) {
+ $this->outputExpectedRegex = $expectedRegex;
+ }
+ }
+
+ /**
+ * @param string $expectedString
+ */
+ public function expectOutputString($expectedString)
+ {
+ if ($this->outputExpectedRegex !== null) {
+ throw new PHPUnit_Framework_Exception;
+ }
+
+ if (is_string($expectedString) || is_null($expectedString)) {
+ $this->outputExpectedString = $expectedString;
+ }
+ }
+
+ /**
+ * @return bool
+ *
+ * @deprecated Use hasExpectationOnOutput() instead
+ */
+ public function hasPerformedExpectationsOnOutput()
+ {
+ return $this->hasExpectationOnOutput();
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasExpectationOnOutput()
+ {
+ return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex);
+ }
+
+ /**
+ * @return string
+ */
+ public function getExpectedException()
+ {
+ return $this->expectedException;
+ }
+
+ /**
+ * @param mixed $exception
+ * @param string $message
+ * @param int|string $code
+ *
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @deprecated Method deprecated since Release 5.2.0; use expectException() instead
+ */
+ public function setExpectedException($exception, $message = '', $code = null)
+ {
+ $this->expectedException = $exception;
+
+ if ($message !== null && $message !== '') {
+ $this->expectExceptionMessage($message);
+ }
+
+ if ($code !== null) {
+ $this->expectExceptionCode($code);
+ }
+ }
+
+ /**
+ * @param mixed $exception
+ * @param string $messageRegExp
+ * @param int $code
+ *
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @deprecated Method deprecated since Release 5.6.0; use expectExceptionMessageRegExp() instead
+ */
+ public function setExpectedExceptionRegExp($exception, $messageRegExp = '', $code = null)
+ {
+ if (!is_string($messageRegExp)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string');
+ }
+
+ $this->expectedException = $exception;
+ $this->expectedExceptionMessageRegExp = $messageRegExp;
+
+ if ($code !== null) {
+ $this->expectExceptionCode($code);
+ }
+ }
+
+ /**
+ * @param string $exception
+ */
+ public function expectException($exception)
+ {
+ if (!is_string($exception)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $this->expectedException = $exception;
+ }
+
+ /**
+ * @param int|string $code
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function expectExceptionCode($code)
+ {
+ if (!$this->expectedException) {
+ $this->expectedException = \Exception::class;
+ }
+
+ if (!is_int($code) && !is_string($code)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer or string');
+ }
+
+ $this->expectedExceptionCode = $code;
+ }
+
+ /**
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function expectExceptionMessage($message)
+ {
+ if (!$this->expectedException) {
+ $this->expectedException = \Exception::class;
+ }
+
+ if (!is_string($message)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $this->expectedExceptionMessage = $message;
+ }
+
+ /**
+ * @param string $messageRegExp
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function expectExceptionMessageRegExp($messageRegExp)
+ {
+ if (!is_string($messageRegExp)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $this->expectedExceptionMessageRegExp = $messageRegExp;
+ }
+
+ /**
+ * @param bool $flag
+ */
+ public function setRegisterMockObjectsFromTestArgumentsRecursively($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
+ }
+
+ protected function setExpectedExceptionFromAnnotation()
+ {
+ try {
+ $expectedException = PHPUnit_Util_Test::getExpectedException(
+ get_class($this),
+ $this->name
+ );
+
+ if ($expectedException !== false) {
+ $this->expectException($expectedException['class']);
+
+ if ($expectedException['code'] !== null) {
+ $this->expectExceptionCode($expectedException['code']);
+ }
+
+ if ($expectedException['message'] !== '') {
+ $this->expectExceptionMessage($expectedException['message']);
+ } elseif ($expectedException['message_regex'] !== '') {
+ $this->expectExceptionMessageRegExp($expectedException['message_regex']);
+ }
+ }
+ } catch (ReflectionException $e) {
+ }
+ }
+
+ /**
+ * @param bool $useErrorHandler
+ */
+ public function setUseErrorHandler($useErrorHandler)
+ {
+ $this->useErrorHandler = $useErrorHandler;
+ }
+
+ protected function setUseErrorHandlerFromAnnotation()
+ {
+ try {
+ $useErrorHandler = PHPUnit_Util_Test::getErrorHandlerSettings(
+ get_class($this),
+ $this->name
+ );
+
+ if ($useErrorHandler !== null) {
+ $this->setUseErrorHandler($useErrorHandler);
+ }
+ } catch (ReflectionException $e) {
+ }
+ }
+
+ protected function checkRequirements()
+ {
+ if (!$this->name || !method_exists($this, $this->name)) {
+ return;
+ }
+
+ $missingRequirements = PHPUnit_Util_Test::getMissingRequirements(
+ get_class($this),
+ $this->name
+ );
+
+ if (!empty($missingRequirements)) {
+ $this->markTestSkipped(implode(PHP_EOL, $missingRequirements));
+ }
+ }
+
+ /**
+ * Returns the status of this test.
+ *
+ * @return int
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ public function markAsRisky()
+ {
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY;
+ }
+
+ /**
+ * Returns the status message of this test.
+ *
+ * @return string
+ */
+ public function getStatusMessage()
+ {
+ return $this->statusMessage;
+ }
+
+ /**
+ * Returns whether or not this test has failed.
+ *
+ * @return bool
+ */
+ public function hasFailed()
+ {
+ $status = $this->getStatus();
+
+ return $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE ||
+ $status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR;
+ }
+
+ /**
+ * Runs the test case and collects the results in a TestResult object.
+ * If no TestResult object is passed a new one will be created.
+ *
+ * @param PHPUnit_Framework_TestResult $result
+ *
+ * @return PHPUnit_Framework_TestResult
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function run(PHPUnit_Framework_TestResult $result = null)
+ {
+ if ($result === null) {
+ $result = $this->createResult();
+ }
+
+ if (!$this instanceof PHPUnit_Framework_WarningTestCase) {
+ $this->setTestResultObject($result);
+ $this->setUseErrorHandlerFromAnnotation();
+ }
+
+ if ($this->useErrorHandler !== null) {
+ $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions();
+ $result->convertErrorsToExceptions($this->useErrorHandler);
+ }
+
+ if (!$this instanceof PHPUnit_Framework_WarningTestCase &&
+ !$this instanceof PHPUnit_Framework_SkippedTestCase &&
+ !$this->handleDependencies()) {
+ return;
+ }
+
+ if ($this->runTestInSeparateProcess === true &&
+ $this->inIsolation !== true &&
+ !$this instanceof PHPUnit_Extensions_PhptTestCase) {
+ $class = new ReflectionClass($this);
+
+ $template = new Text_Template(
+ __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl'
+ );
+
+ if ($this->preserveGlobalState) {
+ $constants = PHPUnit_Util_GlobalState::getConstantsAsString();
+ $globals = PHPUnit_Util_GlobalState::getGlobalsAsString();
+ $includedFiles = PHPUnit_Util_GlobalState::getIncludedFilesAsString();
+ $iniSettings = PHPUnit_Util_GlobalState::getIniSettingsAsString();
+ } else {
+ $constants = '';
+ if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
+ $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n";
+ } else {
+ $globals = '';
+ }
+ $includedFiles = '';
+ $iniSettings = '';
+ }
+
+ $coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false';
+ $isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false';
+ $isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false';
+ $enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false';
+ $isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false';
+ $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false';
+
+ if (defined('PHPUNIT_COMPOSER_INSTALL')) {
+ $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true);
+ } else {
+ $composerAutoload = '\'\'';
+ }
+
+ if (defined('__PHPUNIT_PHAR__')) {
+ $phar = var_export(__PHPUNIT_PHAR__, true);
+ } else {
+ $phar = '\'\'';
+ }
+
+ if ($result->getCodeCoverage()) {
+ $codeCoverageFilter = $result->getCodeCoverage()->filter();
+ } else {
+ $codeCoverageFilter = null;
+ }
+
+ $data = var_export(serialize($this->data), true);
+ $dataName = var_export($this->dataName, true);
+ $dependencyInput = var_export(serialize($this->dependencyInput), true);
+ $includePath = var_export(get_include_path(), true);
+ $codeCoverageFilter = var_export(serialize($codeCoverageFilter), true);
+ // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC
+ // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences
+ $data = "'." . $data . ".'";
+ $dataName = "'.(" . $dataName . ").'";
+ $dependencyInput = "'." . $dependencyInput . ".'";
+ $includePath = "'." . $includePath . ".'";
+ $codeCoverageFilter = "'." . $codeCoverageFilter . ".'";
+
+ $configurationFilePath = (isset($GLOBALS['__PHPUNIT_CONFIGURATION_FILE']) ? $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] : '');
+
+ $template->setVar(
+ [
+ 'composerAutoload' => $composerAutoload,
+ 'phar' => $phar,
+ 'filename' => $class->getFileName(),
+ 'className' => $class->getName(),
+ 'methodName' => $this->name,
+ 'collectCodeCoverageInformation' => $coverage,
+ 'data' => $data,
+ 'dataName' => $dataName,
+ 'dependencyInput' => $dependencyInput,
+ 'constants' => $constants,
+ 'globals' => $globals,
+ 'include_path' => $includePath,
+ 'included_files' => $includedFiles,
+ 'iniSettings' => $iniSettings,
+ 'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything,
+ 'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests,
+ 'enforcesTimeLimit' => $enforcesTimeLimit,
+ 'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests,
+ 'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests,
+ 'codeCoverageFilter' => $codeCoverageFilter,
+ 'configurationFilePath' => $configurationFilePath
+ ]
+ );
+
+ $this->prepareTemplate($template);
+
+ $php = PHPUnit_Util_PHP::factory();
+ $php->runTestJob($template->render(), $this, $result);
+ } else {
+ $result->run($this);
+ }
+
+ if ($this->useErrorHandler !== null) {
+ $result->convertErrorsToExceptions($oldErrorHandlerSetting);
+ }
+
+ $this->result = null;
+
+ return $result;
+ }
+
+ /**
+ * Runs the bare test sequence.
+ */
+ public function runBare()
+ {
+ $this->numAssertions = 0;
+
+ $this->snapshotGlobalState();
+ $this->startOutputBuffering();
+ clearstatcache();
+ $currentWorkingDirectory = getcwd();
+
+ $hookMethods = PHPUnit_Util_Test::getHookMethods(get_class($this));
+
+ try {
+ $hasMetRequirements = false;
+ $this->checkRequirements();
+ $hasMetRequirements = true;
+
+ if ($this->inIsolation) {
+ foreach ($hookMethods['beforeClass'] as $method) {
+ $this->$method();
+ }
+ }
+
+ $this->setExpectedExceptionFromAnnotation();
+ $this->setDoesNotPerformAssertionsFromAnnotation();
+
+ foreach ($hookMethods['before'] as $method) {
+ $this->$method();
+ }
+
+ $this->assertPreConditions();
+ $this->testResult = $this->runTest();
+ $this->verifyMockObjects();
+ $this->assertPostConditions();
+
+ if (!empty($this->warnings)) {
+ throw new PHPUnit_Framework_Warning(
+ implode(
+ "\n",
+ array_unique($this->warnings)
+ )
+ );
+ }
+
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED;
+ } catch (PHPUnit_Framework_IncompleteTest $e) {
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE;
+ $this->statusMessage = $e->getMessage();
+ } catch (PHPUnit_Framework_SkippedTest $e) {
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED;
+ $this->statusMessage = $e->getMessage();
+ } catch (PHPUnit_Framework_Warning $e) {
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_WARNING;
+ $this->statusMessage = $e->getMessage();
+ } catch (PHPUnit_Framework_AssertionFailedError $e) {
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE;
+ $this->statusMessage = $e->getMessage();
+ } catch (PredictionException $e) {
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE;
+ $this->statusMessage = $e->getMessage();
+ } catch (Throwable $_e) {
+ $e = $_e;
+ } catch (Exception $_e) {
+ $e = $_e;
+ }
+
+ if (isset($_e)) {
+ $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR;
+ $this->statusMessage = $_e->getMessage();
+ }
+
+ // Clean up the mock objects.
+ $this->mockObjects = [];
+ $this->prophet = null;
+
+ // Tear down the fixture. An exception raised in tearDown() will be
+ // caught and passed on when no exception was raised before.
+ try {
+ if ($hasMetRequirements) {
+ foreach ($hookMethods['after'] as $method) {
+ $this->$method();
+ }
+
+ if ($this->inIsolation) {
+ foreach ($hookMethods['afterClass'] as $method) {
+ $this->$method();
+ }
+ }
+ }
+ } catch (Throwable $_e) {
+ if (!isset($e)) {
+ $e = $_e;
+ }
+ } catch (Exception $_e) {
+ if (!isset($e)) {
+ $e = $_e;
+ }
+ }
+
+ try {
+ $this->stopOutputBuffering();
+ } catch (PHPUnit_Framework_RiskyTestError $_e) {
+ if (!isset($e)) {
+ $e = $_e;
+ }
+ }
+
+ clearstatcache();
+
+ if ($currentWorkingDirectory != getcwd()) {
+ chdir($currentWorkingDirectory);
+ }
+
+ $this->restoreGlobalState();
+
+ // Clean up INI settings.
+ foreach ($this->iniSettings as $varName => $oldValue) {
+ ini_set($varName, $oldValue);
+ }
+
+ $this->iniSettings = [];
+
+ // Clean up locale settings.
+ foreach ($this->locale as $category => $locale) {
+ setlocale($category, $locale);
+ }
+
+ // Perform assertion on output.
+ if (!isset($e)) {
+ try {
+ if ($this->outputExpectedRegex !== null) {
+ $this->assertRegExp($this->outputExpectedRegex, $this->output);
+ } elseif ($this->outputExpectedString !== null) {
+ $this->assertEquals($this->outputExpectedString, $this->output);
+ }
+ } catch (Throwable $_e) {
+ $e = $_e;
+ } catch (Exception $_e) {
+ $e = $_e;
+ }
+ }
+
+ // Workaround for missing "finally".
+ if (isset($e)) {
+ if ($e instanceof PredictionException) {
+ $e = new PHPUnit_Framework_AssertionFailedError($e->getMessage());
+ }
+
+ $this->onNotSuccessfulTest($e);
+ }
+ }
+
+ /**
+ * Override to run the test and assert its state.
+ *
+ * @return mixed
+ *
+ * @throws Exception|PHPUnit_Framework_Exception
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function runTest()
+ {
+ if ($this->name === null) {
+ throw new PHPUnit_Framework_Exception(
+ 'PHPUnit_Framework_TestCase::$name must not be null.'
+ );
+ }
+
+ try {
+ $class = new ReflectionClass($this);
+ $method = $class->getMethod($this->name);
+ } catch (ReflectionException $e) {
+ $this->fail($e->getMessage());
+ }
+
+ $testArguments = array_merge($this->data, $this->dependencyInput);
+
+ $this->registerMockObjectsFromTestArguments($testArguments);
+
+ try {
+ $testResult = $method->invokeArgs($this, $testArguments);
+ } catch (Throwable $_e) {
+ $e = $_e;
+ } catch (Exception $_e) {
+ $e = $_e;
+ }
+
+ if (isset($e)) {
+ $checkException = false;
+
+ if (!($e instanceof PHPUnit_Framework_SkippedTestError) && is_string($this->expectedException)) {
+ $checkException = true;
+
+ if ($e instanceof PHPUnit_Framework_Exception) {
+ $checkException = false;
+ }
+
+ $reflector = new ReflectionClass($this->expectedException);
+
+ if ($this->expectedException === 'PHPUnit_Framework_Exception' ||
+ $this->expectedException === '\PHPUnit_Framework_Exception' ||
+ $reflector->isSubclassOf('PHPUnit_Framework_Exception')) {
+ $checkException = true;
+ }
+ }
+
+ if ($checkException) {
+ $this->assertThat(
+ $e,
+ new PHPUnit_Framework_Constraint_Exception(
+ $this->expectedException
+ )
+ );
+
+ if (is_string($this->expectedExceptionMessage) &&
+ !empty($this->expectedExceptionMessage)) {
+ $this->assertThat(
+ $e,
+ new PHPUnit_Framework_Constraint_ExceptionMessage(
+ $this->expectedExceptionMessage
+ )
+ );
+ }
+
+ if (is_string($this->expectedExceptionMessageRegExp) &&
+ !empty($this->expectedExceptionMessageRegExp)) {
+ $this->assertThat(
+ $e,
+ new PHPUnit_Framework_Constraint_ExceptionMessageRegExp(
+ $this->expectedExceptionMessageRegExp
+ )
+ );
+ }
+
+ if ($this->expectedExceptionCode !== null) {
+ $this->assertThat(
+ $e,
+ new PHPUnit_Framework_Constraint_ExceptionCode(
+ $this->expectedExceptionCode
+ )
+ );
+ }
+
+ return;
+ } else {
+ throw $e;
+ }
+ }
+
+ if ($this->expectedException !== null) {
+ $this->assertThat(
+ null,
+ new PHPUnit_Framework_Constraint_Exception(
+ $this->expectedException
+ )
+ );
+ }
+
+ return $testResult;
+ }
+
+ /**
+ * Verifies the mock object expectations.
+ */
+ protected function verifyMockObjects()
+ {
+ foreach ($this->mockObjects as $mockObject) {
+ if ($mockObject->__phpunit_hasMatchers()) {
+ $this->numAssertions++;
+ }
+
+ $mockObject->__phpunit_verify(
+ $this->shouldInvocationMockerBeReset($mockObject)
+ );
+ }
+
+ if ($this->prophet !== null) {
+ try {
+ $this->prophet->checkPredictions();
+ } catch (Throwable $t) {
+ /* Intentionally left empty */
+ } catch (Exception $t) {
+ /* Intentionally left empty */
+ }
+
+ foreach ($this->prophet->getProphecies() as $objectProphecy) {
+ foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) {
+ foreach ($methodProphecies as $methodProphecy) {
+ $this->numAssertions += count($methodProphecy->getCheckedPredictions());
+ }
+ }
+ }
+
+ if (isset($t)) {
+ throw $t;
+ }
+ }
+ }
+
+ /**
+ * Sets the name of a TestCase.
+ *
+ * @param string
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Sets the dependencies of a TestCase.
+ *
+ * @param array $dependencies
+ */
+ public function setDependencies(array $dependencies)
+ {
+ $this->dependencies = $dependencies;
+ }
+
+ /**
+ * Returns true if the tests has dependencies
+ *
+ * @return bool
+ */
+ public function hasDependencies()
+ {
+ return count($this->dependencies) > 0;
+ }
+
+ /**
+ * Sets
+ *
+ * @param array $dependencyInput
+ */
+ public function setDependencyInput(array $dependencyInput)
+ {
+ $this->dependencyInput = $dependencyInput;
+ }
+
+ /**
+ * @param bool $beStrictAboutChangesToGlobalState
+ */
+ public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState)
+ {
+ $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
+ }
+
+ /**
+ * Calling this method in setUp() has no effect!
+ *
+ * @param bool $backupGlobals
+ */
+ public function setBackupGlobals($backupGlobals)
+ {
+ if (is_null($this->backupGlobals) && is_bool($backupGlobals)) {
+ $this->backupGlobals = $backupGlobals;
+ }
+ }
+
+ /**
+ * Calling this method in setUp() has no effect!
+ *
+ * @param bool $backupStaticAttributes
+ */
+ public function setBackupStaticAttributes($backupStaticAttributes)
+ {
+ if (is_null($this->backupStaticAttributes) &&
+ is_bool($backupStaticAttributes)) {
+ $this->backupStaticAttributes = $backupStaticAttributes;
+ }
+ }
+
+ /**
+ * @param bool $runTestInSeparateProcess
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setRunTestInSeparateProcess($runTestInSeparateProcess)
+ {
+ if (is_bool($runTestInSeparateProcess)) {
+ if ($this->runTestInSeparateProcess === null) {
+ $this->runTestInSeparateProcess = $runTestInSeparateProcess;
+ }
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+ }
+
+ /**
+ * @param bool $preserveGlobalState
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setPreserveGlobalState($preserveGlobalState)
+ {
+ if (is_bool($preserveGlobalState)) {
+ $this->preserveGlobalState = $preserveGlobalState;
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+ }
+
+ /**
+ * @param bool $inIsolation
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setInIsolation($inIsolation)
+ {
+ if (is_bool($inIsolation)) {
+ $this->inIsolation = $inIsolation;
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ public function isInIsolation()
+ {
+ return $this->inIsolation;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getResult()
+ {
+ return $this->testResult;
+ }
+
+ /**
+ * @param mixed $result
+ */
+ public function setResult($result)
+ {
+ $this->testResult = $result;
+ }
+
+ /**
+ * @param callable $callback
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setOutputCallback($callback)
+ {
+ if (!is_callable($callback)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'callback');
+ }
+
+ $this->outputCallback = $callback;
+ }
+
+ /**
+ * @return PHPUnit_Framework_TestResult
+ */
+ public function getTestResultObject()
+ {
+ return $this->result;
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ public function setTestResultObject(PHPUnit_Framework_TestResult $result)
+ {
+ $this->result = $result;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_MockObject $mockObject
+ */
+ public function registerMockObject(PHPUnit_Framework_MockObject_MockObject $mockObject)
+ {
+ $this->mockObjects[] = $mockObject;
+ }
+
+ /**
+ * This method is a wrapper for the ini_set() function that automatically
+ * resets the modified php.ini setting to its original value after the
+ * test is run.
+ *
+ * @param string $varName
+ * @param string $newValue
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function iniSet($varName, $newValue)
+ {
+ if (!is_string($varName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ $currentValue = ini_set($varName, $newValue);
+
+ if ($currentValue !== false) {
+ $this->iniSettings[$varName] = $currentValue;
+ } else {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'INI setting "%s" could not be set to "%s".',
+ $varName,
+ $newValue
+ )
+ );
+ }
+ }
+
+ /**
+ * This method is a wrapper for the setlocale() function that automatically
+ * resets the locale to its original value after the test is run.
+ *
+ * @param int $category
+ * @param string $locale
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function setLocale()
+ {
+ $args = func_get_args();
+
+ if (count($args) < 2) {
+ throw new PHPUnit_Framework_Exception;
+ }
+
+ $category = $args[0];
+ $locale = $args[1];
+
+ $categories = [
+ LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME
+ ];
+
+ if (defined('LC_MESSAGES')) {
+ $categories[] = LC_MESSAGES;
+ }
+
+ if (!in_array($category, $categories)) {
+ throw new PHPUnit_Framework_Exception;
+ }
+
+ if (!is_array($locale) && !is_string($locale)) {
+ throw new PHPUnit_Framework_Exception;
+ }
+
+ $this->locale[$category] = setlocale($category, 0);
+
+ $result = call_user_func_array('setlocale', $args);
+
+ if ($result === false) {
+ throw new PHPUnit_Framework_Exception(
+ 'The locale functionality is not implemented on your platform, ' .
+ 'the specified locale does not exist or the category name is ' .
+ 'invalid.'
+ );
+ }
+ }
+
+ /**
+ * Returns a builder object to create mock objects using a fluent interface.
+ *
+ * @param string $className
+ *
+ * @return PHPUnit_Framework_MockObject_MockBuilder
+ */
+ public function getMockBuilder($className)
+ {
+ return new PHPUnit_Framework_MockObject_MockBuilder($this, $className);
+ }
+
+ /**
+ * Returns a test double for the specified class.
+ *
+ * @param string $originalClassName
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function createMock($originalClassName)
+ {
+ return $this->getMockBuilder($originalClassName)
+ ->disableOriginalConstructor()
+ ->disableOriginalClone()
+ ->disableArgumentCloning()
+ ->disallowMockingUnknownTypes()
+ ->getMock();
+ }
+
+ /**
+ * Returns a configured test double for the specified class.
+ *
+ * @param string $originalClassName
+ * @param array $configuration
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function createConfiguredMock($originalClassName, array $configuration)
+ {
+ $o = $this->createMock($originalClassName);
+
+ foreach ($configuration as $method => $return) {
+ $o->method($method)->willReturn($return);
+ }
+
+ return $o;
+ }
+
+ /**
+ * Returns a partial test double for the specified class.
+ *
+ * @param string $originalClassName
+ * @param array $methods
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function createPartialMock($originalClassName, array $methods)
+ {
+ return $this->getMockBuilder($originalClassName)
+ ->disableOriginalConstructor()
+ ->disableOriginalClone()
+ ->disableArgumentCloning()
+ ->disallowMockingUnknownTypes()
+ ->setMethods(empty($methods) ? null : $methods)
+ ->getMock();
+ }
+
+ /**
+ * Returns a mock object for the specified class.
+ *
+ * @param string $originalClassName Name of the class to mock.
+ * @param array|null $methods When provided, only methods whose names are in the array
+ * are replaced with a configurable test double. The behavior
+ * of the other methods is not changed.
+ * Providing null means that no methods will be replaced.
+ * @param array $arguments Parameters to pass to the original class' constructor.
+ * @param string $mockClassName Class name for the generated test double class.
+ * @param bool $callOriginalConstructor Can be used to disable the call to the original class' constructor.
+ * @param bool $callOriginalClone Can be used to disable the call to the original class' clone constructor.
+ * @param bool $callAutoload Can be used to disable __autoload() during the generation of the test double class.
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ * @param object $proxyTarget
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @deprecated Method deprecated since Release 5.4.0; use createMock() or getMockBuilder() instead
+ */
+ protected function getMock($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null)
+ {
+ $this->warnings[] = 'PHPUnit_Framework_TestCase::getMock() is deprecated, use PHPUnit_Framework_TestCase::createMock() or PHPUnit_Framework_TestCase::getMockBuilder() instead';
+
+ $mockObject = $this->getMockObjectGenerator()->getMock(
+ $originalClassName,
+ $methods,
+ $arguments,
+ $mockClassName,
+ $callOriginalConstructor,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments,
+ $callOriginalMethods,
+ $proxyTarget
+ );
+
+ $this->registerMockObject($mockObject);
+
+ return $mockObject;
+ }
+
+ /**
+ * Returns a mock with disabled constructor object for the specified class.
+ *
+ * @param string $originalClassName
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @deprecated Method deprecated since Release 5.4.0; use createMock() instead
+ */
+ protected function getMockWithoutInvokingTheOriginalConstructor($originalClassName)
+ {
+ $this->warnings[] = 'PHPUnit_Framework_TestCase::getMockWithoutInvokingTheOriginalConstructor() is deprecated, use PHPUnit_Framework_TestCase::createMock() instead';
+
+ return $this->getMockBuilder($originalClassName)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ /**
+ * Mocks the specified class and returns the name of the mocked class.
+ *
+ * @param string $originalClassName
+ * @param array $methods
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ *
+ * @return string
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function getMockClass($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false)
+ {
+ $mock = $this->getMockObjectGenerator()->getMock(
+ $originalClassName,
+ $methods,
+ $arguments,
+ $mockClassName,
+ $callOriginalConstructor,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments
+ );
+
+ return get_class($mock);
+ }
+
+ /**
+ * Returns a mock object for the specified abstract class with all abstract
+ * methods of the class mocked. Concrete methods are not mocked by default.
+ * To mock concrete methods, use the 7th parameter ($mockedMethods).
+ *
+ * @param string $originalClassName
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param array $mockedMethods
+ * @param bool $cloneArguments
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false)
+ {
+ $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass(
+ $originalClassName,
+ $arguments,
+ $mockClassName,
+ $callOriginalConstructor,
+ $callOriginalClone,
+ $callAutoload,
+ $mockedMethods,
+ $cloneArguments
+ );
+
+ $this->registerMockObject($mockObject);
+
+ return $mockObject;
+ }
+
+ /**
+ * Returns a mock object based on the given WSDL file.
+ *
+ * @param string $wsdlFile
+ * @param string $originalClassName
+ * @param string $mockClassName
+ * @param array $methods
+ * @param bool $callOriginalConstructor
+ * @param array $options An array of options passed to SOAPClient::_construct
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = [])
+ {
+ if ($originalClassName === '') {
+ $originalClassName = pathinfo(basename(parse_url($wsdlFile)['path']), PATHINFO_FILENAME);
+ }
+
+ if (!class_exists($originalClassName)) {
+ eval(
+ $this->getMockObjectGenerator()->generateClassFromWsdl(
+ $wsdlFile,
+ $originalClassName,
+ $methods,
+ $options
+ )
+ );
+ }
+
+ $mockObject = $this->getMockObjectGenerator()->getMock(
+ $originalClassName,
+ $methods,
+ ['', $options],
+ $mockClassName,
+ $callOriginalConstructor,
+ false,
+ false
+ );
+
+ $this->registerMockObject($mockObject);
+
+ return $mockObject;
+ }
+
+ /**
+ * Returns a mock object for the specified trait with all abstract methods
+ * of the trait mocked. Concrete methods to mock can be specified with the
+ * `$mockedMethods` parameter.
+ *
+ * @param string $traitName
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param array $mockedMethods
+ * @param bool $cloneArguments
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false)
+ {
+ $mockObject = $this->getMockObjectGenerator()->getMockForTrait(
+ $traitName,
+ $arguments,
+ $mockClassName,
+ $callOriginalConstructor,
+ $callOriginalClone,
+ $callAutoload,
+ $mockedMethods,
+ $cloneArguments
+ );
+
+ $this->registerMockObject($mockObject);
+
+ return $mockObject;
+ }
+
+ /**
+ * Returns an object for the specified trait.
+ *
+ * @param string $traitName
+ * @param array $arguments
+ * @param string $traitClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ *
+ * @return object
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false)
+ {
+ return $this->getMockObjectGenerator()->getObjectForTrait(
+ $traitName,
+ $arguments,
+ $traitClassName,
+ $callOriginalConstructor,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments
+ );
+ }
+
+ /**
+ * @param string|null $classOrInterface
+ *
+ * @return \Prophecy\Prophecy\ObjectProphecy
+ *
+ * @throws \LogicException
+ */
+ protected function prophesize($classOrInterface = null)
+ {
+ return $this->getProphet()->prophesize($classOrInterface);
+ }
+
+ /**
+ * Adds a value to the assertion counter.
+ *
+ * @param int $count
+ */
+ public function addToAssertionCount($count)
+ {
+ $this->numAssertions += $count;
+ }
+
+ /**
+ * Returns the number of assertions performed by this test.
+ *
+ * @return int
+ */
+ public function getNumAssertions()
+ {
+ return $this->numAssertions;
+ }
+
+ /**
+ * Returns a matcher that matches when the method is executed
+ * zero or more times.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
+ */
+ public static function any()
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
+ }
+
+ /**
+ * Returns a matcher that matches when the method is never executed.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
+ */
+ public static function never()
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(0);
+ }
+
+ /**
+ * Returns a matcher that matches when the method is executed
+ * at least N times.
+ *
+ * @param int $requiredInvocations
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount
+ */
+ public static function atLeast($requiredInvocations)
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount(
+ $requiredInvocations
+ );
+ }
+
+ /**
+ * Returns a matcher that matches when the method is executed at least once.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce
+ */
+ public static function atLeastOnce()
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce;
+ }
+
+ /**
+ * Returns a matcher that matches when the method is executed exactly once.
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
+ */
+ public static function once()
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(1);
+ }
+
+ /**
+ * Returns a matcher that matches when the method is executed
+ * exactly $count times.
+ *
+ * @param int $count
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount
+ */
+ public static function exactly($count)
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_InvokedCount($count);
+ }
+
+ /**
+ * Returns a matcher that matches when the method is executed
+ * at most N times.
+ *
+ * @param int $allowedInvocations
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount
+ */
+ public static function atMost($allowedInvocations)
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount(
+ $allowedInvocations
+ );
+ }
+
+ /**
+ * Returns a matcher that matches when the method is executed
+ * at the given index.
+ *
+ * @param int $index
+ *
+ * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex
+ */
+ public static function at($index)
+ {
+ return new PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex($index);
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_Return
+ */
+ public static function returnValue($value)
+ {
+ return new PHPUnit_Framework_MockObject_Stub_Return($value);
+ }
+
+ /**
+ * @param array $valueMap
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap
+ */
+ public static function returnValueMap(array $valueMap)
+ {
+ return new PHPUnit_Framework_MockObject_Stub_ReturnValueMap($valueMap);
+ }
+
+ /**
+ * @param int $argumentIndex
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument
+ */
+ public static function returnArgument($argumentIndex)
+ {
+ return new PHPUnit_Framework_MockObject_Stub_ReturnArgument(
+ $argumentIndex
+ );
+ }
+
+ /**
+ * @param mixed $callback
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback
+ */
+ public static function returnCallback($callback)
+ {
+ return new PHPUnit_Framework_MockObject_Stub_ReturnCallback($callback);
+ }
+
+ /**
+ * Returns the current object.
+ *
+ * This method is useful when mocking a fluent interface.
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf
+ */
+ public static function returnSelf()
+ {
+ return new PHPUnit_Framework_MockObject_Stub_ReturnSelf();
+ }
+
+ /**
+ * @param Throwable|Exception $exception
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_Exception
+ *
+ * @todo Add type declaration when support for PHP 5 is dropped
+ */
+ public static function throwException($exception)
+ {
+ return new PHPUnit_Framework_MockObject_Stub_Exception($exception);
+ }
+
+ /**
+ * @param mixed $value, ...
+ *
+ * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls
+ */
+ public static function onConsecutiveCalls()
+ {
+ $args = func_get_args();
+
+ return new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($args);
+ }
+
+ /**
+ * @return bool
+ */
+ public function usesDataProvider()
+ {
+ return !empty($this->data);
+ }
+
+ /**
+ * @return string
+ */
+ public function dataDescription()
+ {
+ return is_string($this->dataName) ? $this->dataName : '';
+ }
+
+ /**
+ * Gets the data set description of a TestCase.
+ *
+ * @param bool $includeData
+ *
+ * @return string
+ */
+ protected function getDataSetAsString($includeData = true)
+ {
+ $buffer = '';
+
+ if (!empty($this->data)) {
+ if (is_int($this->dataName)) {
+ $buffer .= sprintf(' with data set #%d', $this->dataName);
+ } else {
+ $buffer .= sprintf(' with data set "%s"', $this->dataName);
+ }
+
+ $exporter = new Exporter;
+
+ if ($includeData) {
+ $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data));
+ }
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Gets the data set of a TestCase.
+ *
+ * @return array
+ */
+ protected function getProvidedData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Creates a default TestResult object.
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ protected function createResult()
+ {
+ return new PHPUnit_Framework_TestResult;
+ }
+
+ protected function handleDependencies()
+ {
+ if (!empty($this->dependencies) && !$this->inIsolation) {
+ $className = get_class($this);
+ $passed = $this->result->passed();
+ $passedKeys = array_keys($passed);
+ $numKeys = count($passedKeys);
+
+ for ($i = 0; $i < $numKeys; $i++) {
+ $pos = strpos($passedKeys[$i], ' with data set');
+
+ if ($pos !== false) {
+ $passedKeys[$i] = substr($passedKeys[$i], 0, $pos);
+ }
+ }
+
+ $passedKeys = array_flip(array_unique($passedKeys));
+
+ foreach ($this->dependencies as $dependency) {
+ $clone = false;
+
+ if (strpos($dependency, 'clone ') === 0) {
+ $clone = true;
+ $dependency = substr($dependency, strlen('clone '));
+ } elseif (strpos($dependency, '!clone ') === 0) {
+ $clone = false;
+ $dependency = substr($dependency, strlen('!clone '));
+ }
+
+ if (strpos($dependency, '::') === false) {
+ $dependency = $className . '::' . $dependency;
+ }
+
+ if (!isset($passedKeys[$dependency])) {
+ $this->result->startTest($this);
+ $this->result->addError(
+ $this,
+ new PHPUnit_Framework_SkippedTestError(
+ sprintf(
+ 'This test depends on "%s" to pass.',
+ $dependency
+ )
+ ),
+ 0
+ );
+ $this->result->endTest($this, 0);
+
+ return false;
+ }
+
+ if (isset($passed[$dependency])) {
+ if ($passed[$dependency]['size'] != PHPUnit_Util_Test::UNKNOWN &&
+ $this->getSize() != PHPUnit_Util_Test::UNKNOWN &&
+ $passed[$dependency]['size'] > $this->getSize()) {
+ $this->result->addError(
+ $this,
+ new PHPUnit_Framework_SkippedTestError(
+ 'This test depends on a test that is larger than itself.'
+ ),
+ 0
+ );
+
+ return false;
+ }
+
+ if ($clone) {
+ $deepCopy = new DeepCopy;
+ $deepCopy->skipUncloneable(false);
+
+ $this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']);
+ } else {
+ $this->dependencyInput[$dependency] = $passed[$dependency]['result'];
+ }
+ } else {
+ $this->dependencyInput[$dependency] = null;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * This method is called before the first test of this test class is run.
+ */
+ public static function setUpBeforeClass()
+ {
+ }
+
+ /**
+ * Sets up the fixture, for example, open a network connection.
+ * This method is called before a test is executed.
+ */
+ protected function setUp()
+ {
+ }
+
+ /**
+ * Performs assertions shared by all tests of a test case.
+ *
+ * This method is called before the execution of a test starts
+ * and after setUp() is called.
+ */
+ protected function assertPreConditions()
+ {
+ }
+
+ /**
+ * Performs assertions shared by all tests of a test case.
+ *
+ * This method is called before the execution of a test ends
+ * and before tearDown() is called.
+ */
+ protected function assertPostConditions()
+ {
+ }
+
+ /**
+ * Tears down the fixture, for example, close a network connection.
+ * This method is called after a test is executed.
+ */
+ protected function tearDown()
+ {
+ }
+
+ /**
+ * This method is called after the last test of this test class is run.
+ */
+ public static function tearDownAfterClass()
+ {
+ }
+
+ /**
+ * This method is called when a test method did not execute successfully.
+ *
+ * @param Exception|Throwable $e
+ *
+ * @throws Exception|Throwable
+ */
+ protected function onNotSuccessfulTest($e)
+ {
+ $expected = PHP_MAJOR_VERSION >= 7 ? 'Throwable' : 'Exception';
+
+ if ($e instanceof $expected) {
+ throw $e;
+ }
+
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'Throwable or Exception'
+ );
+ }
+
+ /**
+ * Performs custom preparations on the process isolation template.
+ *
+ * @param Text_Template $template
+ */
+ protected function prepareTemplate(Text_Template $template)
+ {
+ }
+
+ /**
+ * Get the mock object generator, creating it if it doesn't exist.
+ *
+ * @return PHPUnit_Framework_MockObject_Generator
+ */
+ protected function getMockObjectGenerator()
+ {
+ if (null === $this->mockObjectGenerator) {
+ $this->mockObjectGenerator = new PHPUnit_Framework_MockObject_Generator;
+ }
+
+ return $this->mockObjectGenerator;
+ }
+
+ private function startOutputBuffering()
+ {
+ ob_start();
+
+ $this->outputBufferingActive = true;
+ $this->outputBufferingLevel = ob_get_level();
+ }
+
+ private function stopOutputBuffering()
+ {
+ if (ob_get_level() != $this->outputBufferingLevel) {
+ while (ob_get_level() >= $this->outputBufferingLevel) {
+ ob_end_clean();
+ }
+
+ throw new PHPUnit_Framework_RiskyTestError(
+ 'Test code or tested code did not (only) close its own output buffers'
+ );
+ }
+
+ $output = ob_get_contents();
+
+ if ($this->outputCallback === false) {
+ $this->output = $output;
+ } else {
+ $this->output = call_user_func_array(
+ $this->outputCallback,
+ [$output]
+ );
+ }
+
+ ob_end_clean();
+
+ $this->outputBufferingActive = false;
+ $this->outputBufferingLevel = ob_get_level();
+ }
+
+ private function snapshotGlobalState()
+ {
+ $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true;
+
+ if ($this->runTestInSeparateProcess ||
+ $this->inIsolation ||
+ (!$backupGlobals && !$this->backupStaticAttributes)) {
+ return;
+ }
+
+ $this->snapshot = $this->createGlobalStateSnapshot($backupGlobals);
+ }
+
+ private function restoreGlobalState()
+ {
+ if (!$this->snapshot instanceof Snapshot) {
+ return;
+ }
+
+ $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true;
+
+ if ($this->beStrictAboutChangesToGlobalState) {
+ try {
+ $this->compareGlobalStateSnapshots(
+ $this->snapshot,
+ $this->createGlobalStateSnapshot($backupGlobals)
+ );
+ } catch (PHPUnit_Framework_RiskyTestError $rte) {
+ // Intentionally left empty
+ }
+ }
+
+ $restorer = new Restorer;
+
+ if ($backupGlobals) {
+ $restorer->restoreGlobalVariables($this->snapshot);
+ }
+
+ if ($this->backupStaticAttributes) {
+ $restorer->restoreStaticAttributes($this->snapshot);
+ }
+
+ $this->snapshot = null;
+
+ if (isset($rte)) {
+ throw $rte;
+ }
+ }
+
+ /**
+ * @param bool $backupGlobals
+ *
+ * @return Snapshot
+ */
+ private function createGlobalStateSnapshot($backupGlobals)
+ {
+ $blacklist = new Blacklist;
+
+ foreach ($this->backupGlobalsBlacklist as $globalVariable) {
+ $blacklist->addGlobalVariable($globalVariable);
+ }
+
+ if (!defined('PHPUNIT_TESTSUITE')) {
+ $blacklist->addClassNamePrefix('PHPUnit');
+ $blacklist->addClassNamePrefix('File_Iterator');
+ $blacklist->addClassNamePrefix('SebastianBergmann\CodeCoverage');
+ $blacklist->addClassNamePrefix('PHP_Invoker');
+ $blacklist->addClassNamePrefix('PHP_Timer');
+ $blacklist->addClassNamePrefix('PHP_Token');
+ $blacklist->addClassNamePrefix('Symfony');
+ $blacklist->addClassNamePrefix('Text_Template');
+ $blacklist->addClassNamePrefix('Doctrine\Instantiator');
+ $blacklist->addClassNamePrefix('Prophecy');
+
+ foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) {
+ foreach ($attributes as $attribute) {
+ $blacklist->addStaticAttribute($class, $attribute);
+ }
+ }
+ }
+
+ return new Snapshot(
+ $blacklist,
+ $backupGlobals,
+ (bool) $this->backupStaticAttributes,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ );
+ }
+
+ /**
+ * @param Snapshot $before
+ * @param Snapshot $after
+ *
+ * @throws PHPUnit_Framework_RiskyTestError
+ */
+ private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after)
+ {
+ $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true;
+
+ if ($backupGlobals) {
+ $this->compareGlobalStateSnapshotPart(
+ $before->globalVariables(),
+ $after->globalVariables(),
+ "--- Global variables before the test\n+++ Global variables after the test\n"
+ );
+
+ $this->compareGlobalStateSnapshotPart(
+ $before->superGlobalVariables(),
+ $after->superGlobalVariables(),
+ "--- Super-global variables before the test\n+++ Super-global variables after the test\n"
+ );
+ }
+
+ if ($this->backupStaticAttributes) {
+ $this->compareGlobalStateSnapshotPart(
+ $before->staticAttributes(),
+ $after->staticAttributes(),
+ "--- Static attributes before the test\n+++ Static attributes after the test\n"
+ );
+ }
+ }
+
+ /**
+ * @param array $before
+ * @param array $after
+ * @param string $header
+ *
+ * @throws PHPUnit_Framework_RiskyTestError
+ */
+ private function compareGlobalStateSnapshotPart(array $before, array $after, $header)
+ {
+ if ($before != $after) {
+ $differ = new Differ($header);
+ $exporter = new Exporter;
+
+ $diff = $differ->diff(
+ $exporter->export($before),
+ $exporter->export($after)
+ );
+
+ throw new PHPUnit_Framework_RiskyTestError(
+ $diff
+ );
+ }
+ }
+
+ /**
+ * @return Prophecy\Prophet
+ */
+ private function getProphet()
+ {
+ if ($this->prophet === null) {
+ $this->prophet = new Prophet;
+ }
+
+ return $this->prophet;
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_MockObject $mock
+ *
+ * @return bool
+ */
+ private function shouldInvocationMockerBeReset(PHPUnit_Framework_MockObject_MockObject $mock)
+ {
+ $enumerator = new Enumerator;
+
+ foreach ($enumerator->enumerate($this->dependencyInput) as $object) {
+ if ($mock === $object) {
+ return false;
+ }
+ }
+
+ if (!is_array($this->testResult) && !is_object($this->testResult)) {
+ return true;
+ }
+
+ foreach ($enumerator->enumerate($this->testResult) as $object) {
+ if ($mock === $object) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $testArguments
+ * @param array $originalTestArguments
+ */
+ private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = [])
+ {
+ if ($this->registerMockObjectsFromTestArgumentsRecursively) {
+ $enumerator = new Enumerator;
+
+ foreach ($enumerator->enumerate($testArguments) as $object) {
+ if ($object instanceof PHPUnit_Framework_MockObject_MockObject) {
+ $this->registerMockObject($object);
+ }
+ }
+ } else {
+ foreach ($testArguments as $testArgument) {
+ if ($testArgument instanceof PHPUnit_Framework_MockObject_MockObject) {
+ if ($this->isCloneable($testArgument)) {
+ $testArgument = clone $testArgument;
+ }
+
+ $this->registerMockObject($testArgument);
+ } elseif (is_array($testArgument) && !in_array($testArgument, $visited, true)) {
+ $visited[] = $testArgument;
+
+ $this->registerMockObjectsFromTestArguments(
+ $testArgument,
+ $visited
+ );
+ }
+ }
+ }
+ }
+
+ private function setDoesNotPerformAssertionsFromAnnotation()
+ {
+ $annotations = $this->getAnnotations();
+
+ if (isset($annotations['method']['doesNotPerformAssertions'])) {
+ $this->doesNotPerformAssertions = true;
+ }
+ }
+
+ /**
+ * @param PHPUnit_Framework_MockObject_MockObject $testArgument
+ *
+ * @return bool
+ */
+ private function isCloneable(PHPUnit_Framework_MockObject_MockObject $testArgument)
+ {
+ $reflector = new ReflectionObject($testArgument);
+
+ if (!$reflector->isCloneable()) {
+ return false;
+ }
+
+ if ($reflector->hasMethod('__clone') &&
+ $reflector->getMethod('__clone')->isPublic()) {
+ return true;
+ }
+
+ return false;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TestFailure collects a failed test together with the caught exception.
+ */
+class PHPUnit_Framework_TestFailure
+{
+ /**
+ * @var string
+ */
+ private $testName;
+
+ /**
+ * @var PHPUnit_Framework_Test|null
+ */
+ protected $failedTest;
+
+ /**
+ * @var Exception
+ */
+ protected $thrownException;
+
+ /**
+ * Constructs a TestFailure with the given test and exception.
+ *
+ * @param PHPUnit_Framework_Test $failedTest
+ * @param Throwable $t
+ */
+ public function __construct(PHPUnit_Framework_Test $failedTest, $t)
+ {
+ if ($failedTest instanceof PHPUnit_Framework_SelfDescribing) {
+ $this->testName = $failedTest->toString();
+ } else {
+ $this->testName = get_class($failedTest);
+ }
+
+ if (!$failedTest instanceof PHPUnit_Framework_TestCase || !$failedTest->isInIsolation()) {
+ $this->failedTest = $failedTest;
+ }
+
+ $this->thrownException = $t;
+ }
+
+ /**
+ * Returns a short description of the failure.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return sprintf(
+ '%s: %s',
+ $this->testName,
+ $this->thrownException->getMessage()
+ );
+ }
+
+ /**
+ * Returns a description for the thrown exception.
+ *
+ * @return string
+ */
+ public function getExceptionAsString()
+ {
+ return self::exceptionToString($this->thrownException);
+ }
+
+ /**
+ * Returns a description for an exception.
+ *
+ * @param Exception $e
+ *
+ * @return string
+ */
+ public static function exceptionToString(Exception $e)
+ {
+ if ($e instanceof PHPUnit_Framework_SelfDescribing) {
+ $buffer = $e->toString();
+
+ if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) {
+ $buffer = $buffer . $e->getComparisonFailure()->getDiff();
+ }
+
+ if (!empty($buffer)) {
+ $buffer = trim($buffer) . "\n";
+ }
+ } elseif ($e instanceof PHPUnit_Framework_Error) {
+ $buffer = $e->getMessage() . "\n";
+ } elseif ($e instanceof PHPUnit_Framework_ExceptionWrapper) {
+ $buffer = $e->getClassName() . ': ' . $e->getMessage() . "\n";
+ } else {
+ $buffer = get_class($e) . ': ' . $e->getMessage() . "\n";
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Returns the name of the failing test (including data set, if any).
+ *
+ * @return string
+ */
+ public function getTestName()
+ {
+ return $this->testName;
+ }
+
+ /**
+ * Returns the failing test.
+ *
+ * Note: The test object is not set when the test is executed in process
+ * isolation.
+ *
+ * @see PHPUnit_Framework_Exception
+ *
+ * @return PHPUnit_Framework_Test|null
+ */
+ public function failedTest()
+ {
+ return $this->failedTest;
+ }
+
+ /**
+ * Gets the thrown exception.
+ *
+ * @return Exception
+ */
+ public function thrownException()
+ {
+ return $this->thrownException;
+ }
+
+ /**
+ * Returns the exception's message.
+ *
+ * @return string
+ */
+ public function exceptionMessage()
+ {
+ return $this->thrownException()->getMessage();
+ }
+
+ /**
+ * Returns true if the thrown exception
+ * is of type AssertionFailedError.
+ *
+ * @return bool
+ */
+ public function isFailure()
+ {
+ return ($this->thrownException() instanceof PHPUnit_Framework_AssertionFailedError);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A Listener for test progress.
+ */
+interface PHPUnit_Framework_TestListener
+{
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time);
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ *
+ * @todo Uncomment in time for PHPUnit 6.0.0
+ *
+ * @see https://github.com/sebastianbergmann/phpunit/pull/1840#issuecomment-162535997
+ */
+// public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time);
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time);
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time);
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time);
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time);
+
+ /**
+ * A test suite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite);
+
+ /**
+ * A test suite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite);
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test);
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time);
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
+use SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException;
+use SebastianBergmann\CodeCoverage\MissingCoversAnnotationException;
+use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException;
+use SebastianBergmann\ResourceOperations\ResourceOperations;
+
+/**
+ * A TestResult collects the results of executing a test case.
+ */
+class PHPUnit_Framework_TestResult implements Countable
+{
+ /**
+ * @var array
+ */
+ protected $passed = [];
+
+ /**
+ * @var array
+ */
+ protected $errors = [];
+
+ /**
+ * @var array
+ */
+ protected $failures = [];
+
+ /**
+ * @var array
+ */
+ protected $warnings = [];
+
+ /**
+ * @var array
+ */
+ protected $notImplemented = [];
+
+ /**
+ * @var array
+ */
+ protected $risky = [];
+
+ /**
+ * @var array
+ */
+ protected $skipped = [];
+
+ /**
+ * @var array
+ */
+ protected $listeners = [];
+
+ /**
+ * @var int
+ */
+ protected $runTests = 0;
+
+ /**
+ * @var float
+ */
+ protected $time = 0;
+
+ /**
+ * @var PHPUnit_Framework_TestSuite
+ */
+ protected $topTestSuite = null;
+
+ /**
+ * Code Coverage information.
+ *
+ * @var CodeCoverage
+ */
+ protected $codeCoverage;
+
+ /**
+ * @var bool
+ */
+ protected $convertErrorsToExceptions = true;
+
+ /**
+ * @var bool
+ */
+ protected $stop = false;
+
+ /**
+ * @var bool
+ */
+ protected $stopOnError = false;
+
+ /**
+ * @var bool
+ */
+ protected $stopOnFailure = false;
+
+ /**
+ * @var bool
+ */
+ protected $stopOnWarning = false;
+
+ /**
+ * @var bool
+ */
+ protected $beStrictAboutTestsThatDoNotTestAnything = false;
+
+ /**
+ * @var bool
+ */
+ protected $beStrictAboutOutputDuringTests = false;
+
+ /**
+ * @var bool
+ */
+ protected $beStrictAboutTodoAnnotatedTests = false;
+
+ /**
+ * @var bool
+ */
+ protected $beStrictAboutResourceUsageDuringSmallTests = false;
+
+ /**
+ * @var bool
+ */
+ protected $enforceTimeLimit = false;
+
+ /**
+ * @var int
+ */
+ protected $timeoutForSmallTests = 1;
+
+ /**
+ * @var int
+ */
+ protected $timeoutForMediumTests = 10;
+
+ /**
+ * @var int
+ */
+ protected $timeoutForLargeTests = 60;
+
+ /**
+ * @var bool
+ */
+ protected $stopOnRisky = false;
+
+ /**
+ * @var bool
+ */
+ protected $stopOnIncomplete = false;
+
+ /**
+ * @var bool
+ */
+ protected $stopOnSkipped = false;
+
+ /**
+ * @var bool
+ */
+ protected $lastTestFailed = false;
+
+ /**
+ * @var bool
+ */
+ private $registerMockObjectsFromTestArgumentsRecursively = false;
+
+ /**
+ * Registers a TestListener.
+ *
+ * @param PHPUnit_Framework_TestListener
+ */
+ public function addListener(PHPUnit_Framework_TestListener $listener)
+ {
+ $this->listeners[] = $listener;
+ }
+
+ /**
+ * Unregisters a TestListener.
+ *
+ * @param PHPUnit_Framework_TestListener $listener
+ */
+ public function removeListener(PHPUnit_Framework_TestListener $listener)
+ {
+ foreach ($this->listeners as $key => $_listener) {
+ if ($listener === $_listener) {
+ unset($this->listeners[$key]);
+ }
+ }
+ }
+
+ /**
+ * Flushes all flushable TestListeners.
+ */
+ public function flushListeners()
+ {
+ foreach ($this->listeners as $listener) {
+ if ($listener instanceof PHPUnit_Util_Printer) {
+ $listener->flush();
+ }
+ }
+ }
+
+ /**
+ * Adds an error to the list of errors.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Throwable $t
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, $t, $time)
+ {
+ if ($t instanceof PHPUnit_Framework_RiskyTest) {
+ $this->risky[] = new PHPUnit_Framework_TestFailure($test, $t);
+ $notifyMethod = 'addRiskyTest';
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $test->markAsRisky();
+ }
+
+ if ($this->stopOnRisky) {
+ $this->stop();
+ }
+ } elseif ($t instanceof PHPUnit_Framework_IncompleteTest) {
+ $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $t);
+ $notifyMethod = 'addIncompleteTest';
+
+ if ($this->stopOnIncomplete) {
+ $this->stop();
+ }
+ } elseif ($t instanceof PHPUnit_Framework_SkippedTest) {
+ $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $t);
+ $notifyMethod = 'addSkippedTest';
+
+ if ($this->stopOnSkipped) {
+ $this->stop();
+ }
+ } else {
+ $this->errors[] = new PHPUnit_Framework_TestFailure($test, $t);
+ $notifyMethod = 'addError';
+
+ if ($this->stopOnError || $this->stopOnFailure) {
+ $this->stop();
+ }
+ }
+
+ // @see https://github.com/sebastianbergmann/phpunit/issues/1953
+ if ($t instanceof Error) {
+ $t = new PHPUnit_Framework_ExceptionWrapper($t);
+ }
+
+ foreach ($this->listeners as $listener) {
+ $listener->$notifyMethod($test, $t, $time);
+ }
+
+ $this->lastTestFailed = true;
+ $this->time += $time;
+ }
+
+ /**
+ * Adds a warning to the list of warnings.
+ * The passed in exception caused the warning.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ if ($this->stopOnWarning) {
+ $this->stop();
+ }
+
+ $this->warnings[] = new PHPUnit_Framework_TestFailure($test, $e);
+
+ foreach ($this->listeners as $listener) {
+ // @todo Remove check for PHPUnit 6.0.0
+ // @see https://github.com/sebastianbergmann/phpunit/pull/1840#issuecomment-162535997
+ if (method_exists($listener, 'addWarning')) {
+ $listener->addWarning($test, $e, $time);
+ }
+ }
+
+ $this->time += $time;
+ }
+
+ /**
+ * Adds a failure to the list of failures.
+ * The passed in exception caused the failure.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ if ($e instanceof PHPUnit_Framework_RiskyTest ||
+ $e instanceof PHPUnit_Framework_OutputError) {
+ $this->risky[] = new PHPUnit_Framework_TestFailure($test, $e);
+ $notifyMethod = 'addRiskyTest';
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $test->markAsRisky();
+ }
+
+ if ($this->stopOnRisky) {
+ $this->stop();
+ }
+ } elseif ($e instanceof PHPUnit_Framework_IncompleteTest) {
+ $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e);
+ $notifyMethod = 'addIncompleteTest';
+
+ if ($this->stopOnIncomplete) {
+ $this->stop();
+ }
+ } elseif ($e instanceof PHPUnit_Framework_SkippedTest) {
+ $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e);
+ $notifyMethod = 'addSkippedTest';
+
+ if ($this->stopOnSkipped) {
+ $this->stop();
+ }
+ } else {
+ $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e);
+ $notifyMethod = 'addFailure';
+
+ if ($this->stopOnFailure) {
+ $this->stop();
+ }
+ }
+
+ foreach ($this->listeners as $listener) {
+ $listener->$notifyMethod($test, $e, $time);
+ }
+
+ $this->lastTestFailed = true;
+ $this->time += $time;
+ }
+
+ /**
+ * Informs the result that a testsuite will be started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ if ($this->topTestSuite === null) {
+ $this->topTestSuite = $suite;
+ }
+
+ foreach ($this->listeners as $listener) {
+ $listener->startTestSuite($suite);
+ }
+ }
+
+ /**
+ * Informs the result that a testsuite was completed.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ foreach ($this->listeners as $listener) {
+ $listener->endTestSuite($suite);
+ }
+ }
+
+ /**
+ * Informs the result that a test will be started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ $this->lastTestFailed = false;
+ $this->runTests += count($test);
+
+ foreach ($this->listeners as $listener) {
+ $listener->startTest($test);
+ }
+ }
+
+ /**
+ * Informs the result that a test was completed.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ foreach ($this->listeners as $listener) {
+ $listener->endTest($test, $time);
+ }
+
+ if (!$this->lastTestFailed && $test instanceof PHPUnit_Framework_TestCase) {
+ $class = get_class($test);
+ $key = $class . '::' . $test->getName();
+
+ $this->passed[$key] = [
+ 'result' => $test->getResult(),
+ 'size' => PHPUnit_Util_Test::getSize(
+ $class,
+ $test->getName(false)
+ )
+ ];
+
+ $this->time += $time;
+ }
+ }
+
+ /**
+ * Returns true if no risky test occurred.
+ *
+ * @return bool
+ */
+ public function allHarmless()
+ {
+ return $this->riskyCount() == 0;
+ }
+
+ /**
+ * Gets the number of risky tests.
+ *
+ * @return int
+ */
+ public function riskyCount()
+ {
+ return count($this->risky);
+ }
+
+ /**
+ * Returns true if no incomplete test occurred.
+ *
+ * @return bool
+ */
+ public function allCompletelyImplemented()
+ {
+ return $this->notImplementedCount() == 0;
+ }
+
+ /**
+ * Gets the number of incomplete tests.
+ *
+ * @return int
+ */
+ public function notImplementedCount()
+ {
+ return count($this->notImplemented);
+ }
+
+ /**
+ * Returns an Enumeration for the risky tests.
+ *
+ * @return array
+ */
+ public function risky()
+ {
+ return $this->risky;
+ }
+
+ /**
+ * Returns an Enumeration for the incomplete tests.
+ *
+ * @return array
+ */
+ public function notImplemented()
+ {
+ return $this->notImplemented;
+ }
+
+ /**
+ * Returns true if no test has been skipped.
+ *
+ * @return bool
+ */
+ public function noneSkipped()
+ {
+ return $this->skippedCount() == 0;
+ }
+
+ /**
+ * Gets the number of skipped tests.
+ *
+ * @return int
+ */
+ public function skippedCount()
+ {
+ return count($this->skipped);
+ }
+
+ /**
+ * Returns an Enumeration for the skipped tests.
+ *
+ * @return array
+ */
+ public function skipped()
+ {
+ return $this->skipped;
+ }
+
+ /**
+ * Gets the number of detected errors.
+ *
+ * @return int
+ */
+ public function errorCount()
+ {
+ return count($this->errors);
+ }
+
+ /**
+ * Returns an Enumeration for the errors.
+ *
+ * @return array
+ */
+ public function errors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Gets the number of detected failures.
+ *
+ * @return int
+ */
+ public function failureCount()
+ {
+ return count($this->failures);
+ }
+
+ /**
+ * Returns an Enumeration for the failures.
+ *
+ * @return array
+ */
+ public function failures()
+ {
+ return $this->failures;
+ }
+
+ /**
+ * Gets the number of detected warnings.
+ *
+ * @return int
+ */
+ public function warningCount()
+ {
+ return count($this->warnings);
+ }
+
+ /**
+ * Returns an Enumeration for the warnings.
+ *
+ * @return array
+ */
+ public function warnings()
+ {
+ return $this->warnings;
+ }
+
+ /**
+ * Returns the names of the tests that have passed.
+ *
+ * @return array
+ */
+ public function passed()
+ {
+ return $this->passed;
+ }
+
+ /**
+ * Returns the (top) test suite.
+ *
+ * @return PHPUnit_Framework_TestSuite
+ */
+ public function topTestSuite()
+ {
+ return $this->topTestSuite;
+ }
+
+ /**
+ * Returns whether code coverage information should be collected.
+ *
+ * @return bool If code coverage should be collected
+ */
+ public function getCollectCodeCoverageInformation()
+ {
+ return $this->codeCoverage !== null;
+ }
+
+ /**
+ * Runs a TestCase.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function run(PHPUnit_Framework_Test $test)
+ {
+ PHPUnit_Framework_Assert::resetCount();
+
+ $coversNothing = false;
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $test->setRegisterMockObjectsFromTestArgumentsRecursively(
+ $this->registerMockObjectsFromTestArgumentsRecursively
+ );
+
+ $annotations = $test->getAnnotations();
+
+ if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) {
+ $coversNothing = true;
+ }
+ }
+
+ $error = false;
+ $failure = false;
+ $warning = false;
+ $incomplete = false;
+ $risky = false;
+ $skipped = false;
+
+ $this->startTest($test);
+
+ $errorHandlerSet = false;
+
+ if ($this->convertErrorsToExceptions) {
+ $oldErrorHandler = set_error_handler(
+ ['PHPUnit_Util_ErrorHandler', 'handleError'],
+ E_ALL | E_STRICT
+ );
+
+ if ($oldErrorHandler === null) {
+ $errorHandlerSet = true;
+ } else {
+ restore_error_handler();
+ }
+ }
+
+ $collectCodeCoverage = $this->codeCoverage !== null &&
+ !$test instanceof PHPUnit_Framework_WarningTestCase &&
+ !$coversNothing;
+
+ if ($collectCodeCoverage) {
+ $this->codeCoverage->start($test);
+ }
+
+ $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests &&
+ !$test instanceof PHPUnit_Framework_WarningTestCase &&
+ $test->getSize() == PHPUnit_Util_Test::SMALL &&
+ function_exists('xdebug_start_function_monitor');
+
+ if ($monitorFunctions) {
+ xdebug_start_function_monitor(ResourceOperations::getFunctions());
+ }
+
+ PHP_Timer::start();
+
+ try {
+ if (!$test instanceof PHPUnit_Framework_WarningTestCase &&
+ $test->getSize() != PHPUnit_Util_Test::UNKNOWN &&
+ $this->enforceTimeLimit &&
+ extension_loaded('pcntl') && class_exists('PHP_Invoker')) {
+ switch ($test->getSize()) {
+ case PHPUnit_Util_Test::SMALL:
+ $_timeout = $this->timeoutForSmallTests;
+ break;
+
+ case PHPUnit_Util_Test::MEDIUM:
+ $_timeout = $this->timeoutForMediumTests;
+ break;
+
+ case PHPUnit_Util_Test::LARGE:
+ $_timeout = $this->timeoutForLargeTests;
+ break;
+ }
+
+ $invoker = new PHP_Invoker;
+ $invoker->invoke([$test, 'runBare'], [], $_timeout);
+ } else {
+ $test->runBare();
+ }
+ } catch (PHP_Invoker_TimeoutException $e) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_RiskyTestError(
+ $e->getMessage()
+ ),
+ $_timeout
+ );
+
+ $risky = true;
+ } catch (PHPUnit_Framework_MockObject_Exception $e) {
+ $e = new PHPUnit_Framework_Warning(
+ $e->getMessage()
+ );
+
+ $warning = true;
+ } catch (PHPUnit_Framework_AssertionFailedError $e) {
+ $failure = true;
+
+ if ($e instanceof PHPUnit_Framework_RiskyTestError) {
+ $risky = true;
+ } elseif ($e instanceof PHPUnit_Framework_IncompleteTestError) {
+ $incomplete = true;
+ } elseif ($e instanceof PHPUnit_Framework_SkippedTestError) {
+ $skipped = true;
+ }
+ } catch (PHPUnit_Framework_Warning $e) {
+ $warning = true;
+ } catch (PHPUnit_Framework_Exception $e) {
+ $error = true;
+ } catch (Throwable $e) {
+ // @see https://github.com/sebastianbergmann/phpunit/issues/2394
+ if (PHP_MAJOR_VERSION === 7 && $e instanceof \AssertionError) {
+ $test->addToAssertionCount(1);
+
+ $failure = true;
+ $frame = $e->getTrace()[0];
+
+ $e = new PHPUnit_Framework_AssertionFailedError(
+ sprintf(
+ '%s in %s:%s',
+ $e->getMessage(),
+ $frame['file'],
+ $frame['line']
+ )
+ );
+ } else {
+ $e = new PHPUnit_Framework_ExceptionWrapper($e);
+ $error = true;
+ }
+ } catch (Exception $e) {
+ $e = new PHPUnit_Framework_ExceptionWrapper($e);
+ $error = true;
+ }
+
+ $time = PHP_Timer::stop();
+ $test->addToAssertionCount(PHPUnit_Framework_Assert::getCount());
+
+ if ($monitorFunctions) {
+ $blacklist = new PHPUnit_Util_Blacklist;
+ $functions = xdebug_get_monitored_functions();
+ xdebug_stop_function_monitor();
+
+ foreach ($functions as $function) {
+ if (!$blacklist->isBlacklisted($function['filename'])) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_RiskyTestError(
+ sprintf(
+ '%s() used in %s:%s',
+ $function['function'],
+ $function['filename'],
+ $function['lineno']
+ )
+ ),
+ $time
+ );
+ }
+ }
+ }
+
+ if ($this->beStrictAboutTestsThatDoNotTestAnything &&
+ $test->getNumAssertions() == 0) {
+ $risky = true;
+ }
+
+ if ($collectCodeCoverage) {
+ $append = !$risky && !$incomplete && !$skipped;
+ $linesToBeCovered = [];
+ $linesToBeUsed = [];
+
+ if ($append && $test instanceof PHPUnit_Framework_TestCase) {
+ try {
+ $linesToBeCovered = PHPUnit_Util_Test::getLinesToBeCovered(
+ get_class($test),
+ $test->getName(false)
+ );
+
+ $linesToBeUsed = PHPUnit_Util_Test::getLinesToBeUsed(
+ get_class($test),
+ $test->getName(false)
+ );
+ } catch (PHPUnit_Framework_InvalidCoversTargetException $cce) {
+ $this->addWarning(
+ $test,
+ new PHPUnit_Framework_Warning(
+ $cce->getMessage()
+ ),
+ $time
+ );
+ }
+ }
+
+ try {
+ $this->codeCoverage->stop(
+ $append,
+ $linesToBeCovered,
+ $linesToBeUsed
+ );
+ } catch (UnintentionallyCoveredCodeException $cce) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_UnintentionallyCoveredCodeError(
+ 'This test executed code that is not listed as code to be covered or used:' .
+ PHP_EOL . $cce->getMessage()
+ ),
+ $time
+ );
+ } catch (CoveredCodeNotExecutedException $cce) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_CoveredCodeNotExecutedException(
+ 'This test did not execute all the code that is listed as code to be covered:' .
+ PHP_EOL . $cce->getMessage()
+ ),
+ $time
+ );
+ } catch (MissingCoversAnnotationException $cce) {
+ if ($linesToBeCovered !== false) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_MissingCoversAnnotationException(
+ 'This test does not have a @covers annotation but is expected to have one'
+ ),
+ $time
+ );
+ }
+ } catch (CodeCoverageException $cce) {
+ $error = true;
+
+ if (!isset($e)) {
+ $e = $cce;
+ }
+ }
+ }
+
+ if ($errorHandlerSet === true) {
+ restore_error_handler();
+ }
+
+ if ($error === true) {
+ $this->addError($test, $e, $time);
+ } elseif ($failure === true) {
+ $this->addFailure($test, $e, $time);
+ } elseif ($warning === true) {
+ $this->addWarning($test, $e, $time);
+ } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
+ !$test->doesNotPerformAssertions() &&
+ $test->getNumAssertions() == 0) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_RiskyTestError(
+ 'This test did not perform any assertions'
+ ),
+ $time
+ );
+ } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_OutputError(
+ sprintf(
+ 'This test printed output: %s',
+ $test->getActualOutput()
+ )
+ ),
+ $time
+ );
+ } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof PHPUnit_Framework_TestCase) {
+ $annotations = $test->getAnnotations();
+
+ if (isset($annotations['method']['todo'])) {
+ $this->addFailure(
+ $test,
+ new PHPUnit_Framework_RiskyTestError(
+ 'Test method is annotated with @todo'
+ ),
+ $time
+ );
+ }
+ }
+
+ $this->endTest($test, $time);
+ }
+
+ /**
+ * Gets the number of run tests.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->runTests;
+ }
+
+ /**
+ * Checks whether the test run should stop.
+ *
+ * @return bool
+ */
+ public function shouldStop()
+ {
+ return $this->stop;
+ }
+
+ /**
+ * Marks that the test run should stop.
+ */
+ public function stop()
+ {
+ $this->stop = true;
+ }
+
+ /**
+ * Returns the code coverage object.
+ *
+ * @return CodeCoverage
+ */
+ public function getCodeCoverage()
+ {
+ return $this->codeCoverage;
+ }
+
+ /**
+ * Sets the code coverage object.
+ *
+ * @param CodeCoverage $codeCoverage
+ */
+ public function setCodeCoverage(CodeCoverage $codeCoverage)
+ {
+ $this->codeCoverage = $codeCoverage;
+ }
+
+ /**
+ * Enables or disables the error-to-exception conversion.
+ *
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function convertErrorsToExceptions($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->convertErrorsToExceptions = $flag;
+ }
+
+ /**
+ * Returns the error-to-exception conversion setting.
+ *
+ * @return bool
+ */
+ public function getConvertErrorsToExceptions()
+ {
+ return $this->convertErrorsToExceptions;
+ }
+
+ /**
+ * Enables or disables the stopping when an error occurs.
+ *
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function stopOnError($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->stopOnError = $flag;
+ }
+
+ /**
+ * Enables or disables the stopping when a failure occurs.
+ *
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function stopOnFailure($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->stopOnFailure = $flag;
+ }
+
+ /**
+ * Enables or disables the stopping when a warning occurs.
+ *
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function stopOnWarning($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->stopOnWarning = $flag;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function beStrictAboutTestsThatDoNotTestAnything($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->beStrictAboutTestsThatDoNotTestAnything = $flag;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isStrictAboutTestsThatDoNotTestAnything()
+ {
+ return $this->beStrictAboutTestsThatDoNotTestAnything;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function beStrictAboutOutputDuringTests($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->beStrictAboutOutputDuringTests = $flag;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isStrictAboutOutputDuringTests()
+ {
+ return $this->beStrictAboutOutputDuringTests;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function beStrictAboutResourceUsageDuringSmallTests($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->beStrictAboutResourceUsageDuringSmallTests = $flag;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isStrictAboutResourceUsageDuringSmallTests()
+ {
+ return $this->beStrictAboutResourceUsageDuringSmallTests;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function enforceTimeLimit($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->enforceTimeLimit = $flag;
+ }
+
+ /**
+ * @return bool
+ */
+ public function enforcesTimeLimit()
+ {
+ return $this->enforceTimeLimit;
+ }
+
+ /**
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function beStrictAboutTodoAnnotatedTests($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->beStrictAboutTodoAnnotatedTests = $flag;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isStrictAboutTodoAnnotatedTests()
+ {
+ return $this->beStrictAboutTodoAnnotatedTests;
+ }
+
+ /**
+ * Enables or disables the stopping for risky tests.
+ *
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function stopOnRisky($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->stopOnRisky = $flag;
+ }
+
+ /**
+ * Enables or disables the stopping for incomplete tests.
+ *
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function stopOnIncomplete($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->stopOnIncomplete = $flag;
+ }
+
+ /**
+ * Enables or disables the stopping for skipped tests.
+ *
+ * @param bool $flag
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function stopOnSkipped($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->stopOnSkipped = $flag;
+ }
+
+ /**
+ * Returns the time spent running the tests.
+ *
+ * @return float
+ */
+ public function time()
+ {
+ return $this->time;
+ }
+
+ /**
+ * Returns whether the entire test was successful or not.
+ *
+ * @param bool $includeWarnings
+ *
+ * @return bool
+ */
+ public function wasSuccessful($includeWarnings = true)
+ {
+ if ($includeWarnings) {
+ return empty($this->errors) && empty($this->failures) && empty($this->warnings);
+ } else {
+ return empty($this->errors) && empty($this->failures);
+ }
+ }
+
+ /**
+ * Sets the timeout for small tests.
+ *
+ * @param int $timeout
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setTimeoutForSmallTests($timeout)
+ {
+ if (!is_int($timeout)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
+ }
+
+ $this->timeoutForSmallTests = $timeout;
+ }
+
+ /**
+ * Sets the timeout for medium tests.
+ *
+ * @param int $timeout
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setTimeoutForMediumTests($timeout)
+ {
+ if (!is_int($timeout)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
+ }
+
+ $this->timeoutForMediumTests = $timeout;
+ }
+
+ /**
+ * Sets the timeout for large tests.
+ *
+ * @param int $timeout
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setTimeoutForLargeTests($timeout)
+ {
+ if (!is_int($timeout)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
+ }
+
+ $this->timeoutForLargeTests = $timeout;
+ }
+
+ /**
+ * Returns the set timeout for large tests.
+ *
+ * @return int
+ */
+ public function getTimeoutForLargeTests()
+ {
+ return $this->timeoutForLargeTests;
+ }
+
+ /**
+ * @param bool $flag
+ */
+ public function setRegisterMockObjectsFromTestArgumentsRecursively($flag)
+ {
+ if (!is_bool($flag)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->registerMockObjectsFromTestArgumentsRecursively = $flag;
+ }
+
+ /**
+ * Returns the class hierarchy for a given class.
+ *
+ * @param string $className
+ * @param bool $asReflectionObjects
+ *
+ * @return array
+ */
+ protected function getHierarchy($className, $asReflectionObjects = false)
+ {
+ if ($asReflectionObjects) {
+ $classes = [new ReflectionClass($className)];
+ } else {
+ $classes = [$className];
+ }
+
+ $done = false;
+
+ while (!$done) {
+ if ($asReflectionObjects) {
+ $class = new ReflectionClass(
+ $classes[count($classes) - 1]->getName()
+ );
+ } else {
+ $class = new ReflectionClass($classes[count($classes) - 1]);
+ }
+
+ $parent = $class->getParentClass();
+
+ if ($parent !== false) {
+ if ($asReflectionObjects) {
+ $classes[] = $parent;
+ } else {
+ $classes[] = $parent->getName();
+ }
+ } else {
+ $done = true;
+ }
+ }
+
+ return $classes;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TestSuite is a composite of Tests. It runs a collection of test cases.
+ */
+class PHPUnit_Framework_TestSuite implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing, IteratorAggregate
+{
+ /**
+ * Last count of tests in this suite.
+ *
+ * @var int|null
+ */
+ private $cachedNumTests;
+
+ /**
+ * Enable or disable the backup and restoration of the $GLOBALS array.
+ *
+ * @var bool
+ */
+ protected $backupGlobals = null;
+
+ /**
+ * Enable or disable the backup and restoration of static attributes.
+ *
+ * @var bool
+ */
+ protected $backupStaticAttributes = null;
+
+ /**
+ * @var bool
+ */
+ private $beStrictAboutChangesToGlobalState = null;
+
+ /**
+ * @var bool
+ */
+ protected $runTestInSeparateProcess = false;
+
+ /**
+ * The name of the test suite.
+ *
+ * @var string
+ */
+ protected $name = '';
+
+ /**
+ * The test groups of the test suite.
+ *
+ * @var array
+ */
+ protected $groups = [];
+
+ /**
+ * The tests in the test suite.
+ *
+ * @var array
+ */
+ protected $tests = [];
+
+ /**
+ * The number of tests in the test suite.
+ *
+ * @var int
+ */
+ protected $numTests = -1;
+
+ /**
+ * @var bool
+ */
+ protected $testCase = false;
+
+ /**
+ * @var array
+ */
+ protected $foundClasses = [];
+
+ /**
+ * @var PHPUnit_Runner_Filter_Factory
+ */
+ private $iteratorFilter = null;
+
+ /**
+ * Constructs a new TestSuite:
+ *
+ * - PHPUnit_Framework_TestSuite() constructs an empty TestSuite.
+ *
+ * - PHPUnit_Framework_TestSuite(ReflectionClass) constructs a
+ * TestSuite from the given class.
+ *
+ * - PHPUnit_Framework_TestSuite(ReflectionClass, String)
+ * constructs a TestSuite from the given class with the given
+ * name.
+ *
+ * - PHPUnit_Framework_TestSuite(String) either constructs a
+ * TestSuite from the given class (if the passed string is the
+ * name of an existing class) or constructs an empty TestSuite
+ * with the given name.
+ *
+ * @param mixed $theClass
+ * @param string $name
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($theClass = '', $name = '')
+ {
+ $argumentsValid = false;
+
+ if (is_object($theClass) &&
+ $theClass instanceof ReflectionClass) {
+ $argumentsValid = true;
+ } elseif (is_string($theClass) &&
+ $theClass !== '' &&
+ class_exists($theClass, false)) {
+ $argumentsValid = true;
+
+ if ($name == '') {
+ $name = $theClass;
+ }
+
+ $theClass = new ReflectionClass($theClass);
+ } elseif (is_string($theClass)) {
+ $this->setName($theClass);
+
+ return;
+ }
+
+ if (!$argumentsValid) {
+ throw new PHPUnit_Framework_Exception;
+ }
+
+ if (!$theClass->isSubclassOf('PHPUnit_Framework_TestCase')) {
+ throw new PHPUnit_Framework_Exception(
+ 'Class "' . $theClass->name . '" does not extend PHPUnit_Framework_TestCase.'
+ );
+ }
+
+ if ($name != '') {
+ $this->setName($name);
+ } else {
+ $this->setName($theClass->getName());
+ }
+
+ $constructor = $theClass->getConstructor();
+
+ if ($constructor !== null &&
+ !$constructor->isPublic()) {
+ $this->addTest(
+ self::warning(
+ sprintf(
+ 'Class "%s" has no public constructor.',
+ $theClass->getName()
+ )
+ )
+ );
+
+ return;
+ }
+
+ foreach ($theClass->getMethods() as $method) {
+ $this->addTestMethod($theClass, $method);
+ }
+
+ if (empty($this->tests)) {
+ $this->addTest(
+ self::warning(
+ sprintf(
+ 'No tests found in class "%s".',
+ $theClass->getName()
+ )
+ )
+ );
+ }
+
+ $this->testCase = true;
+ }
+
+ /**
+ * Returns a string representation of the test suite.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->getName();
+ }
+
+ /**
+ * Adds a test to the suite.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param array $groups
+ */
+ public function addTest(PHPUnit_Framework_Test $test, $groups = [])
+ {
+ $class = new ReflectionClass($test);
+
+ if (!$class->isAbstract()) {
+ $this->tests[] = $test;
+ $this->numTests = -1;
+
+ if ($test instanceof self &&
+ empty($groups)) {
+ $groups = $test->getGroups();
+ }
+
+ if (empty($groups)) {
+ $groups = ['default'];
+ }
+
+ foreach ($groups as $group) {
+ if (!isset($this->groups[$group])) {
+ $this->groups[$group] = [$test];
+ } else {
+ $this->groups[$group][] = $test;
+ }
+ }
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $test->setGroups($groups);
+ }
+ }
+ }
+
+ /**
+ * Adds the tests from the given class to the suite.
+ *
+ * @param mixed $testClass
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function addTestSuite($testClass)
+ {
+ if (is_string($testClass) && class_exists($testClass)) {
+ $testClass = new ReflectionClass($testClass);
+ }
+
+ if (!is_object($testClass)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'class name or object'
+ );
+ }
+
+ if ($testClass instanceof self) {
+ $this->addTest($testClass);
+ } elseif ($testClass instanceof ReflectionClass) {
+ $suiteMethod = false;
+
+ if (!$testClass->isAbstract()) {
+ if ($testClass->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) {
+ $method = $testClass->getMethod(
+ PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME
+ );
+
+ if ($method->isStatic()) {
+ $this->addTest(
+ $method->invoke(null, $testClass->getName())
+ );
+
+ $suiteMethod = true;
+ }
+ }
+ }
+
+ if (!$suiteMethod && !$testClass->isAbstract()) {
+ $this->addTest(new self($testClass));
+ }
+ } else {
+ throw new PHPUnit_Framework_Exception;
+ }
+ }
+
+ /**
+ * Wraps both <code>addTest()</code> and <code>addTestSuite</code>
+ * as well as the separate import statements for the user's convenience.
+ *
+ * If the named file cannot be read or there are no new tests that can be
+ * added, a <code>PHPUnit_Framework_WarningTestCase</code> will be created instead,
+ * leaving the current test run untouched.
+ *
+ * @param string $filename
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function addTestFile($filename)
+ {
+ if (!is_string($filename)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (file_exists($filename) && substr($filename, -5) == '.phpt') {
+ $this->addTest(
+ new PHPUnit_Extensions_PhptTestCase($filename)
+ );
+
+ return;
+ }
+
+ // The given file may contain further stub classes in addition to the
+ // test class itself. Figure out the actual test class.
+ $classes = get_declared_classes();
+ $filename = PHPUnit_Util_Fileloader::checkAndLoad($filename);
+ $newClasses = array_diff(get_declared_classes(), $classes);
+
+ // The diff is empty in case a parent class (with test methods) is added
+ // AFTER a child class that inherited from it. To account for that case,
+ // cumulate all discovered classes, so the parent class may be found in
+ // a later invocation.
+ if (!empty($newClasses)) {
+ // On the assumption that test classes are defined first in files,
+ // process discovered classes in approximate LIFO order, so as to
+ // avoid unnecessary reflection.
+ $this->foundClasses = array_merge($newClasses, $this->foundClasses);
+ }
+
+ // The test class's name must match the filename, either in full, or as
+ // a PEAR/PSR-0 prefixed shortname ('NameSpace_ShortName'), or as a
+ // PSR-1 local shortname ('NameSpace\ShortName'). The comparison must be
+ // anchored to prevent false-positive matches (e.g., 'OtherShortName').
+ $shortname = basename($filename, '.php');
+ $shortnameRegEx = '/(?:^|_|\\\\)' . preg_quote($shortname, '/') . '$/';
+
+ foreach ($this->foundClasses as $i => $className) {
+ if (preg_match($shortnameRegEx, $className)) {
+ $class = new ReflectionClass($className);
+
+ if ($class->getFileName() == $filename) {
+ $newClasses = [$className];
+ unset($this->foundClasses[$i]);
+ break;
+ }
+ }
+ }
+
+ foreach ($newClasses as $className) {
+ $class = new ReflectionClass($className);
+
+ if (!$class->isAbstract()) {
+ if ($class->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) {
+ $method = $class->getMethod(
+ PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME
+ );
+
+ if ($method->isStatic()) {
+ $this->addTest($method->invoke(null, $className));
+ }
+ } elseif ($class->implementsInterface('PHPUnit_Framework_Test')) {
+ $this->addTestSuite($class);
+ }
+ }
+ }
+
+ $this->numTests = -1;
+ }
+
+ /**
+ * Wrapper for addTestFile() that adds multiple test files.
+ *
+ * @param array|Iterator $filenames
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function addTestFiles($filenames)
+ {
+ if (!(is_array($filenames) ||
+ (is_object($filenames) && $filenames instanceof Iterator))) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 1,
+ 'array or iterator'
+ );
+ }
+
+ foreach ($filenames as $filename) {
+ $this->addTestFile((string) $filename);
+ }
+ }
+
+ /**
+ * Counts the number of test cases that will be run by this test.
+ *
+ * @param bool $preferCache Indicates if cache is preferred.
+ *
+ * @return int
+ */
+ public function count($preferCache = false)
+ {
+ if ($preferCache && $this->cachedNumTests !== null) {
+ $numTests = $this->cachedNumTests;
+ } else {
+ $numTests = 0;
+
+ foreach ($this as $test) {
+ $numTests += count($test);
+ }
+
+ $this->cachedNumTests = $numTests;
+ }
+
+ return $numTests;
+ }
+
+ /**
+ * @param ReflectionClass $theClass
+ * @param string $name
+ *
+ * @return PHPUnit_Framework_Test
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public static function createTest(ReflectionClass $theClass, $name)
+ {
+ $className = $theClass->getName();
+
+ if (!$theClass->isInstantiable()) {
+ return self::warning(
+ sprintf('Cannot instantiate class "%s".', $className)
+ );
+ }
+
+ $backupSettings = PHPUnit_Util_Test::getBackupSettings(
+ $className,
+ $name
+ );
+
+ $preserveGlobalState = PHPUnit_Util_Test::getPreserveGlobalStateSettings(
+ $className,
+ $name
+ );
+
+ $runTestInSeparateProcess = PHPUnit_Util_Test::getProcessIsolationSettings(
+ $className,
+ $name
+ );
+
+ $constructor = $theClass->getConstructor();
+
+ if ($constructor !== null) {
+ $parameters = $constructor->getParameters();
+
+ // TestCase() or TestCase($name)
+ if (count($parameters) < 2) {
+ $test = new $className;
+ } // TestCase($name, $data)
+ else {
+ try {
+ $data = PHPUnit_Util_Test::getProvidedData(
+ $className,
+ $name
+ );
+ } catch (PHPUnit_Framework_IncompleteTestError $e) {
+ $message = sprintf(
+ 'Test for %s::%s marked incomplete by data provider',
+ $className,
+ $name
+ );
+
+ $_message = $e->getMessage();
+
+ if (!empty($_message)) {
+ $message .= "\n" . $_message;
+ }
+
+ $data = self::incompleteTest($className, $name, $message);
+ } catch (PHPUnit_Framework_SkippedTestError $e) {
+ $message = sprintf(
+ 'Test for %s::%s skipped by data provider',
+ $className,
+ $name
+ );
+
+ $_message = $e->getMessage();
+
+ if (!empty($_message)) {
+ $message .= "\n" . $_message;
+ }
+
+ $data = self::skipTest($className, $name, $message);
+ } catch (Throwable $_t) {
+ $t = $_t;
+ } catch (Exception $_t) {
+ $t = $_t;
+ }
+
+ if (isset($t)) {
+ $message = sprintf(
+ 'The data provider specified for %s::%s is invalid.',
+ $className,
+ $name
+ );
+
+ $_message = $t->getMessage();
+
+ if (!empty($_message)) {
+ $message .= "\n" . $_message;
+ }
+
+ $data = self::warning($message);
+ }
+
+ // Test method with @dataProvider.
+ if (isset($data)) {
+ $test = new PHPUnit_Framework_TestSuite_DataProvider(
+ $className . '::' . $name
+ );
+
+ if (empty($data)) {
+ $data = self::warning(
+ sprintf(
+ 'No tests found in suite "%s".',
+ $test->getName()
+ )
+ );
+ }
+
+ $groups = PHPUnit_Util_Test::getGroups($className, $name);
+
+ if ($data instanceof PHPUnit_Framework_WarningTestCase ||
+ $data instanceof PHPUnit_Framework_SkippedTestCase ||
+ $data instanceof PHPUnit_Framework_IncompleteTestCase) {
+ $test->addTest($data, $groups);
+ } else {
+ foreach ($data as $_dataName => $_data) {
+ $_test = new $className($name, $_data, $_dataName);
+
+ if ($runTestInSeparateProcess) {
+ $_test->setRunTestInSeparateProcess(true);
+
+ if ($preserveGlobalState !== null) {
+ $_test->setPreserveGlobalState($preserveGlobalState);
+ }
+ }
+
+ if ($backupSettings['backupGlobals'] !== null) {
+ $_test->setBackupGlobals(
+ $backupSettings['backupGlobals']
+ );
+ }
+
+ if ($backupSettings['backupStaticAttributes'] !== null) {
+ $_test->setBackupStaticAttributes(
+ $backupSettings['backupStaticAttributes']
+ );
+ }
+
+ $test->addTest($_test, $groups);
+ }
+ }
+ } else {
+ $test = new $className;
+ }
+ }
+ }
+
+ if (!isset($test)) {
+ throw new PHPUnit_Framework_Exception('No valid test provided.');
+ }
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $test->setName($name);
+
+ if ($runTestInSeparateProcess) {
+ $test->setRunTestInSeparateProcess(true);
+
+ if ($preserveGlobalState !== null) {
+ $test->setPreserveGlobalState($preserveGlobalState);
+ }
+ }
+
+ if ($backupSettings['backupGlobals'] !== null) {
+ $test->setBackupGlobals($backupSettings['backupGlobals']);
+ }
+
+ if ($backupSettings['backupStaticAttributes'] !== null) {
+ $test->setBackupStaticAttributes(
+ $backupSettings['backupStaticAttributes']
+ );
+ }
+ }
+
+ return $test;
+ }
+
+ /**
+ * Creates a default TestResult object.
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ protected function createResult()
+ {
+ return new PHPUnit_Framework_TestResult;
+ }
+
+ /**
+ * Returns the name of the suite.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Returns the test groups of the suite.
+ *
+ * @return array
+ */
+ public function getGroups()
+ {
+ return array_keys($this->groups);
+ }
+
+ public function getGroupDetails()
+ {
+ return $this->groups;
+ }
+
+ /**
+ * Set tests groups of the test case
+ *
+ * @param array $groups
+ */
+ public function setGroupDetails(array $groups)
+ {
+ $this->groups = $groups;
+ }
+
+ /**
+ * Runs the tests and collects their result in a TestResult.
+ *
+ * @param PHPUnit_Framework_TestResult $result
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ public function run(PHPUnit_Framework_TestResult $result = null)
+ {
+ if ($result === null) {
+ $result = $this->createResult();
+ }
+
+ if (count($this) == 0) {
+ return $result;
+ }
+
+ $hookMethods = PHPUnit_Util_Test::getHookMethods($this->name);
+
+ $result->startTestSuite($this);
+
+ try {
+ $this->setUp();
+
+ foreach ($hookMethods['beforeClass'] as $beforeClassMethod) {
+ if ($this->testCase === true &&
+ class_exists($this->name, false) &&
+ method_exists($this->name, $beforeClassMethod)) {
+ if ($missingRequirements = PHPUnit_Util_Test::getMissingRequirements($this->name, $beforeClassMethod)) {
+ $this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements));
+ }
+
+ call_user_func([$this->name, $beforeClassMethod]);
+ }
+ }
+ } catch (PHPUnit_Framework_SkippedTestSuiteError $e) {
+ $numTests = count($this);
+
+ for ($i = 0; $i < $numTests; $i++) {
+ $result->startTest($this);
+ $result->addFailure($this, $e, 0);
+ $result->endTest($this, 0);
+ }
+
+ $this->tearDown();
+ $result->endTestSuite($this);
+
+ return $result;
+ } catch (Throwable $_t) {
+ $t = $_t;
+ } catch (Exception $_t) {
+ $t = $_t;
+ }
+
+ if (isset($t)) {
+ $numTests = count($this);
+
+ for ($i = 0; $i < $numTests; $i++) {
+ if ($result->shouldStop()) {
+ break;
+ }
+
+ $result->startTest($this);
+ $result->addError($this, $t, 0);
+ $result->endTest($this, 0);
+ }
+
+ $this->tearDown();
+ $result->endTestSuite($this);
+
+ return $result;
+ }
+
+ foreach ($this as $test) {
+ if ($result->shouldStop()) {
+ break;
+ }
+
+ if ($test instanceof PHPUnit_Framework_TestCase ||
+ $test instanceof self) {
+ $test->setbeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState);
+ $test->setBackupGlobals($this->backupGlobals);
+ $test->setBackupStaticAttributes($this->backupStaticAttributes);
+ $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess);
+ }
+
+ $test->run($result);
+ }
+
+ foreach ($hookMethods['afterClass'] as $afterClassMethod) {
+ if ($this->testCase === true && class_exists($this->name, false) && method_exists($this->name, $afterClassMethod)) {
+ call_user_func([$this->name, $afterClassMethod]);
+ }
+ }
+
+ $this->tearDown();
+
+ $result->endTestSuite($this);
+
+ return $result;
+ }
+
+ /**
+ * @param bool $runTestInSeparateProcess
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function setRunTestInSeparateProcess($runTestInSeparateProcess)
+ {
+ if (is_bool($runTestInSeparateProcess)) {
+ $this->runTestInSeparateProcess = $runTestInSeparateProcess;
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+ }
+
+ /**
+ * Runs a test.
+ *
+ * @deprecated
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ public function runTest(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result)
+ {
+ $test->run($result);
+ }
+
+ /**
+ * Sets the name of the suite.
+ *
+ * @param string
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Returns the test at the given index.
+ *
+ * @param int|false
+ *
+ * @return PHPUnit_Framework_Test
+ */
+ public function testAt($index)
+ {
+ if (isset($this->tests[$index])) {
+ return $this->tests[$index];
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the tests as an enumeration.
+ *
+ * @return array
+ */
+ public function tests()
+ {
+ return $this->tests;
+ }
+
+ /**
+ * Set tests of the test suite
+ *
+ * @param array $tests
+ */
+ public function setTests(array $tests)
+ {
+ $this->tests = $tests;
+ }
+
+ /**
+ * Mark the test suite as skipped.
+ *
+ * @param string $message
+ *
+ * @throws PHPUnit_Framework_SkippedTestSuiteError
+ */
+ public function markTestSuiteSkipped($message = '')
+ {
+ throw new PHPUnit_Framework_SkippedTestSuiteError($message);
+ }
+
+ /**
+ * @param ReflectionClass $class
+ * @param ReflectionMethod $method
+ */
+ protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method)
+ {
+ if (!$this->isTestMethod($method)) {
+ return;
+ }
+
+ $name = $method->getName();
+
+ if (!$method->isPublic()) {
+ $this->addTest(
+ self::warning(
+ sprintf(
+ 'Test method "%s" in test class "%s" is not public.',
+ $name,
+ $class->getName()
+ )
+ )
+ );
+
+ return;
+ }
+
+ $test = self::createTest($class, $name);
+
+ if ($test instanceof PHPUnit_Framework_TestCase ||
+ $test instanceof PHPUnit_Framework_TestSuite_DataProvider) {
+ $test->setDependencies(
+ PHPUnit_Util_Test::getDependencies($class->getName(), $name)
+ );
+ }
+
+ $this->addTest(
+ $test,
+ PHPUnit_Util_Test::getGroups($class->getName(), $name)
+ );
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ public static function isTestMethod(ReflectionMethod $method)
+ {
+ if (strpos($method->name, 'test') === 0) {
+ return true;
+ }
+
+ // @scenario on TestCase::testMethod()
+ // @test on TestCase::testMethod()
+ $docComment = $method->getDocComment();
+
+ return strpos($docComment, '@test') !== false ||
+ strpos($docComment, '@scenario') !== false;
+ }
+
+ /**
+ * @param string $message
+ *
+ * @return PHPUnit_Framework_WarningTestCase
+ */
+ protected static function warning($message)
+ {
+ return new PHPUnit_Framework_WarningTestCase($message);
+ }
+
+ /**
+ * @param string $class
+ * @param string $methodName
+ * @param string $message
+ *
+ * @return PHPUnit_Framework_SkippedTestCase
+ */
+ protected static function skipTest($class, $methodName, $message)
+ {
+ return new PHPUnit_Framework_SkippedTestCase($class, $methodName, $message);
+ }
+
+ /**
+ * @param string $class
+ * @param string $methodName
+ * @param string $message
+ *
+ * @return PHPUnit_Framework_IncompleteTestCase
+ */
+ protected static function incompleteTest($class, $methodName, $message)
+ {
+ return new PHPUnit_Framework_IncompleteTestCase($class, $methodName, $message);
+ }
+
+ /**
+ * @param bool $beStrictAboutChangesToGlobalState
+ */
+ public function setbeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState)
+ {
+ if (is_null($this->beStrictAboutChangesToGlobalState) && is_bool($beStrictAboutChangesToGlobalState)) {
+ $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState;
+ }
+ }
+
+ /**
+ * @param bool $backupGlobals
+ */
+ public function setBackupGlobals($backupGlobals)
+ {
+ if (is_null($this->backupGlobals) && is_bool($backupGlobals)) {
+ $this->backupGlobals = $backupGlobals;
+ }
+ }
+
+ /**
+ * @param bool $backupStaticAttributes
+ */
+ public function setBackupStaticAttributes($backupStaticAttributes)
+ {
+ if (is_null($this->backupStaticAttributes) &&
+ is_bool($backupStaticAttributes)) {
+ $this->backupStaticAttributes = $backupStaticAttributes;
+ }
+ }
+
+ /**
+ * Returns an iterator for this test suite.
+ *
+ * @return RecursiveIteratorIterator
+ */
+ public function getIterator()
+ {
+ $iterator = new PHPUnit_Util_TestSuiteIterator($this);
+
+ if ($this->iteratorFilter !== null) {
+ $iterator = $this->iteratorFilter->factory($iterator, $this);
+ }
+
+ return $iterator;
+ }
+
+ public function injectFilter(PHPUnit_Runner_Filter_Factory $filter)
+ {
+ $this->iteratorFilter = $filter;
+ foreach ($this as $test) {
+ if ($test instanceof self) {
+ $test->injectFilter($filter);
+ }
+ }
+ }
+
+ /**
+ * Template Method that is called before the tests
+ * of this test suite are run.
+ */
+ protected function setUp()
+ {
+ }
+
+ /**
+ * Template Method that is called after the tests
+ * of this test suite have finished running.
+ */
+ protected function tearDown()
+ {
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Framework_TestSuite_DataProvider extends PHPUnit_Framework_TestSuite
+{
+ /**
+ * Sets the dependencies of a TestCase.
+ *
+ * @param array $dependencies
+ */
+ public function setDependencies(array $dependencies)
+ {
+ foreach ($this->tests as $test) {
+ $test->setDependencies($dependencies);
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Extension to PHPUnit_Framework_AssertionFailedError to mark the special
+ * case of a test that unintentionally covers code.
+ */
+class PHPUnit_Framework_UnintentionallyCoveredCodeError extends PHPUnit_Framework_RiskyTestError
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Thrown when an there is a warning.
+ */
+class PHPUnit_Framework_Warning extends PHPUnit_Framework_Exception implements PHPUnit_Framework_SelfDescribing
+{
+ /**
+ * Wrapper for getMessage() which is declared as final.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->getMessage();
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A warning.
+ */
+class PHPUnit_Framework_WarningTestCase extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @var string
+ */
+ protected $message = '';
+
+ /**
+ * @var bool
+ */
+ protected $backupGlobals = false;
+
+ /**
+ * @var bool
+ */
+ protected $backupStaticAttributes = false;
+
+ /**
+ * @var bool
+ */
+ protected $runTestInSeparateProcess = false;
+
+ /**
+ * @var bool
+ */
+ protected $useErrorHandler = false;
+
+ /**
+ * @param string $message
+ */
+ public function __construct($message = '')
+ {
+ $this->message = $message;
+ parent::__construct('Warning');
+ }
+
+ /**
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function runTest()
+ {
+ throw new PHPUnit_Framework_Warning($this->message);
+ }
+
+ /**
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Returns a string representation of the test case.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return 'Warning';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Base class for all test runners.
+ */
+abstract class PHPUnit_Runner_BaseTestRunner
+{
+ const STATUS_PASSED = 0;
+ const STATUS_SKIPPED = 1;
+ const STATUS_INCOMPLETE = 2;
+ const STATUS_FAILURE = 3;
+ const STATUS_ERROR = 4;
+ const STATUS_RISKY = 5;
+ const STATUS_WARNING = 6;
+ const SUITE_METHODNAME = 'suite';
+
+ /**
+ * Returns the loader to be used.
+ *
+ * @return PHPUnit_Runner_TestSuiteLoader
+ */
+ public function getLoader()
+ {
+ return new PHPUnit_Runner_StandardTestSuiteLoader;
+ }
+
+ /**
+ * Returns the Test corresponding to the given suite.
+ * This is a template method, subclasses override
+ * the runFailed() and clearStatus() methods.
+ *
+ * @param string $suiteClassName
+ * @param string $suiteClassFile
+ * @param mixed $suffixes
+ *
+ * @return PHPUnit_Framework_Test
+ */
+ public function getTest($suiteClassName, $suiteClassFile = '', $suffixes = '')
+ {
+ if (is_dir($suiteClassName) &&
+ !is_file($suiteClassName . '.php') && empty($suiteClassFile)) {
+ $facade = new File_Iterator_Facade;
+ $files = $facade->getFilesAsArray(
+ $suiteClassName,
+ $suffixes
+ );
+
+ $suite = new PHPUnit_Framework_TestSuite($suiteClassName);
+ $suite->addTestFiles($files);
+
+ return $suite;
+ }
+
+ try {
+ $testClass = $this->loadSuiteClass(
+ $suiteClassName,
+ $suiteClassFile
+ );
+ } catch (PHPUnit_Framework_Exception $e) {
+ $this->runFailed($e->getMessage());
+
+ return;
+ }
+
+ try {
+ $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME);
+
+ if (!$suiteMethod->isStatic()) {
+ $this->runFailed(
+ 'suite() method must be static.'
+ );
+
+ return;
+ }
+
+ try {
+ $test = $suiteMethod->invoke(null, $testClass->getName());
+ } catch (ReflectionException $e) {
+ $this->runFailed(
+ sprintf(
+ "Failed to invoke suite() method.\n%s",
+ $e->getMessage()
+ )
+ );
+
+ return;
+ }
+ } catch (ReflectionException $e) {
+ try {
+ $test = new PHPUnit_Framework_TestSuite($testClass);
+ } catch (PHPUnit_Framework_Exception $e) {
+ $test = new PHPUnit_Framework_TestSuite;
+ $test->setName($suiteClassName);
+ }
+ }
+
+ $this->clearStatus();
+
+ return $test;
+ }
+
+ /**
+ * Returns the loaded ReflectionClass for a suite name.
+ *
+ * @param string $suiteClassName
+ * @param string $suiteClassFile
+ *
+ * @return ReflectionClass
+ */
+ protected function loadSuiteClass($suiteClassName, $suiteClassFile = '')
+ {
+ $loader = $this->getLoader();
+
+ return $loader->load($suiteClassName, $suiteClassFile);
+ }
+
+ /**
+ * Clears the status message.
+ */
+ protected function clearStatus()
+ {
+ }
+
+ /**
+ * Override to define how to handle a failed loading of
+ * a test suite.
+ *
+ * @param string $message
+ */
+ abstract protected function runFailed($message);
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Runner_Exception extends RuntimeException implements PHPUnit_Exception
+{
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Runner_Filter_Factory
+{
+ /**
+ * @var array
+ */
+ private $filters = [];
+
+ /**
+ * @param ReflectionClass $filter
+ * @param mixed $args
+ */
+ public function addFilter(ReflectionClass $filter, $args)
+ {
+ if (!$filter->isSubclassOf('RecursiveFilterIterator')) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'Class "%s" does not extend RecursiveFilterIterator',
+ $filter->name
+ )
+ );
+ }
+
+ $this->filters[] = [$filter, $args];
+ }
+
+ /**
+ * @return FilterIterator
+ */
+ public function factory(Iterator $iterator, PHPUnit_Framework_TestSuite $suite)
+ {
+ foreach ($this->filters as $filter) {
+ list($class, $args) = $filter;
+ $iterator = $class->newInstance($iterator, $args, $suite);
+ }
+
+ return $iterator;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+abstract class PHPUnit_Runner_Filter_GroupFilterIterator extends RecursiveFilterIterator
+{
+ /**
+ * @var array
+ */
+ protected $groupTests = [];
+
+ /**
+ * @param RecursiveIterator $iterator
+ * @param array $groups
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function __construct(RecursiveIterator $iterator, array $groups, PHPUnit_Framework_TestSuite $suite)
+ {
+ parent::__construct($iterator);
+
+ foreach ($suite->getGroupDetails() as $group => $tests) {
+ if (in_array($group, $groups)) {
+ $testHashes = array_map(
+ function ($test) {
+ return spl_object_hash($test);
+ },
+ $tests
+ );
+
+ $this->groupTests = array_merge($this->groupTests, $testHashes);
+ }
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ public function accept()
+ {
+ $test = $this->getInnerIterator()->current();
+
+ if ($test instanceof PHPUnit_Framework_TestSuite) {
+ return true;
+ }
+
+ return $this->doAccept(spl_object_hash($test));
+ }
+
+ abstract protected function doAccept($hash);
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Runner_Filter_Group_Exclude extends PHPUnit_Runner_Filter_GroupFilterIterator
+{
+ /**
+ * @param string $hash
+ *
+ * @return bool
+ */
+ protected function doAccept($hash)
+ {
+ return !in_array($hash, $this->groupTests);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Runner_Filter_Group_Include extends PHPUnit_Runner_Filter_GroupFilterIterator
+{
+ /**
+ * @param string $hash
+ *
+ * @return bool
+ */
+ protected function doAccept($hash)
+ {
+ return in_array($hash, $this->groupTests);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Runner_Filter_Test extends RecursiveFilterIterator
+{
+ /**
+ * @var string
+ */
+ protected $filter = null;
+
+ /**
+ * @var int
+ */
+ protected $filterMin;
+ /**
+ * @var int
+ */
+ protected $filterMax;
+
+ /**
+ * @param RecursiveIterator $iterator
+ * @param string $filter
+ */
+ public function __construct(RecursiveIterator $iterator, $filter)
+ {
+ parent::__construct($iterator);
+ $this->setFilter($filter);
+ }
+
+ /**
+ * @param string $filter
+ */
+ protected function setFilter($filter)
+ {
+ if (PHPUnit_Util_Regex::pregMatchSafe($filter, '') === false) {
+ // Handles:
+ // * testAssertEqualsSucceeds#4
+ // * testAssertEqualsSucceeds#4-8
+ if (preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) {
+ if (isset($matches[3]) && $matches[2] < $matches[3]) {
+ $filter = sprintf(
+ '%s.*with data set #(\d+)$',
+ $matches[1]
+ );
+
+ $this->filterMin = $matches[2];
+ $this->filterMax = $matches[3];
+ } else {
+ $filter = sprintf(
+ '%s.*with data set #%s$',
+ $matches[1],
+ $matches[2]
+ );
+ }
+ } // Handles:
+ // * testDetermineJsonError@JSON_ERROR_NONE
+ // * testDetermineJsonError@JSON.*
+ elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) {
+ $filter = sprintf(
+ '%s.*with data set "%s"$',
+ $matches[1],
+ $matches[2]
+ );
+ }
+
+ // Escape delimiters in regular expression. Do NOT use preg_quote,
+ // to keep magic characters.
+ $filter = sprintf('/%s/', str_replace(
+ '/',
+ '\\/',
+ $filter
+ ));
+ }
+
+ $this->filter = $filter;
+ }
+
+ /**
+ * @return bool
+ */
+ public function accept()
+ {
+ $test = $this->getInnerIterator()->current();
+
+ if ($test instanceof PHPUnit_Framework_TestSuite) {
+ return true;
+ }
+
+ if ($test instanceof PHPUnit_Framework_WarningTestCase) {
+ $name = $test->getMessage();
+ } else {
+ $tmp = PHPUnit_Util_Test::describe($test, false);
+
+ if ($tmp[0] != '') {
+ $name = implode('::', $tmp);
+ } else {
+ $name = $tmp[1];
+ }
+ }
+
+ $accepted = @preg_match($this->filter, $name, $matches);
+
+ if ($accepted && isset($this->filterMax)) {
+ $set = end($matches);
+ $accepted = $set >= $this->filterMin && $set <= $this->filterMax;
+ }
+
+ return $accepted;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * The standard test suite loader.
+ */
+class PHPUnit_Runner_StandardTestSuiteLoader implements PHPUnit_Runner_TestSuiteLoader
+{
+ /**
+ * @param string $suiteClassName
+ * @param string $suiteClassFile
+ *
+ * @return ReflectionClass
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function load($suiteClassName, $suiteClassFile = '')
+ {
+ $suiteClassName = str_replace('.php', '', $suiteClassName);
+
+ if (empty($suiteClassFile)) {
+ $suiteClassFile = PHPUnit_Util_Filesystem::classNameToFilename(
+ $suiteClassName
+ );
+ }
+
+ if (!class_exists($suiteClassName, false)) {
+ $loadedClasses = get_declared_classes();
+
+ $filename = PHPUnit_Util_Fileloader::checkAndLoad($suiteClassFile);
+
+ $loadedClasses = array_values(
+ array_diff(get_declared_classes(), $loadedClasses)
+ );
+ }
+
+ if (!class_exists($suiteClassName, false) && !empty($loadedClasses)) {
+ $offset = 0 - strlen($suiteClassName);
+
+ foreach ($loadedClasses as $loadedClass) {
+ $class = new ReflectionClass($loadedClass);
+ if (substr($loadedClass, $offset) === $suiteClassName &&
+ $class->getFileName() == $filename) {
+ $suiteClassName = $loadedClass;
+ break;
+ }
+ }
+ }
+
+ if (!class_exists($suiteClassName, false) && !empty($loadedClasses)) {
+ $testCaseClass = 'PHPUnit_Framework_TestCase';
+
+ foreach ($loadedClasses as $loadedClass) {
+ $class = new ReflectionClass($loadedClass);
+ $classFile = $class->getFileName();
+
+ if ($class->isSubclassOf($testCaseClass) &&
+ !$class->isAbstract()) {
+ $suiteClassName = $loadedClass;
+ $testCaseClass = $loadedClass;
+
+ if ($classFile == realpath($suiteClassFile)) {
+ break;
+ }
+ }
+
+ if ($class->hasMethod('suite')) {
+ $method = $class->getMethod('suite');
+
+ if (!$method->isAbstract() &&
+ $method->isPublic() &&
+ $method->isStatic()) {
+ $suiteClassName = $loadedClass;
+
+ if ($classFile == realpath($suiteClassFile)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (class_exists($suiteClassName, false)) {
+ $class = new ReflectionClass($suiteClassName);
+
+ if ($class->getFileName() == realpath($suiteClassFile)) {
+ return $class;
+ }
+ }
+
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ "Class '%s' could not be found in '%s'.",
+ $suiteClassName,
+ $suiteClassFile
+ )
+ );
+ }
+
+ /**
+ * @param ReflectionClass $aClass
+ *
+ * @return ReflectionClass
+ */
+ public function reload(ReflectionClass $aClass)
+ {
+ return $aClass;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * An interface to define how a test suite should be loaded.
+ */
+interface PHPUnit_Runner_TestSuiteLoader
+{
+ /**
+ * @param string $suiteClassName
+ * @param string $suiteClassFile
+ *
+ * @return ReflectionClass
+ */
+ public function load($suiteClassName, $suiteClassFile = '');
+
+ /**
+ * @param ReflectionClass $aClass
+ *
+ * @return ReflectionClass
+ */
+ public function reload(ReflectionClass $aClass);
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Version;
+
+/**
+ * This class defines the current version of PHPUnit.
+ */
+class PHPUnit_Runner_Version
+{
+ private static $pharVersion = "5.7.20";
+ private static $version;
+
+ /**
+ * Returns the current version of PHPUnit.
+ *
+ * @return string
+ */
+ public static function id()
+ {
+ if (self::$pharVersion !== null) {
+ return self::$pharVersion;
+ }
+
+ if (self::$version === null) {
+ $version = new Version('5.7.20', dirname(dirname(__DIR__)));
+ self::$version = $version->getVersion();
+ }
+
+ return self::$version;
+ }
+
+ /**
+ * @return string
+ */
+ public static function series()
+ {
+ if (strpos(self::id(), '-')) {
+ $version = explode('-', self::id())[0];
+ } else {
+ $version = self::id();
+ }
+
+ return implode('.', array_slice(explode('.', $version), 0, 2));
+ }
+
+ /**
+ * @return string
+ */
+ public static function getVersionString()
+ {
+ return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.';
+ }
+
+ /**
+ * @return string
+ */
+ public static function getReleaseChannel()
+ {
+ if (strpos(self::$pharVersion, '-') !== false) {
+ return '-nightly';
+ }
+
+ return '';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TestRunner for the Command Line Interface (CLI)
+ * PHP SAPI Module.
+ */
+class PHPUnit_TextUI_Command
+{
+ /**
+ * @var array
+ */
+ protected $arguments = [
+ 'listGroups' => false,
+ 'listSuites' => false,
+ 'loader' => null,
+ 'useDefaultConfiguration' => true,
+ 'loadedExtensions' => [],
+ 'notLoadedExtensions' => []
+ ];
+
+ /**
+ * @var array
+ */
+ protected $options = [];
+
+ /**
+ * @var array
+ */
+ protected $longOptions = [
+ 'atleast-version=' => null,
+ 'bootstrap=' => null,
+ 'colors==' => null,
+ 'columns=' => null,
+ 'configuration=' => null,
+ 'coverage-clover=' => null,
+ 'coverage-crap4j=' => null,
+ 'coverage-html=' => null,
+ 'coverage-php=' => null,
+ 'coverage-text==' => null,
+ 'coverage-xml=' => null,
+ 'debug' => null,
+ 'disallow-test-output' => null,
+ 'disallow-resource-usage' => null,
+ 'disallow-todo-tests' => null,
+ 'enforce-time-limit' => null,
+ 'exclude-group=' => null,
+ 'filter=' => null,
+ 'generate-configuration' => null,
+ 'group=' => null,
+ 'help' => null,
+ 'include-path=' => null,
+ 'list-groups' => null,
+ 'list-suites' => null,
+ 'loader=' => null,
+ 'log-json=' => null,
+ 'log-junit=' => null,
+ 'log-tap=' => null,
+ 'log-teamcity=' => null,
+ 'no-configuration' => null,
+ 'no-coverage' => null,
+ 'no-extensions' => null,
+ 'no-globals-backup' => null,
+ 'printer=' => null,
+ 'process-isolation' => null,
+ 'repeat=' => null,
+ 'report-useless-tests' => null,
+ 'reverse-list' => null,
+ 'static-backup' => null,
+ 'stderr' => null,
+ 'stop-on-error' => null,
+ 'stop-on-failure' => null,
+ 'stop-on-warning' => null,
+ 'stop-on-incomplete' => null,
+ 'stop-on-risky' => null,
+ 'stop-on-skipped' => null,
+ 'fail-on-warning' => null,
+ 'fail-on-risky' => null,
+ 'strict-coverage' => null,
+ 'disable-coverage-ignore' => null,
+ 'strict-global-state' => null,
+ 'tap' => null,
+ 'teamcity' => null,
+ 'testdox' => null,
+ 'testdox-group=' => null,
+ 'testdox-exclude-group=' => null,
+ 'testdox-html=' => null,
+ 'testdox-text=' => null,
+ 'testdox-xml=' => null,
+ 'test-suffix=' => null,
+ 'testsuite=' => null,
+ 'verbose' => null,
+ 'version' => null,
+ 'whitelist=' => null
+ ];
+
+ /**
+ * @var bool
+ */
+ private $versionStringPrinted = false;
+
+ /**
+ * @param bool $exit
+ */
+ public static function main($exit = true)
+ {
+ $command = new static;
+
+ return $command->run($_SERVER['argv'], $exit);
+ }
+
+ /**
+ * @param array $argv
+ * @param bool $exit
+ *
+ * @return int
+ */
+ public function run(array $argv, $exit = true)
+ {
+ $this->handleArguments($argv);
+
+ $runner = $this->createRunner();
+
+ if (is_object($this->arguments['test']) &&
+ $this->arguments['test'] instanceof PHPUnit_Framework_Test) {
+ $suite = $this->arguments['test'];
+ } else {
+ $suite = $runner->getTest(
+ $this->arguments['test'],
+ $this->arguments['testFile'],
+ $this->arguments['testSuffixes']
+ );
+ }
+
+ if ($this->arguments['listGroups']) {
+ $this->printVersionString();
+
+ print "Available test group(s):\n";
+
+ $groups = $suite->getGroups();
+ sort($groups);
+
+ foreach ($groups as $group) {
+ print " - $group\n";
+ }
+
+ if ($exit) {
+ exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
+ } else {
+ return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT;
+ }
+ }
+
+ if ($this->arguments['listSuites']) {
+ $this->printVersionString();
+
+ print "Available test suite(s):\n";
+
+ $configuration = PHPUnit_Util_Configuration::getInstance(
+ $this->arguments['configuration']
+ );
+
+ $suiteNames = $configuration->getTestSuiteNames();
+ foreach ($suiteNames as $suiteName) {
+ print " - $suiteName\n";
+ }
+
+ if ($exit) {
+ exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
+ } else {
+ return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT;
+ }
+ }
+
+ unset($this->arguments['test']);
+ unset($this->arguments['testFile']);
+
+ try {
+ $result = $runner->doRun($suite, $this->arguments, $exit);
+ } catch (PHPUnit_Framework_Exception $e) {
+ print $e->getMessage() . "\n";
+ }
+
+ $return = PHPUnit_TextUI_TestRunner::FAILURE_EXIT;
+
+ if (isset($result) && $result->wasSuccessful(false)) {
+ $return = PHPUnit_TextUI_TestRunner::SUCCESS_EXIT;
+ } elseif (!isset($result) || $result->errorCount() > 0) {
+ $return = PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT;
+ }
+
+ if ($exit) {
+ exit($return);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Create a TestRunner, override in subclasses.
+ *
+ * @return PHPUnit_TextUI_TestRunner
+ */
+ protected function createRunner()
+ {
+ return new PHPUnit_TextUI_TestRunner($this->arguments['loader']);
+ }
+
+ /**
+ * Handles the command-line arguments.
+ *
+ * A child class of PHPUnit_TextUI_Command can hook into the argument
+ * parsing by adding the switch(es) to the $longOptions array and point to a
+ * callback method that handles the switch(es) in the child class like this
+ *
+ * <code>
+ * <?php
+ * class MyCommand extends PHPUnit_TextUI_Command
+ * {
+ * public function __construct()
+ * {
+ * // my-switch won't accept a value, it's an on/off
+ * $this->longOptions['my-switch'] = 'myHandler';
+ * // my-secondswitch will accept a value - note the equals sign
+ * $this->longOptions['my-secondswitch='] = 'myOtherHandler';
+ * }
+ *
+ * // --my-switch -> myHandler()
+ * protected function myHandler()
+ * {
+ * }
+ *
+ * // --my-secondswitch foo -> myOtherHandler('foo')
+ * protected function myOtherHandler ($value)
+ * {
+ * }
+ *
+ * // You will also need this - the static keyword in the
+ * // PHPUnit_TextUI_Command will mean that it'll be
+ * // PHPUnit_TextUI_Command that gets instantiated,
+ * // not MyCommand
+ * public static function main($exit = true)
+ * {
+ * $command = new static;
+ *
+ * return $command->run($_SERVER['argv'], $exit);
+ * }
+ *
+ * }
+ * </code>
+ *
+ * @param array $argv
+ */
+ protected function handleArguments(array $argv)
+ {
+ if (defined('__PHPUNIT_PHAR__')) {
+ $this->longOptions['check-version'] = null;
+ $this->longOptions['selfupdate'] = null;
+ $this->longOptions['self-update'] = null;
+ $this->longOptions['selfupgrade'] = null;
+ $this->longOptions['self-upgrade'] = null;
+ }
+
+ try {
+ $this->options = PHPUnit_Util_Getopt::getopt(
+ $argv,
+ 'd:c:hv',
+ array_keys($this->longOptions)
+ );
+ } catch (PHPUnit_Framework_Exception $e) {
+ $this->showError($e->getMessage());
+ }
+
+ foreach ($this->options[0] as $option) {
+ switch ($option[0]) {
+ case '--colors':
+ $this->arguments['colors'] = $option[1] ?: PHPUnit_TextUI_ResultPrinter::COLOR_AUTO;
+ break;
+
+ case '--bootstrap':
+ $this->arguments['bootstrap'] = $option[1];
+ break;
+
+ case '--columns':
+ if (is_numeric($option[1])) {
+ $this->arguments['columns'] = (int) $option[1];
+ } elseif ($option[1] == 'max') {
+ $this->arguments['columns'] = 'max';
+ }
+ break;
+
+ case 'c':
+ case '--configuration':
+ $this->arguments['configuration'] = $option[1];
+ break;
+
+ case '--coverage-clover':
+ $this->arguments['coverageClover'] = $option[1];
+ break;
+
+ case '--coverage-crap4j':
+ $this->arguments['coverageCrap4J'] = $option[1];
+ break;
+
+ case '--coverage-html':
+ $this->arguments['coverageHtml'] = $option[1];
+ break;
+
+ case '--coverage-php':
+ $this->arguments['coveragePHP'] = $option[1];
+ break;
+
+ case '--coverage-text':
+ if ($option[1] === null) {
+ $option[1] = 'php://stdout';
+ }
+
+ $this->arguments['coverageText'] = $option[1];
+ $this->arguments['coverageTextShowUncoveredFiles'] = false;
+ $this->arguments['coverageTextShowOnlySummary'] = false;
+ break;
+
+ case '--coverage-xml':
+ $this->arguments['coverageXml'] = $option[1];
+ break;
+
+ case 'd':
+ $ini = explode('=', $option[1]);
+
+ if (isset($ini[0])) {
+ if (isset($ini[1])) {
+ ini_set($ini[0], $ini[1]);
+ } else {
+ ini_set($ini[0], true);
+ }
+ }
+ break;
+
+ case '--debug':
+ $this->arguments['debug'] = true;
+ break;
+
+ case 'h':
+ case '--help':
+ $this->showHelp();
+ exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
+ break;
+
+ case '--filter':
+ $this->arguments['filter'] = $option[1];
+ break;
+
+ case '--testsuite':
+ $this->arguments['testsuite'] = $option[1];
+ break;
+
+ case '--generate-configuration':
+ $this->printVersionString();
+
+ printf(
+ "Generating phpunit.xml in %s\n\n",
+ getcwd()
+ );
+
+ print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): ';
+ $bootstrapScript = trim(fgets(STDIN));
+
+ print 'Tests directory (relative to path shown above; default: tests): ';
+ $testsDirectory = trim(fgets(STDIN));
+
+ print 'Source directory (relative to path shown above; default: src): ';
+ $src = trim(fgets(STDIN));
+
+ if ($bootstrapScript == '') {
+ $bootstrapScript = 'vendor/autoload.php';
+ }
+
+ if ($testsDirectory == '') {
+ $testsDirectory = 'tests';
+ }
+
+ if ($src == '') {
+ $src = 'src';
+ }
+
+ $generator = new PHPUnit_Util_ConfigurationGenerator;
+
+ file_put_contents(
+ 'phpunit.xml',
+ $generator->generateDefaultConfiguration(
+ PHPUnit_Runner_Version::series(),
+ $bootstrapScript,
+ $testsDirectory,
+ $src
+ )
+ );
+
+ printf(
+ "\nGenerated phpunit.xml in %s\n",
+ getcwd()
+ );
+
+ exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
+ break;
+
+ case '--group':
+ $this->arguments['groups'] = explode(',', $option[1]);
+ break;
+
+ case '--exclude-group':
+ $this->arguments['excludeGroups'] = explode(
+ ',',
+ $option[1]
+ );
+ break;
+
+ case '--test-suffix':
+ $this->arguments['testSuffixes'] = explode(
+ ',',
+ $option[1]
+ );
+ break;
+
+ case '--include-path':
+ $includePath = $option[1];
+ break;
+
+ case '--list-groups':
+ $this->arguments['listGroups'] = true;
+ break;
+
+ case '--list-suites':
+ $this->arguments['listSuites'] = true;
+ break;
+
+ case '--printer':
+ $this->arguments['printer'] = $option[1];
+ break;
+
+ case '--loader':
+ $this->arguments['loader'] = $option[1];
+ break;
+
+ case '--log-json':
+ $this->arguments['jsonLogfile'] = $option[1];
+ break;
+
+ case '--log-junit':
+ $this->arguments['junitLogfile'] = $option[1];
+ break;
+
+ case '--log-tap':
+ $this->arguments['tapLogfile'] = $option[1];
+ break;
+
+ case '--log-teamcity':
+ $this->arguments['teamcityLogfile'] = $option[1];
+ break;
+
+ case '--process-isolation':
+ $this->arguments['processIsolation'] = true;
+ break;
+
+ case '--repeat':
+ $this->arguments['repeat'] = (int) $option[1];
+ break;
+
+ case '--stderr':
+ $this->arguments['stderr'] = true;
+ break;
+
+ case '--stop-on-error':
+ $this->arguments['stopOnError'] = true;
+ break;
+
+ case '--stop-on-failure':
+ $this->arguments['stopOnFailure'] = true;
+ break;
+
+ case '--stop-on-warning':
+ $this->arguments['stopOnWarning'] = true;
+ break;
+
+ case '--stop-on-incomplete':
+ $this->arguments['stopOnIncomplete'] = true;
+ break;
+
+ case '--stop-on-risky':
+ $this->arguments['stopOnRisky'] = true;
+ break;
+
+ case '--stop-on-skipped':
+ $this->arguments['stopOnSkipped'] = true;
+ break;
+
+ case '--fail-on-warning':
+ $this->arguments['failOnWarning'] = true;
+ break;
+
+ case '--fail-on-risky':
+ $this->arguments['failOnRisky'] = true;
+ break;
+
+ case '--tap':
+ $this->arguments['printer'] = 'PHPUnit_Util_Log_TAP';
+ break;
+
+ case '--teamcity':
+ $this->arguments['printer'] = 'PHPUnit_Util_Log_TeamCity';
+ break;
+
+ case '--testdox':
+ $this->arguments['printer'] = 'PHPUnit_Util_TestDox_ResultPrinter_Text';
+ break;
+
+ case '--testdox-group':
+ $this->arguments['testdoxGroups'] = explode(
+ ',',
+ $option[1]
+ );
+ break;
+
+ case '--testdox-exclude-group':
+ $this->arguments['testdoxExcludeGroups'] = explode(
+ ',',
+ $option[1]
+ );
+ break;
+
+ case '--testdox-html':
+ $this->arguments['testdoxHTMLFile'] = $option[1];
+ break;
+
+ case '--testdox-text':
+ $this->arguments['testdoxTextFile'] = $option[1];
+ break;
+
+ case '--testdox-xml':
+ $this->arguments['testdoxXMLFile'] = $option[1];
+ break;
+
+ case '--no-configuration':
+ $this->arguments['useDefaultConfiguration'] = false;
+ break;
+
+ case '--no-extensions':
+ $this->arguments['noExtensions'] = true;
+ break;
+
+ case '--no-coverage':
+ $this->arguments['noCoverage'] = true;
+ break;
+
+ case '--no-globals-backup':
+ $this->arguments['backupGlobals'] = false;
+ break;
+
+ case '--static-backup':
+ $this->arguments['backupStaticAttributes'] = true;
+ break;
+
+ case 'v':
+ case '--verbose':
+ $this->arguments['verbose'] = true;
+ break;
+
+ case '--atleast-version':
+ exit(version_compare(PHPUnit_Runner_Version::id(), $option[1], '>=')
+ ? PHPUnit_TextUI_TestRunner::SUCCESS_EXIT
+ : PHPUnit_TextUI_TestRunner::FAILURE_EXIT
+ );
+ break;
+
+ case '--version':
+ $this->printVersionString();
+ exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
+ break;
+
+ case '--report-useless-tests':
+ $this->arguments['reportUselessTests'] = true;
+ break;
+
+ case '--strict-coverage':
+ $this->arguments['strictCoverage'] = true;
+ break;
+
+ case '--disable-coverage-ignore':
+ $this->arguments['disableCodeCoverageIgnore'] = true;
+ break;
+
+ case '--strict-global-state':
+ $this->arguments['beStrictAboutChangesToGlobalState'] = true;
+ break;
+
+ case '--disallow-test-output':
+ $this->arguments['disallowTestOutput'] = true;
+ break;
+
+ case '--disallow-resource-usage':
+ $this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true;
+ break;
+
+ case '--enforce-time-limit':
+ $this->arguments['enforceTimeLimit'] = true;
+ break;
+
+ case '--disallow-todo-tests':
+ $this->arguments['disallowTodoAnnotatedTests'] = true;
+ break;
+
+ case '--reverse-list':
+ $this->arguments['reverseList'] = true;
+ break;
+
+ case '--check-version':
+ $this->handleVersionCheck();
+ break;
+
+ case '--selfupdate':
+ case '--self-update':
+ $this->handleSelfUpdate();
+ break;
+
+ case '--selfupgrade':
+ case '--self-upgrade':
+ $this->handleSelfUpdate(true);
+ break;
+
+ case '--whitelist':
+ $this->arguments['whitelist'] = $option[1];
+ break;
+
+ default:
+ $optionName = str_replace('--', '', $option[0]);
+
+ $handler = null;
+ if (isset($this->longOptions[$optionName])) {
+ $handler = $this->longOptions[$optionName];
+ } elseif (isset($this->longOptions[$optionName . '='])) {
+ $handler = $this->longOptions[$optionName . '='];
+ }
+
+ if (isset($handler) && is_callable([$this, $handler])) {
+ $this->$handler($option[1]);
+ }
+ }
+ }
+
+ $this->handleCustomTestSuite();
+
+ if (!isset($this->arguments['test'])) {
+ if (isset($this->options[1][0])) {
+ $this->arguments['test'] = $this->options[1][0];
+ }
+
+ if (isset($this->options[1][1])) {
+ $this->arguments['testFile'] = realpath($this->options[1][1]);
+ } else {
+ $this->arguments['testFile'] = '';
+ }
+
+ if (isset($this->arguments['test']) &&
+ is_file($this->arguments['test']) &&
+ substr($this->arguments['test'], -5, 5) != '.phpt') {
+ $this->arguments['testFile'] = realpath($this->arguments['test']);
+ $this->arguments['test'] = substr($this->arguments['test'], 0, strrpos($this->arguments['test'], '.'));
+ }
+ }
+
+ if (!isset($this->arguments['testSuffixes'])) {
+ $this->arguments['testSuffixes'] = ['Test.php', '.phpt'];
+ }
+
+ if (isset($includePath)) {
+ ini_set(
+ 'include_path',
+ $includePath . PATH_SEPARATOR . ini_get('include_path')
+ );
+ }
+
+ if ($this->arguments['loader'] !== null) {
+ $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
+ }
+
+ if (isset($this->arguments['configuration']) &&
+ is_dir($this->arguments['configuration'])) {
+ $configurationFile = $this->arguments['configuration'] . '/phpunit.xml';
+
+ if (file_exists($configurationFile)) {
+ $this->arguments['configuration'] = realpath(
+ $configurationFile
+ );
+ } elseif (file_exists($configurationFile . '.dist')) {
+ $this->arguments['configuration'] = realpath(
+ $configurationFile . '.dist'
+ );
+ }
+ } elseif (!isset($this->arguments['configuration']) &&
+ $this->arguments['useDefaultConfiguration']) {
+ if (file_exists('phpunit.xml')) {
+ $this->arguments['configuration'] = realpath('phpunit.xml');
+ } elseif (file_exists('phpunit.xml.dist')) {
+ $this->arguments['configuration'] = realpath(
+ 'phpunit.xml.dist'
+ );
+ }
+ }
+
+ if (isset($this->arguments['configuration'])) {
+ try {
+ $configuration = PHPUnit_Util_Configuration::getInstance(
+ $this->arguments['configuration']
+ );
+ } catch (Throwable $e) {
+ print $e->getMessage() . "\n";
+ exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
+ } catch (Exception $e) {
+ print $e->getMessage() . "\n";
+ exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
+ }
+
+ $phpunitConfiguration = $configuration->getPHPUnitConfiguration();
+
+ $configuration->handlePHPConfiguration();
+
+ /*
+ * Issue #1216
+ */
+ if (isset($this->arguments['bootstrap'])) {
+ $this->handleBootstrap($this->arguments['bootstrap']);
+ } elseif (isset($phpunitConfiguration['bootstrap'])) {
+ $this->handleBootstrap($phpunitConfiguration['bootstrap']);
+ }
+
+ /*
+ * Issue #657
+ */
+ if (isset($phpunitConfiguration['stderr']) && ! isset($this->arguments['stderr'])) {
+ $this->arguments['stderr'] = $phpunitConfiguration['stderr'];
+ }
+
+ if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && extension_loaded('phar')) {
+ $this->handleExtensions($phpunitConfiguration['extensionsDirectory']);
+ }
+
+ if (isset($phpunitConfiguration['columns']) && ! isset($this->arguments['columns'])) {
+ $this->arguments['columns'] = $phpunitConfiguration['columns'];
+ }
+
+ if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) {
+ if (isset($phpunitConfiguration['printerFile'])) {
+ $file = $phpunitConfiguration['printerFile'];
+ } else {
+ $file = '';
+ }
+
+ $this->arguments['printer'] = $this->handlePrinter(
+ $phpunitConfiguration['printerClass'],
+ $file
+ );
+ }
+
+ if (isset($phpunitConfiguration['testSuiteLoaderClass'])) {
+ if (isset($phpunitConfiguration['testSuiteLoaderFile'])) {
+ $file = $phpunitConfiguration['testSuiteLoaderFile'];
+ } else {
+ $file = '';
+ }
+
+ $this->arguments['loader'] = $this->handleLoader(
+ $phpunitConfiguration['testSuiteLoaderClass'],
+ $file
+ );
+ }
+
+ if (!isset($this->arguments['test'])) {
+ $testSuite = $configuration->getTestSuiteConfiguration(isset($this->arguments['testsuite']) ? $this->arguments['testsuite'] : null);
+
+ if ($testSuite !== null) {
+ $this->arguments['test'] = $testSuite;
+ }
+ }
+ } elseif (isset($this->arguments['bootstrap'])) {
+ $this->handleBootstrap($this->arguments['bootstrap']);
+ }
+
+ if (isset($this->arguments['printer']) &&
+ is_string($this->arguments['printer'])) {
+ $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']);
+ }
+
+ if (isset($this->arguments['test']) && is_string($this->arguments['test']) && substr($this->arguments['test'], -5, 5) == '.phpt') {
+ $test = new PHPUnit_Extensions_PhptTestCase($this->arguments['test']);
+
+ $this->arguments['test'] = new PHPUnit_Framework_TestSuite;
+ $this->arguments['test']->addTest($test);
+ }
+
+ if (!isset($this->arguments['test']) ||
+ (isset($this->arguments['testDatabaseLogRevision']) && !isset($this->arguments['testDatabaseDSN']))) {
+ $this->showHelp();
+ exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
+ }
+ }
+
+ /**
+ * Handles the loading of the PHPUnit_Runner_TestSuiteLoader implementation.
+ *
+ * @param string $loaderClass
+ * @param string $loaderFile
+ *
+ * @return PHPUnit_Runner_TestSuiteLoader
+ */
+ protected function handleLoader($loaderClass, $loaderFile = '')
+ {
+ if (!class_exists($loaderClass, false)) {
+ if ($loaderFile == '') {
+ $loaderFile = PHPUnit_Util_Filesystem::classNameToFilename(
+ $loaderClass
+ );
+ }
+
+ $loaderFile = stream_resolve_include_path($loaderFile);
+
+ if ($loaderFile) {
+ require $loaderFile;
+ }
+ }
+
+ if (class_exists($loaderClass, false)) {
+ $class = new ReflectionClass($loaderClass);
+
+ if ($class->implementsInterface('PHPUnit_Runner_TestSuiteLoader') &&
+ $class->isInstantiable()) {
+ return $class->newInstance();
+ }
+ }
+
+ if ($loaderClass == 'PHPUnit_Runner_StandardTestSuiteLoader') {
+ return;
+ }
+
+ $this->showError(
+ sprintf(
+ 'Could not use "%s" as loader.',
+ $loaderClass
+ )
+ );
+ }
+
+ /**
+ * Handles the loading of the PHPUnit_Util_Printer implementation.
+ *
+ * @param string $printerClass
+ * @param string $printerFile
+ *
+ * @return PHPUnit_Util_Printer|string
+ */
+ protected function handlePrinter($printerClass, $printerFile = '')
+ {
+ if (!class_exists($printerClass, false)) {
+ if ($printerFile == '') {
+ $printerFile = PHPUnit_Util_Filesystem::classNameToFilename(
+ $printerClass
+ );
+ }
+
+ $printerFile = stream_resolve_include_path($printerFile);
+
+ if ($printerFile) {
+ require $printerFile;
+ }
+ }
+
+ if (class_exists($printerClass)) {
+ $class = new ReflectionClass($printerClass);
+
+ if ($class->implementsInterface('PHPUnit_Framework_TestListener') &&
+ $class->isSubclassOf('PHPUnit_Util_Printer') &&
+ $class->isInstantiable()) {
+ if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) {
+ return $printerClass;
+ }
+
+ $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null;
+
+ return $class->newInstance($outputStream);
+ }
+ }
+
+ $this->showError(
+ sprintf(
+ 'Could not use "%s" as printer.',
+ $printerClass
+ )
+ );
+ }
+
+ /**
+ * Loads a bootstrap file.
+ *
+ * @param string $filename
+ */
+ protected function handleBootstrap($filename)
+ {
+ try {
+ PHPUnit_Util_Fileloader::checkAndLoad($filename);
+ } catch (PHPUnit_Framework_Exception $e) {
+ $this->showError($e->getMessage());
+ }
+ }
+
+ protected function handleSelfUpdate($upgrade = false)
+ {
+ $this->printVersionString();
+
+ if ($upgrade) {
+ print "Warning: Deprecated --self-upgrade used\n\n";
+ } else {
+ print "Warning: Deprecated --self-update used\n\n";
+ }
+
+ $localFilename = realpath($_SERVER['argv'][0]);
+
+ if (!is_writable($localFilename)) {
+ print 'No write permission to update ' . $localFilename . "\n";
+ exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
+ }
+
+ if (!extension_loaded('openssl')) {
+ print "The OpenSSL extension is not loaded.\n";
+ exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
+ }
+
+ if (!$upgrade) {
+ $remoteFilename = sprintf(
+ 'https://phar.phpunit.de/phpunit-%s.phar',
+ file_get_contents(
+ sprintf(
+ 'https://phar.phpunit.de/latest-version-of/phpunit-%s',
+ PHPUnit_Runner_Version::series()
+ )
+ )
+ );
+ } else {
+ $remoteFilename = sprintf(
+ 'https://phar.phpunit.de/phpunit%s.phar',
+ PHPUnit_Runner_Version::getReleaseChannel()
+ );
+ }
+
+ $tempFilename = tempnam(sys_get_temp_dir(), 'phpunit') . '.phar';
+
+ // Workaround for https://bugs.php.net/bug.php?id=65538
+ $caFile = dirname($tempFilename) . '/ca.pem';
+ copy(__PHPUNIT_PHAR_ROOT__ . '/ca.pem', $caFile);
+
+ print 'Updating the PHPUnit PHAR ... ';
+
+ $options = [
+ 'ssl' => [
+ 'allow_self_signed' => false,
+ 'cafile' => $caFile,
+ 'verify_peer' => true
+ ]
+ ];
+
+ file_put_contents(
+ $tempFilename,
+ file_get_contents(
+ $remoteFilename,
+ false,
+ stream_context_create($options)
+ )
+ );
+
+ chmod($tempFilename, 0777 & ~umask());
+
+ try {
+ $phar = new Phar($tempFilename);
+ unset($phar);
+ rename($tempFilename, $localFilename);
+ unlink($caFile);
+ } catch (Throwable $_e) {
+ $e = $_e;
+ } catch (Exception $_e) {
+ $e = $_e;
+ }
+
+ if (isset($e)) {
+ unlink($caFile);
+ unlink($tempFilename);
+ print " done\n\n" . $e->getMessage() . "\n";
+ exit(2);
+ }
+
+ print " done\n";
+ exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
+ }
+
+ protected function handleVersionCheck()
+ {
+ $this->printVersionString();
+
+ $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit');
+ $isOutdated = version_compare($latestVersion, PHPUnit_Runner_Version::id(), '>');
+
+ if ($isOutdated) {
+ print "You are not using the latest version of PHPUnit.\n";
+ print 'Use "phpunit --self-upgrade" to install PHPUnit ' . $latestVersion . "\n";
+ } else {
+ print "You are using the latest version of PHPUnit.\n";
+ }
+
+ exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
+ }
+
+ /**
+ * Show the help message.
+ */
+ protected function showHelp()
+ {
+ $this->printVersionString();
+
+ print <<<EOT
+Usage: phpunit [options] UnitTest [UnitTest.php]
+ phpunit [options] <directory>
+
+Code Coverage Options:
+
+ --coverage-clover <file> Generate code coverage report in Clover XML format.
+ --coverage-crap4j <file> Generate code coverage report in Crap4J XML format.
+ --coverage-html <dir> Generate code coverage report in HTML format.
+ --coverage-php <file> Export PHP_CodeCoverage object to file.
+ --coverage-text=<file> Generate code coverage report in text format.
+ Default: Standard output.
+ --coverage-xml <dir> Generate code coverage report in PHPUnit XML format.
+ --whitelist <dir> Whitelist <dir> for code coverage analysis.
+ --disable-coverage-ignore Disable annotations for ignoring code coverage.
+
+Logging Options:
+
+ --log-junit <file> Log test execution in JUnit XML format to file.
+ --log-teamcity <file> Log test execution in TeamCity format to file.
+ --testdox-html <file> Write agile documentation in HTML format to file.
+ --testdox-text <file> Write agile documentation in Text format to file.
+ --testdox-xml <file> Write agile documentation in XML format to file.
+ --reverse-list Print defects in reverse order
+
+Test Selection Options:
+
+ --filter <pattern> Filter which tests to run.
+ --testsuite <name> Filter which testsuite to run.
+ --group ... Only runs tests from the specified group(s).
+ --exclude-group ... Exclude tests from the specified group(s).
+ --list-groups List available test groups.
+ --list-suites List available test suites.
+ --test-suffix ... Only search for test in files with specified
+ suffix(es). Default: Test.php,.phpt
+
+Test Execution Options:
+
+ --report-useless-tests Be strict about tests that do not test anything.
+ --strict-coverage Be strict about @covers annotation usage.
+ --strict-global-state Be strict about changes to global state
+ --disallow-test-output Be strict about output during tests.
+ --disallow-resource-usage Be strict about resource usage during small tests.
+ --enforce-time-limit Enforce time limit based on test size.
+ --disallow-todo-tests Disallow @todo-annotated tests.
+
+ --process-isolation Run each test in a separate PHP process.
+ --no-globals-backup Do not backup and restore \$GLOBALS for each test.
+ --static-backup Backup and restore static attributes for each test.
+
+ --colors=<flag> Use colors in output ("never", "auto" or "always").
+ --columns <n> Number of columns to use for progress output.
+ --columns max Use maximum number of columns for progress output.
+ --stderr Write to STDERR instead of STDOUT.
+ --stop-on-error Stop execution upon first error.
+ --stop-on-failure Stop execution upon first error or failure.
+ --stop-on-warning Stop execution upon first warning.
+ --stop-on-risky Stop execution upon first risky test.
+ --stop-on-skipped Stop execution upon first skipped test.
+ --stop-on-incomplete Stop execution upon first incomplete test.
+ --fail-on-warning Treat tests with warnings as failures.
+ --fail-on-risky Treat risky tests as failures.
+ -v|--verbose Output more verbose information.
+ --debug Display debugging information during test execution.
+
+ --loader <loader> TestSuiteLoader implementation to use.
+ --repeat <times> Runs the test(s) repeatedly.
+ --teamcity Report test execution progress in TeamCity format.
+ --testdox Report test execution progress in TestDox format.
+ --testdox-group Only include tests from the specified group(s).
+ --testdox-exclude-group Exclude tests from the specified group(s).
+ --printer <printer> TestListener implementation to use.
+
+Configuration Options:
+
+ --bootstrap <file> A "bootstrap" PHP file that is run before the tests.
+ -c|--configuration <file> Read configuration from XML file.
+ --no-configuration Ignore default configuration file (phpunit.xml).
+ --no-coverage Ignore code coverage configuration.
+ --no-extensions Do not load PHPUnit extensions.
+ --include-path <path(s)> Prepend PHP's include_path with given path(s).
+ -d key[=value] Sets a php.ini value.
+ --generate-configuration Generate configuration file with suggested settings.
+
+Miscellaneous Options:
+
+ -h|--help Prints this usage information.
+ --version Prints the version and exits.
+ --atleast-version <min> Checks that version is greater than min and exits.
+
+EOT;
+
+ if (defined('__PHPUNIT_PHAR__')) {
+ print "\n --check-version Check whether PHPUnit is the latest version.";
+ }
+ }
+
+ /**
+ * Custom callback for test suite discovery.
+ */
+ protected function handleCustomTestSuite()
+ {
+ }
+
+ private function printVersionString()
+ {
+ if ($this->versionStringPrinted) {
+ return;
+ }
+
+ print PHPUnit_Runner_Version::getVersionString() . "\n\n";
+
+ $this->versionStringPrinted = true;
+ }
+
+ /**
+ * @param string $message
+ */
+ private function showError($message)
+ {
+ $this->printVersionString();
+
+ print $message . "\n";
+
+ exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
+ }
+
+ /**
+ * @param string $directory
+ */
+ private function handleExtensions($directory)
+ {
+ $facade = new File_Iterator_Facade;
+
+ foreach ($facade->getFilesAsArray($directory, '.phar') as $file) {
+ require $file;
+
+ $this->arguments['loadedExtensions'][] = $file;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Environment\Console;
+
+/**
+ * Prints the result of a TextUI TestRunner run.
+ */
+class PHPUnit_TextUI_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
+{
+ const EVENT_TEST_START = 0;
+ const EVENT_TEST_END = 1;
+ const EVENT_TESTSUITE_START = 2;
+ const EVENT_TESTSUITE_END = 3;
+
+ const COLOR_NEVER = 'never';
+ const COLOR_AUTO = 'auto';
+ const COLOR_ALWAYS = 'always';
+ const COLOR_DEFAULT = self::COLOR_NEVER;
+
+ /**
+ * @var array
+ */
+ private static $ansiCodes = [
+ 'bold' => 1,
+ 'fg-black' => 30,
+ 'fg-red' => 31,
+ 'fg-green' => 32,
+ 'fg-yellow' => 33,
+ 'fg-blue' => 34,
+ 'fg-magenta' => 35,
+ 'fg-cyan' => 36,
+ 'fg-white' => 37,
+ 'bg-black' => 40,
+ 'bg-red' => 41,
+ 'bg-green' => 42,
+ 'bg-yellow' => 43,
+ 'bg-blue' => 44,
+ 'bg-magenta' => 45,
+ 'bg-cyan' => 46,
+ 'bg-white' => 47
+ ];
+
+ /**
+ * @var int
+ */
+ protected $column = 0;
+
+ /**
+ * @var int
+ */
+ protected $maxColumn;
+
+ /**
+ * @var bool
+ */
+ protected $lastTestFailed = false;
+
+ /**
+ * @var int
+ */
+ protected $numAssertions = 0;
+
+ /**
+ * @var int
+ */
+ protected $numTests = -1;
+
+ /**
+ * @var int
+ */
+ protected $numTestsRun = 0;
+
+ /**
+ * @var int
+ */
+ protected $numTestsWidth;
+
+ /**
+ * @var bool
+ */
+ protected $colors = false;
+
+ /**
+ * @var bool
+ */
+ protected $debug = false;
+
+ /**
+ * @var bool
+ */
+ protected $verbose = false;
+
+ /**
+ * @var int
+ */
+ private $numberOfColumns;
+
+ /**
+ * @var bool
+ */
+ private $reverse = false;
+
+ /**
+ * @var bool
+ */
+ private $defectListPrinted = false;
+
+ /**
+ * Constructor.
+ *
+ * @param mixed $out
+ * @param bool $verbose
+ * @param string $colors
+ * @param bool $debug
+ * @param int|string $numberOfColumns
+ * @param bool $reverse
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80, $reverse = false)
+ {
+ parent::__construct($out);
+
+ if (!is_bool($verbose)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean');
+ }
+
+ $availableColors = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS];
+
+ if (!in_array($colors, $availableColors)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(
+ 3,
+ vsprintf('value from "%s", "%s" or "%s"', $availableColors)
+ );
+ }
+
+ if (!is_bool($debug)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean');
+ }
+
+ if (!is_int($numberOfColumns) && $numberOfColumns != 'max') {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'integer or "max"');
+ }
+
+ if (!is_bool($reverse)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(6, 'boolean');
+ }
+
+ $console = new Console;
+ $maxNumberOfColumns = $console->getNumberOfColumns();
+
+ if ($numberOfColumns == 'max' || $numberOfColumns > $maxNumberOfColumns) {
+ $numberOfColumns = $maxNumberOfColumns;
+ }
+
+ $this->numberOfColumns = $numberOfColumns;
+ $this->verbose = $verbose;
+ $this->debug = $debug;
+ $this->reverse = $reverse;
+
+ if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) {
+ $this->colors = true;
+ } else {
+ $this->colors = (self::COLOR_ALWAYS === $colors);
+ }
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ public function printResult(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printHeader();
+ $this->printErrors($result);
+ $this->printWarnings($result);
+ $this->printFailures($result);
+
+ if ($this->verbose) {
+ $this->printRisky($result);
+ $this->printIncompletes($result);
+ $this->printSkipped($result);
+ }
+
+ $this->printFooter($result);
+ }
+
+ /**
+ * @param array $defects
+ * @param string $type
+ */
+ protected function printDefects(array $defects, $type)
+ {
+ $count = count($defects);
+
+ if ($count == 0) {
+ return;
+ }
+
+ if ($this->defectListPrinted) {
+ $this->write("\n--\n\n");
+ }
+
+ $this->write(
+ sprintf(
+ "There %s %d %s%s:\n",
+ ($count == 1) ? 'was' : 'were',
+ $count,
+ $type,
+ ($count == 1) ? '' : 's'
+ )
+ );
+
+ $i = 1;
+
+ if ($this->reverse) {
+ $defects = array_reverse($defects);
+ }
+
+ foreach ($defects as $defect) {
+ $this->printDefect($defect, $i++);
+ }
+
+ $this->defectListPrinted = true;
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestFailure $defect
+ * @param int $count
+ */
+ protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count)
+ {
+ $this->printDefectHeader($defect, $count);
+ $this->printDefectTrace($defect);
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestFailure $defect
+ * @param int $count
+ */
+ protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count)
+ {
+ $this->write(
+ sprintf(
+ "\n%d) %s\n",
+ $count,
+ $defect->getTestName()
+ )
+ );
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestFailure $defect
+ */
+ protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect)
+ {
+ $e = $defect->thrownException();
+ $this->write((string) $e);
+
+ while ($e = $e->getPrevious()) {
+ $this->write("\nCaused by\n" . $e);
+ }
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ protected function printErrors(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printDefects($result->errors(), 'error');
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ protected function printFailures(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printDefects($result->failures(), 'failure');
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ protected function printWarnings(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printDefects($result->warnings(), 'warning');
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ protected function printIncompletes(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printDefects($result->notImplemented(), 'incomplete test');
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ protected function printRisky(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printDefects($result->risky(), 'risky test');
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ protected function printSkipped(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printDefects($result->skipped(), 'skipped test');
+ }
+
+ protected function printHeader()
+ {
+ $this->write("\n\n" . PHP_Timer::resourceUsage() . "\n\n");
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ protected function printFooter(PHPUnit_Framework_TestResult $result)
+ {
+ if (count($result) === 0) {
+ $this->writeWithColor(
+ 'fg-black, bg-yellow',
+ 'No tests executed!'
+ );
+
+ return;
+ }
+
+ if ($result->wasSuccessful() &&
+ $result->allHarmless() &&
+ $result->allCompletelyImplemented() &&
+ $result->noneSkipped()) {
+ $this->writeWithColor(
+ 'fg-black, bg-green',
+ sprintf(
+ 'OK (%d test%s, %d assertion%s)',
+ count($result),
+ (count($result) == 1) ? '' : 's',
+ $this->numAssertions,
+ ($this->numAssertions == 1) ? '' : 's'
+ )
+ );
+ } else {
+ if ($result->wasSuccessful()) {
+ $color = 'fg-black, bg-yellow';
+
+ if ($this->verbose) {
+ $this->write("\n");
+ }
+
+ $this->writeWithColor(
+ $color,
+ 'OK, but incomplete, skipped, or risky tests!'
+ );
+ } else {
+ $this->write("\n");
+
+ if ($result->errorCount()) {
+ $color = 'fg-white, bg-red';
+
+ $this->writeWithColor(
+ $color,
+ 'ERRORS!'
+ );
+ } elseif ($result->failureCount()) {
+ $color = 'fg-white, bg-red';
+
+ $this->writeWithColor(
+ $color,
+ 'FAILURES!'
+ );
+ } elseif ($result->warningCount()) {
+ $color = 'fg-black, bg-yellow';
+
+ $this->writeWithColor(
+ $color,
+ 'WARNINGS!'
+ );
+ }
+ }
+
+ $this->writeCountString(count($result), 'Tests', $color, true);
+ $this->writeCountString($this->numAssertions, 'Assertions', $color, true);
+ $this->writeCountString($result->errorCount(), 'Errors', $color);
+ $this->writeCountString($result->failureCount(), 'Failures', $color);
+ $this->writeCountString($result->warningCount(), 'Warnings', $color);
+ $this->writeCountString($result->skippedCount(), 'Skipped', $color);
+ $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color);
+ $this->writeCountString($result->riskyCount(), 'Risky', $color);
+ $this->writeWithColor($color, '.', true);
+ }
+ }
+
+ public function printWaitPrompt()
+ {
+ $this->write("\n<RETURN> to continue\n");
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeProgressWithColor('fg-red, bold', 'E');
+ $this->lastTestFailed = true;
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ $this->writeProgressWithColor('bg-red, fg-white', 'F');
+ $this->lastTestFailed = true;
+ }
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ $this->writeProgressWithColor('fg-yellow, bold', 'W');
+ $this->lastTestFailed = true;
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeProgressWithColor('fg-yellow, bold', 'I');
+ $this->lastTestFailed = true;
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeProgressWithColor('fg-yellow, bold', 'R');
+ $this->lastTestFailed = true;
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeProgressWithColor('fg-cyan, bold', 'S');
+ $this->lastTestFailed = true;
+ }
+
+ /**
+ * A testsuite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ if ($this->numTests == -1) {
+ $this->numTests = count($suite);
+ $this->numTestsWidth = strlen((string) $this->numTests);
+ $this->maxColumn = $this->numberOfColumns - strlen(' / (XXX%)') - (2 * $this->numTestsWidth);
+ }
+ }
+
+ /**
+ * A testsuite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ if ($this->debug) {
+ $this->write(
+ sprintf(
+ "\nStarting test '%s'.\n",
+ PHPUnit_Util_Test::describe($test)
+ )
+ );
+ }
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ if (!$this->lastTestFailed) {
+ $this->writeProgress('.');
+ }
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $this->numAssertions += $test->getNumAssertions();
+ } elseif ($test instanceof PHPUnit_Extensions_PhptTestCase) {
+ $this->numAssertions++;
+ }
+
+ $this->lastTestFailed = false;
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ if (!$test->hasExpectationOnOutput()) {
+ $this->write($test->getActualOutput());
+ }
+ }
+ }
+
+ /**
+ * @param string $progress
+ */
+ protected function writeProgress($progress)
+ {
+ $this->write($progress);
+ $this->column++;
+ $this->numTestsRun++;
+
+ if ($this->column == $this->maxColumn
+ || $this->numTestsRun == $this->numTests
+ ) {
+ if ($this->numTestsRun == $this->numTests) {
+ $this->write(str_repeat(' ', $this->maxColumn - $this->column));
+ }
+
+ $this->write(
+ sprintf(
+ ' %' . $this->numTestsWidth . 'd / %' .
+ $this->numTestsWidth . 'd (%3s%%)',
+ $this->numTestsRun,
+ $this->numTests,
+ floor(($this->numTestsRun / $this->numTests) * 100)
+ )
+ );
+
+ if ($this->column == $this->maxColumn) {
+ $this->writeNewLine();
+ }
+ }
+ }
+
+ protected function writeNewLine()
+ {
+ $this->column = 0;
+ $this->write("\n");
+ }
+
+ /**
+ * Formats a buffer with a specified ANSI color sequence if colors are
+ * enabled.
+ *
+ * @param string $color
+ * @param string $buffer
+ *
+ * @return string
+ */
+ protected function formatWithColor($color, $buffer)
+ {
+ if (!$this->colors) {
+ return $buffer;
+ }
+
+ $codes = array_map('trim', explode(',', $color));
+ $lines = explode("\n", $buffer);
+ $padding = max(array_map('strlen', $lines));
+ $styles = [];
+
+ foreach ($codes as $code) {
+ $styles[] = self::$ansiCodes[$code];
+ }
+
+ $style = sprintf("\x1b[%sm", implode(';', $styles));
+
+ $styledLines = [];
+
+ foreach ($lines as $line) {
+ $styledLines[] = $style . str_pad($line, $padding) . "\x1b[0m";
+ }
+
+ return implode("\n", $styledLines);
+ }
+
+ /**
+ * Writes a buffer out with a color sequence if colors are enabled.
+ *
+ * @param string $color
+ * @param string $buffer
+ * @param bool $lf
+ */
+ protected function writeWithColor($color, $buffer, $lf = true)
+ {
+ $this->write($this->formatWithColor($color, $buffer));
+
+ if ($lf) {
+ $this->write("\n");
+ }
+ }
+
+ /**
+ * Writes progress with a color sequence if colors are enabled.
+ *
+ * @param string $color
+ * @param string $buffer
+ */
+ protected function writeProgressWithColor($color, $buffer)
+ {
+ $buffer = $this->formatWithColor($color, $buffer);
+ $this->writeProgress($buffer);
+ }
+
+ /**
+ * @param int $count
+ * @param string $name
+ * @param string $color
+ * @param bool $always
+ */
+ private function writeCountString($count, $name, $color, $always = false)
+ {
+ static $first = true;
+
+ if ($always || $count > 0) {
+ $this->writeWithColor(
+ $color,
+ sprintf(
+ '%s%s: %d',
+ !$first ? ', ' : '',
+ $name,
+ $count
+ ),
+ false
+ );
+
+ $first = false;
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
+use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
+use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport;
+use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport;
+use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport;
+use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
+use SebastianBergmann\CodeCoverage\Report\Text as TextReport;
+use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport;
+use SebastianBergmann\Environment\Runtime;
+
+/**
+ * A TestRunner for the Command Line Interface (CLI)
+ * PHP SAPI Module.
+ */
+class PHPUnit_TextUI_TestRunner extends PHPUnit_Runner_BaseTestRunner
+{
+ const SUCCESS_EXIT = 0;
+ const FAILURE_EXIT = 1;
+ const EXCEPTION_EXIT = 2;
+
+ /**
+ * @var CodeCoverageFilter
+ */
+ protected $codeCoverageFilter;
+
+ /**
+ * @var PHPUnit_Runner_TestSuiteLoader
+ */
+ protected $loader = null;
+
+ /**
+ * @var PHPUnit_TextUI_ResultPrinter
+ */
+ protected $printer = null;
+
+ /**
+ * @var bool
+ */
+ protected static $versionStringPrinted = false;
+
+ /**
+ * @var Runtime
+ */
+ private $runtime;
+
+ /**
+ * @var bool
+ */
+ private $messagePrinted = false;
+
+ /**
+ * @param PHPUnit_Runner_TestSuiteLoader $loader
+ * @param CodeCoverageFilter $filter
+ */
+ public function __construct(PHPUnit_Runner_TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null)
+ {
+ if ($filter === null) {
+ $filter = new CodeCoverageFilter;
+ }
+
+ $this->codeCoverageFilter = $filter;
+ $this->loader = $loader;
+ $this->runtime = new Runtime;
+ }
+
+ /**
+ * @param PHPUnit_Framework_Test|ReflectionClass $test
+ * @param array $arguments
+ *
+ * @return PHPUnit_Framework_TestResult
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public static function run($test, array $arguments = [])
+ {
+ if ($test instanceof ReflectionClass) {
+ $test = new PHPUnit_Framework_TestSuite($test);
+ }
+
+ if ($test instanceof PHPUnit_Framework_Test) {
+ $aTestRunner = new self;
+
+ return $aTestRunner->doRun(
+ $test,
+ $arguments
+ );
+ } else {
+ throw new PHPUnit_Framework_Exception(
+ 'No test case or test suite found.'
+ );
+ }
+ }
+
+ /**
+ * @return PHPUnit_Framework_TestResult
+ */
+ protected function createTestResult()
+ {
+ return new PHPUnit_Framework_TestResult;
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestSuite $suite
+ * @param array $arguments
+ */
+ private function processSuiteFilters(PHPUnit_Framework_TestSuite $suite, array $arguments)
+ {
+ if (!$arguments['filter'] &&
+ empty($arguments['groups']) &&
+ empty($arguments['excludeGroups'])) {
+ return;
+ }
+
+ $filterFactory = new PHPUnit_Runner_Filter_Factory();
+
+ if (!empty($arguments['excludeGroups'])) {
+ $filterFactory->addFilter(
+ new ReflectionClass('PHPUnit_Runner_Filter_Group_Exclude'),
+ $arguments['excludeGroups']
+ );
+ }
+
+ if (!empty($arguments['groups'])) {
+ $filterFactory->addFilter(
+ new ReflectionClass('PHPUnit_Runner_Filter_Group_Include'),
+ $arguments['groups']
+ );
+ }
+
+ if ($arguments['filter']) {
+ $filterFactory->addFilter(
+ new ReflectionClass('PHPUnit_Runner_Filter_Test'),
+ $arguments['filter']
+ );
+ }
+ $suite->injectFilter($filterFactory);
+ }
+
+ /**
+ * @param PHPUnit_Framework_Test $suite
+ * @param array $arguments
+ * @param bool $exit
+ *
+ * @return PHPUnit_Framework_TestResult
+ */
+ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = [], $exit = true)
+ {
+ if (isset($arguments['configuration'])) {
+ $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration'];
+ }
+
+ $this->handleConfiguration($arguments);
+
+ $this->processSuiteFilters($suite, $arguments);
+
+ if (isset($arguments['bootstrap'])) {
+ $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap'];
+ }
+
+ if ($arguments['backupGlobals'] === false) {
+ $suite->setBackupGlobals(false);
+ }
+
+ if ($arguments['backupStaticAttributes'] === true) {
+ $suite->setBackupStaticAttributes(true);
+ }
+
+ if ($arguments['beStrictAboutChangesToGlobalState'] === true) {
+ $suite->setbeStrictAboutChangesToGlobalState(true);
+ }
+
+ if (is_int($arguments['repeat'])) {
+ $test = new PHPUnit_Extensions_RepeatedTest(
+ $suite,
+ $arguments['repeat'],
+ $arguments['processIsolation']
+ );
+
+ $suite = new PHPUnit_Framework_TestSuite();
+ $suite->addTest($test);
+ }
+
+ $result = $this->createTestResult();
+
+ if (!$arguments['convertErrorsToExceptions']) {
+ $result->convertErrorsToExceptions(false);
+ }
+
+ if (!$arguments['convertNoticesToExceptions']) {
+ PHPUnit_Framework_Error_Notice::$enabled = false;
+ }
+
+ if (!$arguments['convertWarningsToExceptions']) {
+ PHPUnit_Framework_Error_Warning::$enabled = false;
+ }
+
+ if ($arguments['stopOnError']) {
+ $result->stopOnError(true);
+ }
+
+ if ($arguments['stopOnFailure']) {
+ $result->stopOnFailure(true);
+ }
+
+ if ($arguments['stopOnWarning']) {
+ $result->stopOnWarning(true);
+ }
+
+ if ($arguments['stopOnIncomplete']) {
+ $result->stopOnIncomplete(true);
+ }
+
+ if ($arguments['stopOnRisky']) {
+ $result->stopOnRisky(true);
+ }
+
+ if ($arguments['stopOnSkipped']) {
+ $result->stopOnSkipped(true);
+ }
+
+ if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) {
+ $result->setRegisterMockObjectsFromTestArgumentsRecursively(true);
+ }
+
+ if ($this->printer === null) {
+ if (isset($arguments['printer']) &&
+ $arguments['printer'] instanceof PHPUnit_Util_Printer) {
+ $this->printer = $arguments['printer'];
+ } else {
+ $printerClass = 'PHPUnit_TextUI_ResultPrinter';
+
+ if (isset($arguments['printer']) &&
+ is_string($arguments['printer']) &&
+ class_exists($arguments['printer'], false)) {
+ $class = new ReflectionClass($arguments['printer']);
+
+ if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) {
+ $printerClass = $arguments['printer'];
+ }
+ }
+
+ $this->printer = new $printerClass(
+ isset($arguments['stderr']) ? 'php://stderr' : null,
+ $arguments['verbose'],
+ $arguments['colors'],
+ $arguments['debug'],
+ $arguments['columns'],
+ $arguments['reverseList']
+ );
+ }
+ }
+
+ if (!$this->printer instanceof PHPUnit_Util_Log_TAP) {
+ $this->printer->write(
+ PHPUnit_Runner_Version::getVersionString() . "\n"
+ );
+
+ self::$versionStringPrinted = true;
+
+ if ($arguments['verbose']) {
+ $runtime = $this->runtime->getNameWithVersion();
+
+ if ($this->runtime->hasXdebug()) {
+ $runtime .= sprintf(
+ ' with Xdebug %s',
+ phpversion('xdebug')
+ );
+ }
+
+ $this->writeMessage('Runtime', $runtime);
+
+ if (isset($arguments['configuration'])) {
+ $this->writeMessage(
+ 'Configuration',
+ $arguments['configuration']->getFilename()
+ );
+ }
+
+ foreach ($arguments['loadedExtensions'] as $extension) {
+ $this->writeMessage(
+ 'Extension',
+ $extension
+ );
+ }
+
+ foreach ($arguments['notLoadedExtensions'] as $extension) {
+ $this->writeMessage(
+ 'Extension',
+ $extension
+ );
+ }
+ }
+
+ if (isset($arguments['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'])) {
+ $this->writeMessage('Warning', 'Deprecated configuration setting "checkForUnintentionallyCoveredCode" used');
+ }
+
+ if (isset($arguments['tapLogfile'])) {
+ $this->writeMessage('Warning', 'Deprecated TAP test listener used');
+ }
+
+ if (isset($arguments['jsonLogfile'])) {
+ $this->writeMessage('Warning', 'Deprecated JSON test listener used');
+ }
+ }
+
+ foreach ($arguments['listeners'] as $listener) {
+ $result->addListener($listener);
+ }
+
+ $result->addListener($this->printer);
+
+ if (isset($arguments['testdoxHTMLFile'])) {
+ $result->addListener(
+ new PHPUnit_Util_TestDox_ResultPrinter_HTML(
+ $arguments['testdoxHTMLFile'],
+ $arguments['testdoxGroups'],
+ $arguments['testdoxExcludeGroups']
+ )
+ );
+ }
+
+ if (isset($arguments['testdoxTextFile'])) {
+ $result->addListener(
+ new PHPUnit_Util_TestDox_ResultPrinter_Text(
+ $arguments['testdoxTextFile'],
+ $arguments['testdoxGroups'],
+ $arguments['testdoxExcludeGroups']
+ )
+ );
+ }
+
+ if (isset($arguments['testdoxXMLFile'])) {
+ $result->addListener(
+ new PHPUnit_Util_TestDox_ResultPrinter_XML(
+ $arguments['testdoxXMLFile']
+ )
+ );
+ }
+
+ $codeCoverageReports = 0;
+
+ if (isset($arguments['coverageClover'])) {
+ $codeCoverageReports++;
+ }
+
+ if (isset($arguments['coverageCrap4J'])) {
+ $codeCoverageReports++;
+ }
+
+ if (isset($arguments['coverageHtml'])) {
+ $codeCoverageReports++;
+ }
+
+ if (isset($arguments['coveragePHP'])) {
+ $codeCoverageReports++;
+ }
+
+ if (isset($arguments['coverageText'])) {
+ $codeCoverageReports++;
+ }
+
+ if (isset($arguments['coverageXml'])) {
+ $codeCoverageReports++;
+ }
+
+ if (isset($arguments['noCoverage'])) {
+ $codeCoverageReports = 0;
+ }
+
+ if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) {
+ $this->writeMessage('Error', 'No code coverage driver is available');
+
+ $codeCoverageReports = 0;
+ }
+
+ if (!$this->printer instanceof PHPUnit_Util_Log_TAP) {
+ $this->printer->write("\n");
+ }
+
+ if ($codeCoverageReports > 0) {
+ $codeCoverage = new CodeCoverage(
+ null,
+ $this->codeCoverageFilter
+ );
+
+ $codeCoverage->setUnintentionallyCoveredSubclassesWhitelist(
+ [SebastianBergmann\Comparator\Comparator::class]
+ );
+
+ $codeCoverage->setCheckForUnintentionallyCoveredCode(
+ $arguments['strictCoverage']
+ );
+
+ $codeCoverage->setCheckForMissingCoversAnnotation(
+ $arguments['strictCoverage']
+ );
+
+ if (isset($arguments['forceCoversAnnotation'])) {
+ $codeCoverage->setForceCoversAnnotation(
+ $arguments['forceCoversAnnotation']
+ );
+ }
+
+ if (isset($arguments['disableCodeCoverageIgnore'])) {
+ $codeCoverage->setDisableIgnoredLines(true);
+ }
+
+ if (isset($arguments['whitelist'])) {
+ $this->codeCoverageFilter->addDirectoryToWhitelist($arguments['whitelist']);
+ }
+
+ if (isset($arguments['configuration'])) {
+ $filterConfiguration = $arguments['configuration']->getFilterConfiguration();
+
+ $codeCoverage->setAddUncoveredFilesFromWhitelist(
+ $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']
+ );
+
+ $codeCoverage->setProcessUncoveredFilesFromWhitelist(
+ $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']
+ );
+
+ foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) {
+ $this->codeCoverageFilter->addDirectoryToWhitelist(
+ $dir['path'],
+ $dir['suffix'],
+ $dir['prefix']
+ );
+ }
+
+ foreach ($filterConfiguration['whitelist']['include']['file'] as $file) {
+ $this->codeCoverageFilter->addFileToWhitelist($file);
+ }
+
+ foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) {
+ $this->codeCoverageFilter->removeDirectoryFromWhitelist(
+ $dir['path'],
+ $dir['suffix'],
+ $dir['prefix']
+ );
+ }
+
+ foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) {
+ $this->codeCoverageFilter->removeFileFromWhitelist($file);
+ }
+ }
+
+ if (!$this->codeCoverageFilter->hasWhitelist()) {
+ $this->writeMessage('Error', 'No whitelist configured, no code coverage will be generated');
+
+ $codeCoverageReports = 0;
+
+ unset($codeCoverage);
+ }
+ }
+
+ if (isset($codeCoverage)) {
+ $result->setCodeCoverage($codeCoverage);
+
+ if ($codeCoverageReports > 1 && isset($arguments['cacheTokens'])) {
+ $codeCoverage->setCacheTokens($arguments['cacheTokens']);
+ }
+ }
+
+ if (isset($arguments['jsonLogfile'])) {
+ $result->addListener(
+ new PHPUnit_Util_Log_JSON($arguments['jsonLogfile'])
+ );
+ }
+
+ if (isset($arguments['tapLogfile'])) {
+ $result->addListener(
+ new PHPUnit_Util_Log_TAP($arguments['tapLogfile'])
+ );
+ }
+
+ if (isset($arguments['teamcityLogfile'])) {
+ $result->addListener(
+ new PHPUnit_Util_Log_TeamCity($arguments['teamcityLogfile'])
+ );
+ }
+
+ if (isset($arguments['junitLogfile'])) {
+ $result->addListener(
+ new PHPUnit_Util_Log_JUnit(
+ $arguments['junitLogfile'],
+ $arguments['logIncompleteSkipped']
+ )
+ );
+ }
+
+ $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']);
+ $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']);
+ $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']);
+ $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']);
+ $result->enforceTimeLimit($arguments['enforceTimeLimit']);
+ $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']);
+ $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']);
+ $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']);
+
+ if ($suite instanceof PHPUnit_Framework_TestSuite) {
+ $suite->setRunTestInSeparateProcess($arguments['processIsolation']);
+ }
+
+ $suite->run($result);
+
+ unset($suite);
+ $result->flushListeners();
+
+ if ($this->printer instanceof PHPUnit_TextUI_ResultPrinter) {
+ $this->printer->printResult($result);
+ }
+
+ if (isset($codeCoverage)) {
+ if (isset($arguments['coverageClover'])) {
+ $this->printer->write(
+ "\nGenerating code coverage report in Clover XML format ..."
+ );
+
+ try {
+ $writer = new CloverReport();
+ $writer->process($codeCoverage, $arguments['coverageClover']);
+
+ $this->printer->write(" done\n");
+ unset($writer);
+ } catch (CodeCoverageException $e) {
+ $this->printer->write(
+ " failed\n" . $e->getMessage() . "\n"
+ );
+ }
+ }
+
+ if (isset($arguments['coverageCrap4J'])) {
+ $this->printer->write(
+ "\nGenerating Crap4J report XML file ..."
+ );
+
+ try {
+ $writer = new Crap4jReport($arguments['crap4jThreshold']);
+ $writer->process($codeCoverage, $arguments['coverageCrap4J']);
+
+ $this->printer->write(" done\n");
+ unset($writer);
+ } catch (CodeCoverageException $e) {
+ $this->printer->write(
+ " failed\n" . $e->getMessage() . "\n"
+ );
+ }
+ }
+
+ if (isset($arguments['coverageHtml'])) {
+ $this->printer->write(
+ "\nGenerating code coverage report in HTML format ..."
+ );
+
+ try {
+ $writer = new HtmlReport(
+ $arguments['reportLowUpperBound'],
+ $arguments['reportHighLowerBound'],
+ sprintf(
+ ' and <a href="https://phpunit.de/">PHPUnit %s</a>',
+ PHPUnit_Runner_Version::id()
+ )
+ );
+
+ $writer->process($codeCoverage, $arguments['coverageHtml']);
+
+ $this->printer->write(" done\n");
+ unset($writer);
+ } catch (CodeCoverageException $e) {
+ $this->printer->write(
+ " failed\n" . $e->getMessage() . "\n"
+ );
+ }
+ }
+
+ if (isset($arguments['coveragePHP'])) {
+ $this->printer->write(
+ "\nGenerating code coverage report in PHP format ..."
+ );
+
+ try {
+ $writer = new PhpReport();
+ $writer->process($codeCoverage, $arguments['coveragePHP']);
+
+ $this->printer->write(" done\n");
+ unset($writer);
+ } catch (CodeCoverageException $e) {
+ $this->printer->write(
+ " failed\n" . $e->getMessage() . "\n"
+ );
+ }
+ }
+
+ if (isset($arguments['coverageText'])) {
+ if ($arguments['coverageText'] == 'php://stdout') {
+ $outputStream = $this->printer;
+ $colors = $arguments['colors'] && $arguments['colors'] != PHPUnit_TextUI_ResultPrinter::COLOR_NEVER;
+ } else {
+ $outputStream = new PHPUnit_Util_Printer($arguments['coverageText']);
+ $colors = false;
+ }
+
+ $processor = new TextReport(
+ $arguments['reportLowUpperBound'],
+ $arguments['reportHighLowerBound'],
+ $arguments['coverageTextShowUncoveredFiles'],
+ $arguments['coverageTextShowOnlySummary']
+ );
+
+ $outputStream->write(
+ $processor->process($codeCoverage, $colors)
+ );
+ }
+
+ if (isset($arguments['coverageXml'])) {
+ $this->printer->write(
+ "\nGenerating code coverage report in PHPUnit XML format ..."
+ );
+
+ try {
+ $writer = new XmlReport;
+ $writer->process($codeCoverage, $arguments['coverageXml']);
+
+ $this->printer->write(" done\n");
+ unset($writer);
+ } catch (CodeCoverageException $e) {
+ $this->printer->write(
+ " failed\n" . $e->getMessage() . "\n"
+ );
+ }
+ }
+ }
+
+ if ($exit) {
+ if ($result->wasSuccessful(false)) {
+ if ($arguments['failOnRisky'] && !$result->allHarmless()) {
+ exit(self::FAILURE_EXIT);
+ }
+
+ if ($arguments['failOnWarning'] && $result->warningCount() > 0) {
+ exit(self::FAILURE_EXIT);
+ }
+
+ exit(self::SUCCESS_EXIT);
+ }
+
+ if ($result->errorCount() > 0) {
+ exit(self::EXCEPTION_EXIT);
+ }
+
+ if ($result->failureCount() > 0) {
+ exit(self::FAILURE_EXIT);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param PHPUnit_TextUI_ResultPrinter $resultPrinter
+ */
+ public function setPrinter(PHPUnit_TextUI_ResultPrinter $resultPrinter)
+ {
+ $this->printer = $resultPrinter;
+ }
+
+ /**
+ * Override to define how to handle a failed loading of
+ * a test suite.
+ *
+ * @param string $message
+ */
+ protected function runFailed($message)
+ {
+ $this->write($message . PHP_EOL);
+ exit(self::FAILURE_EXIT);
+ }
+
+ /**
+ * @param string $buffer
+ */
+ protected function write($buffer)
+ {
+ if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') {
+ $buffer = htmlspecialchars($buffer);
+ }
+
+ if ($this->printer !== null) {
+ $this->printer->write($buffer);
+ } else {
+ print $buffer;
+ }
+ }
+
+ /**
+ * Returns the loader to be used.
+ *
+ * @return PHPUnit_Runner_TestSuiteLoader
+ */
+ public function getLoader()
+ {
+ if ($this->loader === null) {
+ $this->loader = new PHPUnit_Runner_StandardTestSuiteLoader;
+ }
+
+ return $this->loader;
+ }
+
+ /**
+ * @param array $arguments
+ */
+ protected function handleConfiguration(array &$arguments)
+ {
+ if (isset($arguments['configuration']) &&
+ !$arguments['configuration'] instanceof PHPUnit_Util_Configuration) {
+ $arguments['configuration'] = PHPUnit_Util_Configuration::getInstance(
+ $arguments['configuration']
+ );
+ }
+
+ $arguments['debug'] = isset($arguments['debug']) ? $arguments['debug'] : false;
+ $arguments['filter'] = isset($arguments['filter']) ? $arguments['filter'] : false;
+ $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : [];
+
+ if (isset($arguments['configuration'])) {
+ $arguments['configuration']->handlePHPConfiguration();
+
+ $phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration();
+
+ if (isset($phpunitConfiguration['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'])) {
+ $arguments['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'] = true;
+ }
+
+ if (isset($phpunitConfiguration['backupGlobals']) &&
+ !isset($arguments['backupGlobals'])) {
+ $arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals'];
+ }
+
+ if (isset($phpunitConfiguration['backupStaticAttributes']) &&
+ !isset($arguments['backupStaticAttributes'])) {
+ $arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes'];
+ }
+
+ if (isset($phpunitConfiguration['beStrictAboutChangesToGlobalState']) &&
+ !isset($arguments['beStrictAboutChangesToGlobalState'])) {
+ $arguments['beStrictAboutChangesToGlobalState'] = $phpunitConfiguration['beStrictAboutChangesToGlobalState'];
+ }
+
+ if (isset($phpunitConfiguration['bootstrap']) &&
+ !isset($arguments['bootstrap'])) {
+ $arguments['bootstrap'] = $phpunitConfiguration['bootstrap'];
+ }
+
+ if (isset($phpunitConfiguration['cacheTokens']) &&
+ !isset($arguments['cacheTokens'])) {
+ $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens'];
+ }
+
+ if (isset($phpunitConfiguration['colors']) &&
+ !isset($arguments['colors'])) {
+ $arguments['colors'] = $phpunitConfiguration['colors'];
+ }
+
+ if (isset($phpunitConfiguration['convertErrorsToExceptions']) &&
+ !isset($arguments['convertErrorsToExceptions'])) {
+ $arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions'];
+ }
+
+ if (isset($phpunitConfiguration['convertNoticesToExceptions']) &&
+ !isset($arguments['convertNoticesToExceptions'])) {
+ $arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions'];
+ }
+
+ if (isset($phpunitConfiguration['convertWarningsToExceptions']) &&
+ !isset($arguments['convertWarningsToExceptions'])) {
+ $arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions'];
+ }
+
+ if (isset($phpunitConfiguration['processIsolation']) &&
+ !isset($arguments['processIsolation'])) {
+ $arguments['processIsolation'] = $phpunitConfiguration['processIsolation'];
+ }
+
+ if (isset($phpunitConfiguration['stopOnError']) &&
+ !isset($arguments['stopOnError'])) {
+ $arguments['stopOnError'] = $phpunitConfiguration['stopOnError'];
+ }
+
+ if (isset($phpunitConfiguration['stopOnFailure']) &&
+ !isset($arguments['stopOnFailure'])) {
+ $arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure'];
+ }
+
+ if (isset($phpunitConfiguration['stopOnWarning']) &&
+ !isset($arguments['stopOnWarning'])) {
+ $arguments['stopOnWarning'] = $phpunitConfiguration['stopOnWarning'];
+ }
+
+ if (isset($phpunitConfiguration['stopOnIncomplete']) &&
+ !isset($arguments['stopOnIncomplete'])) {
+ $arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete'];
+ }
+
+ if (isset($phpunitConfiguration['stopOnRisky']) &&
+ !isset($arguments['stopOnRisky'])) {
+ $arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky'];
+ }
+
+ if (isset($phpunitConfiguration['stopOnSkipped']) &&
+ !isset($arguments['stopOnSkipped'])) {
+ $arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped'];
+ }
+
+ if (isset($phpunitConfiguration['failOnWarning']) &&
+ !isset($arguments['failOnWarning'])) {
+ $arguments['failOnWarning'] = $phpunitConfiguration['failOnWarning'];
+ }
+
+ if (isset($phpunitConfiguration['failOnRisky']) &&
+ !isset($arguments['failOnRisky'])) {
+ $arguments['failOnRisky'] = $phpunitConfiguration['failOnRisky'];
+ }
+
+ if (isset($phpunitConfiguration['timeoutForSmallTests']) &&
+ !isset($arguments['timeoutForSmallTests'])) {
+ $arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests'];
+ }
+
+ if (isset($phpunitConfiguration['timeoutForMediumTests']) &&
+ !isset($arguments['timeoutForMediumTests'])) {
+ $arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests'];
+ }
+
+ if (isset($phpunitConfiguration['timeoutForLargeTests']) &&
+ !isset($arguments['timeoutForLargeTests'])) {
+ $arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests'];
+ }
+
+ if (isset($phpunitConfiguration['reportUselessTests']) &&
+ !isset($arguments['reportUselessTests'])) {
+ $arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests'];
+ }
+
+ if (isset($phpunitConfiguration['strictCoverage']) &&
+ !isset($arguments['strictCoverage'])) {
+ $arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage'];
+ }
+
+ if (isset($phpunitConfiguration['disallowTestOutput']) &&
+ !isset($arguments['disallowTestOutput'])) {
+ $arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput'];
+ }
+
+ if (isset($phpunitConfiguration['enforceTimeLimit']) &&
+ !isset($arguments['enforceTimeLimit'])) {
+ $arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit'];
+ }
+
+ if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) &&
+ !isset($arguments['disallowTodoAnnotatedTests'])) {
+ $arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests'];
+ }
+
+ if (isset($phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']) &&
+ !isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])) {
+ $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests'];
+ }
+
+ if (isset($phpunitConfiguration['verbose']) &&
+ !isset($arguments['verbose'])) {
+ $arguments['verbose'] = $phpunitConfiguration['verbose'];
+ }
+
+ if (isset($phpunitConfiguration['reverseDefectList']) &&
+ !isset($arguments['reverseList'])) {
+ $arguments['reverseList'] = $phpunitConfiguration['reverseDefectList'];
+ }
+
+ if (isset($phpunitConfiguration['forceCoversAnnotation']) &&
+ !isset($arguments['forceCoversAnnotation'])) {
+ $arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation'];
+ }
+
+ if (isset($phpunitConfiguration['disableCodeCoverageIgnore']) &&
+ !isset($arguments['disableCodeCoverageIgnore'])) {
+ $arguments['disableCodeCoverageIgnore'] = $phpunitConfiguration['disableCodeCoverageIgnore'];
+ }
+
+ if (isset($phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']) &&
+ !isset($arguments['registerMockObjectsFromTestArgumentsRecursively'])) {
+ $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively'];
+ }
+
+ $groupCliArgs = [];
+
+ if (!empty($arguments['groups'])) {
+ $groupCliArgs = $arguments['groups'];
+ }
+
+ $groupConfiguration = $arguments['configuration']->getGroupConfiguration();
+
+ if (!empty($groupConfiguration['include']) &&
+ !isset($arguments['groups'])) {
+ $arguments['groups'] = $groupConfiguration['include'];
+ }
+
+ if (!empty($groupConfiguration['exclude']) &&
+ !isset($arguments['excludeGroups'])) {
+ $arguments['excludeGroups'] = array_diff($groupConfiguration['exclude'], $groupCliArgs);
+ }
+
+ foreach ($arguments['configuration']->getListenerConfiguration() as $listener) {
+ if (!class_exists($listener['class'], false) &&
+ $listener['file'] !== '') {
+ require_once $listener['file'];
+ }
+
+ if (!class_exists($listener['class'])) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Class "%s" does not exist',
+ $listener['class']
+ )
+ );
+ }
+
+ $listenerClass = new ReflectionClass($listener['class']);
+
+ if (!$listenerClass->implementsInterface(PHPUnit_Framework_TestListener::class)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Class "%s" does not implement the PHPUnit_Framework_TestListener interface',
+ $listener['class']
+ )
+ );
+ }
+
+ if (count($listener['arguments']) == 0) {
+ $listener = new $listener['class'];
+ } else {
+ $listener = $listenerClass->newInstanceArgs(
+ $listener['arguments']
+ );
+ }
+
+ $arguments['listeners'][] = $listener;
+ }
+
+ $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration();
+
+ if (isset($loggingConfiguration['coverage-clover']) &&
+ !isset($arguments['coverageClover'])) {
+ $arguments['coverageClover'] = $loggingConfiguration['coverage-clover'];
+ }
+
+ if (isset($loggingConfiguration['coverage-crap4j']) &&
+ !isset($arguments['coverageCrap4J'])) {
+ $arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j'];
+
+ if (isset($loggingConfiguration['crap4jThreshold']) &&
+ !isset($arguments['crap4jThreshold'])) {
+ $arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold'];
+ }
+ }
+
+ if (isset($loggingConfiguration['coverage-html']) &&
+ !isset($arguments['coverageHtml'])) {
+ if (isset($loggingConfiguration['lowUpperBound']) &&
+ !isset($arguments['reportLowUpperBound'])) {
+ $arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound'];
+ }
+
+ if (isset($loggingConfiguration['highLowerBound']) &&
+ !isset($arguments['reportHighLowerBound'])) {
+ $arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound'];
+ }
+
+ $arguments['coverageHtml'] = $loggingConfiguration['coverage-html'];
+ }
+
+ if (isset($loggingConfiguration['coverage-php']) &&
+ !isset($arguments['coveragePHP'])) {
+ $arguments['coveragePHP'] = $loggingConfiguration['coverage-php'];
+ }
+
+ if (isset($loggingConfiguration['coverage-text']) &&
+ !isset($arguments['coverageText'])) {
+ $arguments['coverageText'] = $loggingConfiguration['coverage-text'];
+ if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) {
+ $arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles'];
+ } else {
+ $arguments['coverageTextShowUncoveredFiles'] = false;
+ }
+ if (isset($loggingConfiguration['coverageTextShowOnlySummary'])) {
+ $arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary'];
+ } else {
+ $arguments['coverageTextShowOnlySummary'] = false;
+ }
+ }
+
+ if (isset($loggingConfiguration['coverage-xml']) &&
+ !isset($arguments['coverageXml'])) {
+ $arguments['coverageXml'] = $loggingConfiguration['coverage-xml'];
+ }
+
+ if (isset($loggingConfiguration['json']) &&
+ !isset($arguments['jsonLogfile'])) {
+ $arguments['jsonLogfile'] = $loggingConfiguration['json'];
+ }
+
+ if (isset($loggingConfiguration['plain'])) {
+ $arguments['listeners'][] = new PHPUnit_TextUI_ResultPrinter(
+ $loggingConfiguration['plain'],
+ true
+ );
+ }
+
+ if (isset($loggingConfiguration['tap']) &&
+ !isset($arguments['tapLogfile'])) {
+ $arguments['tapLogfile'] = $loggingConfiguration['tap'];
+ }
+
+ if (isset($loggingConfiguration['teamcity']) &&
+ !isset($arguments['teamcityLogfile'])) {
+ $arguments['teamcityLogfile'] = $loggingConfiguration['teamcity'];
+ }
+
+ if (isset($loggingConfiguration['junit']) &&
+ !isset($arguments['junitLogfile'])) {
+ $arguments['junitLogfile'] = $loggingConfiguration['junit'];
+
+ if (isset($loggingConfiguration['logIncompleteSkipped']) &&
+ !isset($arguments['logIncompleteSkipped'])) {
+ $arguments['logIncompleteSkipped'] = $loggingConfiguration['logIncompleteSkipped'];
+ }
+ }
+
+ if (isset($loggingConfiguration['testdox-html']) &&
+ !isset($arguments['testdoxHTMLFile'])) {
+ $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html'];
+ }
+
+ if (isset($loggingConfiguration['testdox-text']) &&
+ !isset($arguments['testdoxTextFile'])) {
+ $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text'];
+ }
+
+ if (isset($loggingConfiguration['testdox-xml']) &&
+ !isset($arguments['testdoxXMLFile'])) {
+ $arguments['testdoxXMLFile'] = $loggingConfiguration['testdox-xml'];
+ }
+
+ $testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration();
+
+ if (isset($testdoxGroupConfiguration['include']) &&
+ !isset($arguments['testdoxGroups'])) {
+ $arguments['testdoxGroups'] = $testdoxGroupConfiguration['include'];
+ }
+
+ if (isset($testdoxGroupConfiguration['exclude']) &&
+ !isset($arguments['testdoxExcludeGroups'])) {
+ $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration['exclude'];
+ }
+ }
+
+ $arguments['addUncoveredFilesFromWhitelist'] = isset($arguments['addUncoveredFilesFromWhitelist']) ? $arguments['addUncoveredFilesFromWhitelist'] : true;
+ $arguments['processUncoveredFilesFromWhitelist'] = isset($arguments['processUncoveredFilesFromWhitelist']) ? $arguments['processUncoveredFilesFromWhitelist'] : false;
+ $arguments['backupGlobals'] = isset($arguments['backupGlobals']) ? $arguments['backupGlobals'] : null;
+ $arguments['backupStaticAttributes'] = isset($arguments['backupStaticAttributes']) ? $arguments['backupStaticAttributes'] : null;
+ $arguments['beStrictAboutChangesToGlobalState'] = isset($arguments['beStrictAboutChangesToGlobalState']) ? $arguments['beStrictAboutChangesToGlobalState'] : null;
+ $arguments['cacheTokens'] = isset($arguments['cacheTokens']) ? $arguments['cacheTokens'] : false;
+ $arguments['columns'] = isset($arguments['columns']) ? $arguments['columns'] : 80;
+ $arguments['colors'] = isset($arguments['colors']) ? $arguments['colors'] : PHPUnit_TextUI_ResultPrinter::COLOR_DEFAULT;
+ $arguments['convertErrorsToExceptions'] = isset($arguments['convertErrorsToExceptions']) ? $arguments['convertErrorsToExceptions'] : true;
+ $arguments['convertNoticesToExceptions'] = isset($arguments['convertNoticesToExceptions']) ? $arguments['convertNoticesToExceptions'] : true;
+ $arguments['convertWarningsToExceptions'] = isset($arguments['convertWarningsToExceptions']) ? $arguments['convertWarningsToExceptions'] : true;
+ $arguments['excludeGroups'] = isset($arguments['excludeGroups']) ? $arguments['excludeGroups'] : [];
+ $arguments['groups'] = isset($arguments['groups']) ? $arguments['groups'] : [];
+ $arguments['logIncompleteSkipped'] = isset($arguments['logIncompleteSkipped']) ? $arguments['logIncompleteSkipped'] : false;
+ $arguments['processIsolation'] = isset($arguments['processIsolation']) ? $arguments['processIsolation'] : false;
+ $arguments['repeat'] = isset($arguments['repeat']) ? $arguments['repeat'] : false;
+ $arguments['reportHighLowerBound'] = isset($arguments['reportHighLowerBound']) ? $arguments['reportHighLowerBound'] : 90;
+ $arguments['reportLowUpperBound'] = isset($arguments['reportLowUpperBound']) ? $arguments['reportLowUpperBound'] : 50;
+ $arguments['crap4jThreshold'] = isset($arguments['crap4jThreshold']) ? $arguments['crap4jThreshold'] : 30;
+ $arguments['stopOnError'] = isset($arguments['stopOnError']) ? $arguments['stopOnError'] : false;
+ $arguments['stopOnFailure'] = isset($arguments['stopOnFailure']) ? $arguments['stopOnFailure'] : false;
+ $arguments['stopOnWarning'] = isset($arguments['stopOnWarning']) ? $arguments['stopOnWarning'] : false;
+ $arguments['stopOnIncomplete'] = isset($arguments['stopOnIncomplete']) ? $arguments['stopOnIncomplete'] : false;
+ $arguments['stopOnRisky'] = isset($arguments['stopOnRisky']) ? $arguments['stopOnRisky'] : false;
+ $arguments['stopOnSkipped'] = isset($arguments['stopOnSkipped']) ? $arguments['stopOnSkipped'] : false;
+ $arguments['failOnWarning'] = isset($arguments['failOnWarning']) ? $arguments['failOnWarning'] : false;
+ $arguments['failOnRisky'] = isset($arguments['failOnRisky']) ? $arguments['failOnRisky'] : false;
+ $arguments['timeoutForSmallTests'] = isset($arguments['timeoutForSmallTests']) ? $arguments['timeoutForSmallTests'] : 1;
+ $arguments['timeoutForMediumTests'] = isset($arguments['timeoutForMediumTests']) ? $arguments['timeoutForMediumTests'] : 10;
+ $arguments['timeoutForLargeTests'] = isset($arguments['timeoutForLargeTests']) ? $arguments['timeoutForLargeTests'] : 60;
+ $arguments['reportUselessTests'] = isset($arguments['reportUselessTests']) ? $arguments['reportUselessTests'] : false;
+ $arguments['strictCoverage'] = isset($arguments['strictCoverage']) ? $arguments['strictCoverage'] : false;
+ $arguments['disallowTestOutput'] = isset($arguments['disallowTestOutput']) ? $arguments['disallowTestOutput'] : false;
+ $arguments['enforceTimeLimit'] = isset($arguments['enforceTimeLimit']) ? $arguments['enforceTimeLimit'] : false;
+ $arguments['disallowTodoAnnotatedTests'] = isset($arguments['disallowTodoAnnotatedTests']) ? $arguments['disallowTodoAnnotatedTests'] : false;
+ $arguments['beStrictAboutResourceUsageDuringSmallTests'] = isset($arguments['beStrictAboutResourceUsageDuringSmallTests']) ? $arguments['beStrictAboutResourceUsageDuringSmallTests'] : false;
+ $arguments['reverseList'] = isset($arguments['reverseList']) ? $arguments['reverseList'] : false;
+ $arguments['registerMockObjectsFromTestArgumentsRecursively'] = isset($arguments['registerMockObjectsFromTestArgumentsRecursively']) ? $arguments['registerMockObjectsFromTestArgumentsRecursively'] : false;
+ $arguments['verbose'] = isset($arguments['verbose']) ? $arguments['verbose'] : false;
+ $arguments['testdoxExcludeGroups'] = isset($arguments['testdoxExcludeGroups']) ? $arguments['testdoxExcludeGroups'] : [];
+ $arguments['testdoxGroups'] = isset($arguments['testdoxGroups']) ? $arguments['testdoxGroups'] : [];
+ }
+
+ /**
+ * @param string $type
+ * @param string $message
+ */
+ private function writeMessage($type, $message)
+ {
+ if (!$this->messagePrinted) {
+ $this->write("\n");
+ }
+
+ $this->write(
+ sprintf(
+ "%-15s%s\n",
+ $type . ':',
+ $message
+ )
+ );
+
+ $this->messagePrinted = true;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Utility class for blacklisting PHPUnit's own source code files.
+ */
+class PHPUnit_Util_Blacklist
+{
+ /**
+ * @var array
+ */
+ public static $blacklistedClassNames = [
+ 'File_Iterator' => 1,
+ 'PHP_Invoker' => 1,
+ 'PHP_Timer' => 1,
+ 'PHP_Token' => 1,
+ 'PHPUnit_Framework_TestCase' => 2,
+ 'PHPUnit_Extensions_Database_TestCase' => 2,
+ 'PHPUnit_Framework_MockObject_Generator' => 2,
+ 'Text_Template' => 1,
+ 'Symfony\Component\Yaml\Yaml' => 1,
+ 'SebastianBergmann\CodeCoverage\CodeCoverage' => 1,
+ 'SebastianBergmann\Diff\Diff' => 1,
+ 'SebastianBergmann\Environment\Runtime' => 1,
+ 'SebastianBergmann\Comparator\Comparator' => 1,
+ 'SebastianBergmann\Exporter\Exporter' => 1,
+ 'SebastianBergmann\GlobalState\Snapshot' => 1,
+ 'SebastianBergmann\RecursionContext\Context' => 1,
+ 'SebastianBergmann\Version' => 1,
+ 'Composer\Autoload\ClassLoader' => 1,
+ 'Doctrine\Instantiator\Instantiator' => 1,
+ 'phpDocumentor\Reflection\DocBlock' => 1,
+ 'Prophecy\Prophet' => 1,
+ 'DeepCopy\DeepCopy' => 1
+ ];
+
+ /**
+ * @var array
+ */
+ private static $directories;
+
+ /**
+ * @return array
+ */
+ public function getBlacklistedDirectories()
+ {
+ $this->initialize();
+
+ return self::$directories;
+ }
+
+ /**
+ * @param string $file
+ *
+ * @return bool
+ */
+ public function isBlacklisted($file)
+ {
+ if (defined('PHPUNIT_TESTSUITE')) {
+ return false;
+ }
+
+ $this->initialize();
+
+ foreach (self::$directories as $directory) {
+ if (strpos($file, $directory) === 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function initialize()
+ {
+ if (self::$directories === null) {
+ self::$directories = [];
+
+ foreach (self::$blacklistedClassNames as $className => $parent) {
+ if (!class_exists($className)) {
+ continue;
+ }
+
+ $reflector = new ReflectionClass($className);
+ $directory = $reflector->getFileName();
+
+ for ($i = 0; $i < $parent; $i++) {
+ $directory = dirname($directory);
+ }
+
+ self::$directories[] = $directory;
+ }
+
+ // Hide process isolation workaround on Windows.
+ // @see PHPUnit_Util_PHP::factory()
+ // @see PHPUnit_Util_PHP_Windows::process()
+ if (DIRECTORY_SEPARATOR === '\\') {
+ // tempnam() prefix is limited to first 3 chars.
+ // @see http://php.net/manual/en/function.tempnam.php
+ self::$directories[] = sys_get_temp_dir() . '\\PHP';
+ }
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Wrapper for the PHPUnit XML configuration file.
+ *
+ * Example XML configuration file:
+ * <code>
+ * <?xml version="1.0" encoding="utf-8" ?>
+ *
+ * <phpunit backupGlobals="true"
+ * backupStaticAttributes="false"
+ * bootstrap="/path/to/bootstrap.php"
+ * cacheTokens="false"
+ * columns="80"
+ * colors="false"
+ * stderr="false"
+ * convertErrorsToExceptions="true"
+ * convertNoticesToExceptions="true"
+ * convertWarningsToExceptions="true"
+ * forceCoversAnnotation="false"
+ * processIsolation="false"
+ * stopOnError="false"
+ * stopOnFailure="false"
+ * stopOnWarning="false"
+ * stopOnIncomplete="false"
+ * stopOnRisky="false"
+ * stopOnSkipped="false"
+ * failOnWarning="false"
+ * failOnRisky="false"
+ * extensionsDirectory="tools/phpunit.d"
+ * printerClass="PHPUnit_TextUI_ResultPrinter"
+ * testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
+ * beStrictAboutChangesToGlobalState="false"
+ * beStrictAboutCoversAnnotation="false"
+ * beStrictAboutOutputDuringTests="false"
+ * beStrictAboutResourceUsageDuringSmallTests="false"
+ * beStrictAboutTestsThatDoNotTestAnything="false"
+ * beStrictAboutTodoAnnotatedTests="false"
+ * checkForUnintentionallyCoveredCode="false"
+ * enforceTimeLimit="false"
+ * timeoutForSmallTests="1"
+ * timeoutForMediumTests="10"
+ * timeoutForLargeTests="60"
+ * verbose="false"
+ * reverseDefectList="false"
+ * registerMockObjectsFromTestArgumentsRecursively="false">
+ * <testsuites>
+ * <testsuite name="My Test Suite">
+ * <directory suffix="Test.php" phpVersion="5.3.0" phpVersionOperator=">=">/path/to/files</directory>
+ * <file phpVersion="5.3.0" phpVersionOperator=">=">/path/to/MyTest.php</file>
+ * <exclude>/path/to/files/exclude</exclude>
+ * </testsuite>
+ * </testsuites>
+ *
+ * <groups>
+ * <include>
+ * <group>name</group>
+ * </include>
+ * <exclude>
+ * <group>name</group>
+ * </exclude>
+ * </groups>
+ *
+ * <testdoxGroups>
+ * <include>
+ * <group>name</group>
+ * </include>
+ * <exclude>
+ * <group>name</group>
+ * </exclude>
+ * </testdoxGroups>
+ *
+ * <filter>
+ * <whitelist addUncoveredFilesFromWhitelist="true"
+ * processUncoveredFilesFromWhitelist="false">
+ * <directory suffix=".php">/path/to/files</directory>
+ * <file>/path/to/file</file>
+ * <exclude>
+ * <directory suffix=".php">/path/to/files</directory>
+ * <file>/path/to/file</file>
+ * </exclude>
+ * </whitelist>
+ * </filter>
+ *
+ * <listeners>
+ * <listener class="MyListener" file="/optional/path/to/MyListener.php">
+ * <arguments>
+ * <array>
+ * <element key="0">
+ * <string>Sebastian</string>
+ * </element>
+ * </array>
+ * <integer>22</integer>
+ * <string>April</string>
+ * <double>19.78</double>
+ * <null/>
+ * <object class="stdClass"/>
+ * <file>MyRelativeFile.php</file>
+ * <directory>MyRelativeDir</directory>
+ * </arguments>
+ * </listener>
+ * </listeners>
+ *
+ * <logging>
+ * <log type="coverage-html" target="/tmp/report" lowUpperBound="50" highLowerBound="90"/>
+ * <log type="coverage-clover" target="/tmp/clover.xml"/>
+ * <log type="coverage-crap4j" target="/tmp/crap.xml" threshold="30"/>
+ * <log type="json" target="/tmp/logfile.json"/>
+ * <log type="plain" target="/tmp/logfile.txt"/>
+ * <log type="tap" target="/tmp/logfile.tap"/>
+ * <log type="teamcity" target="/tmp/logfile.txt"/>
+ * <log type="junit" target="/tmp/logfile.xml" logIncompleteSkipped="false"/>
+ * <log type="testdox-html" target="/tmp/testdox.html"/>
+ * <log type="testdox-text" target="/tmp/testdox.txt"/>
+ * <log type="testdox-xml" target="/tmp/testdox.xml"/>
+ * </logging>
+ *
+ * <php>
+ * <includePath>.</includePath>
+ * <ini name="foo" value="bar"/>
+ * <const name="foo" value="bar"/>
+ * <var name="foo" value="bar"/>
+ * <env name="foo" value="bar"/>
+ * <post name="foo" value="bar"/>
+ * <get name="foo" value="bar"/>
+ * <cookie name="foo" value="bar"/>
+ * <server name="foo" value="bar"/>
+ * <files name="foo" value="bar"/>
+ * <request name="foo" value="bar"/>
+ * </php>
+ * </phpunit>
+ * </code>
+ */
+class PHPUnit_Util_Configuration
+{
+ private static $instances = [];
+
+ protected $document;
+ protected $xpath;
+ protected $filename;
+
+ /**
+ * Loads a PHPUnit configuration file.
+ *
+ * @param string $filename
+ */
+ protected function __construct($filename)
+ {
+ $this->filename = $filename;
+ $this->document = PHPUnit_Util_XML::loadFile($filename, false, true, true);
+ $this->xpath = new DOMXPath($this->document);
+ }
+
+ final private function __clone()
+ {
+ }
+
+ /**
+ * Returns a PHPUnit configuration object.
+ *
+ * @param string $filename
+ *
+ * @return PHPUnit_Util_Configuration
+ */
+ public static function getInstance($filename)
+ {
+ $realpath = realpath($filename);
+
+ if ($realpath === false) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Could not read "%s".',
+ $filename
+ )
+ );
+ }
+
+ if (!isset(self::$instances[$realpath])) {
+ self::$instances[$realpath] = new self($realpath);
+ }
+
+ return self::$instances[$realpath];
+ }
+
+ /**
+ * Returns the realpath to the configuration file.
+ *
+ * @return string
+ */
+ public function getFilename()
+ {
+ return $this->filename;
+ }
+
+ /**
+ * Returns the configuration for SUT filtering.
+ *
+ * @return array
+ */
+ public function getFilterConfiguration()
+ {
+ $addUncoveredFilesFromWhitelist = true;
+ $processUncoveredFilesFromWhitelist = false;
+
+ $tmp = $this->xpath->query('filter/whitelist');
+
+ if ($tmp->length == 1) {
+ if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) {
+ $addUncoveredFilesFromWhitelist = $this->getBoolean(
+ (string) $tmp->item(0)->getAttribute(
+ 'addUncoveredFilesFromWhitelist'
+ ),
+ true
+ );
+ }
+
+ if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) {
+ $processUncoveredFilesFromWhitelist = $this->getBoolean(
+ (string) $tmp->item(0)->getAttribute(
+ 'processUncoveredFilesFromWhitelist'
+ ),
+ false
+ );
+ }
+ }
+
+ return [
+ 'whitelist' => [
+ 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist,
+ 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist,
+ 'include' => [
+ 'directory' => $this->readFilterDirectories(
+ 'filter/whitelist/directory'
+ ),
+ 'file' => $this->readFilterFiles(
+ 'filter/whitelist/file'
+ )
+ ],
+ 'exclude' => [
+ 'directory' => $this->readFilterDirectories(
+ 'filter/whitelist/exclude/directory'
+ ),
+ 'file' => $this->readFilterFiles(
+ 'filter/whitelist/exclude/file'
+ )
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Returns the configuration for groups.
+ *
+ * @return array
+ */
+ public function getGroupConfiguration()
+ {
+ return $this->parseGroupConfiguration('groups');
+ }
+
+ /**
+ * Returns the configuration for testdox groups.
+ *
+ * @return array
+ */
+ public function getTestdoxGroupConfiguration()
+ {
+ return $this->parseGroupConfiguration('testdoxGroups');
+ }
+
+ /**
+ * @param string $root
+ *
+ * @return array
+ */
+ private function parseGroupConfiguration($root)
+ {
+ $groups = [
+ 'include' => [],
+ 'exclude' => []
+ ];
+
+ foreach ($this->xpath->query($root . '/include/group') as $group) {
+ $groups['include'][] = (string) $group->textContent;
+ }
+
+ foreach ($this->xpath->query($root . '/exclude/group') as $group) {
+ $groups['exclude'][] = (string) $group->textContent;
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Returns the configuration for listeners.
+ *
+ * @return array
+ */
+ public function getListenerConfiguration()
+ {
+ $result = [];
+
+ foreach ($this->xpath->query('listeners/listener') as $listener) {
+ $class = (string) $listener->getAttribute('class');
+ $file = '';
+ $arguments = [];
+
+ if ($listener->getAttribute('file')) {
+ $file = $this->toAbsolutePath(
+ (string) $listener->getAttribute('file'),
+ true
+ );
+ }
+
+ foreach ($listener->childNodes as $node) {
+ if ($node instanceof DOMElement && $node->tagName == 'arguments') {
+ foreach ($node->childNodes as $argument) {
+ if ($argument instanceof DOMElement) {
+ if ($argument->tagName == 'file' ||
+ $argument->tagName == 'directory') {
+ $arguments[] = $this->toAbsolutePath((string) $argument->textContent);
+ } else {
+ $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument);
+ }
+ }
+ }
+ }
+ }
+
+ $result[] = [
+ 'class' => $class,
+ 'file' => $file,
+ 'arguments' => $arguments
+ ];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the logging configuration.
+ *
+ * @return array
+ */
+ public function getLoggingConfiguration()
+ {
+ $result = [];
+
+ foreach ($this->xpath->query('logging/log') as $log) {
+ $type = (string) $log->getAttribute('type');
+ $target = (string) $log->getAttribute('target');
+
+ if (!$target) {
+ continue;
+ }
+
+ $target = $this->toAbsolutePath($target);
+
+ if ($type == 'coverage-html') {
+ if ($log->hasAttribute('lowUpperBound')) {
+ $result['lowUpperBound'] = $this->getInteger(
+ (string) $log->getAttribute('lowUpperBound'),
+ 50
+ );
+ }
+
+ if ($log->hasAttribute('highLowerBound')) {
+ $result['highLowerBound'] = $this->getInteger(
+ (string) $log->getAttribute('highLowerBound'),
+ 90
+ );
+ }
+ } elseif ($type == 'coverage-crap4j') {
+ if ($log->hasAttribute('threshold')) {
+ $result['crap4jThreshold'] = $this->getInteger(
+ (string) $log->getAttribute('threshold'),
+ 30
+ );
+ }
+ } elseif ($type == 'junit') {
+ if ($log->hasAttribute('logIncompleteSkipped')) {
+ $result['logIncompleteSkipped'] = $this->getBoolean(
+ (string) $log->getAttribute('logIncompleteSkipped'),
+ false
+ );
+ }
+ } elseif ($type == 'coverage-text') {
+ if ($log->hasAttribute('showUncoveredFiles')) {
+ $result['coverageTextShowUncoveredFiles'] = $this->getBoolean(
+ (string) $log->getAttribute('showUncoveredFiles'),
+ false
+ );
+ }
+ if ($log->hasAttribute('showOnlySummary')) {
+ $result['coverageTextShowOnlySummary'] = $this->getBoolean(
+ (string) $log->getAttribute('showOnlySummary'),
+ false
+ );
+ }
+ }
+
+ $result[$type] = $target;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the PHP configuration.
+ *
+ * @return array
+ */
+ public function getPHPConfiguration()
+ {
+ $result = [
+ 'include_path' => [],
+ 'ini' => [],
+ 'const' => [],
+ 'var' => [],
+ 'env' => [],
+ 'post' => [],
+ 'get' => [],
+ 'cookie' => [],
+ 'server' => [],
+ 'files' => [],
+ 'request' => []
+ ];
+
+ foreach ($this->xpath->query('php/includePath') as $includePath) {
+ $path = (string) $includePath->textContent;
+ if ($path) {
+ $result['include_path'][] = $this->toAbsolutePath($path);
+ }
+ }
+
+ foreach ($this->xpath->query('php/ini') as $ini) {
+ $name = (string) $ini->getAttribute('name');
+ $value = (string) $ini->getAttribute('value');
+
+ $result['ini'][$name] = $value;
+ }
+
+ foreach ($this->xpath->query('php/const') as $const) {
+ $name = (string) $const->getAttribute('name');
+ $value = (string) $const->getAttribute('value');
+
+ $result['const'][$name] = $this->getBoolean($value, $value);
+ }
+
+ foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
+ foreach ($this->xpath->query('php/' . $array) as $var) {
+ $name = (string) $var->getAttribute('name');
+ $value = (string) $var->getAttribute('value');
+
+ $result[$array][$name] = $this->getBoolean($value, $value);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Handles the PHP configuration.
+ */
+ public function handlePHPConfiguration()
+ {
+ $configuration = $this->getPHPConfiguration();
+
+ if (! empty($configuration['include_path'])) {
+ ini_set(
+ 'include_path',
+ implode(PATH_SEPARATOR, $configuration['include_path']) .
+ PATH_SEPARATOR .
+ ini_get('include_path')
+ );
+ }
+
+ foreach ($configuration['ini'] as $name => $value) {
+ if (defined($value)) {
+ $value = constant($value);
+ }
+
+ ini_set($name, $value);
+ }
+
+ foreach ($configuration['const'] as $name => $value) {
+ if (!defined($name)) {
+ define($name, $value);
+ }
+ }
+
+ foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) {
+ // See https://github.com/sebastianbergmann/phpunit/issues/277
+ switch ($array) {
+ case 'var':
+ $target = &$GLOBALS;
+ break;
+
+ case 'server':
+ $target = &$_SERVER;
+ break;
+
+ default:
+ $target = &$GLOBALS['_' . strtoupper($array)];
+ break;
+ }
+
+ foreach ($configuration[$array] as $name => $value) {
+ $target[$name] = $value;
+ }
+ }
+
+ foreach ($configuration['env'] as $name => $value) {
+ if (false === getenv($name)) {
+ putenv("{$name}={$value}");
+ }
+ if (!isset($_ENV[$name])) {
+ $_ENV[$name] = $value;
+ }
+ }
+ }
+
+ /**
+ * Returns the PHPUnit configuration.
+ *
+ * @return array
+ */
+ public function getPHPUnitConfiguration()
+ {
+ $result = [];
+ $root = $this->document->documentElement;
+
+ if ($root->hasAttribute('cacheTokens')) {
+ $result['cacheTokens'] = $this->getBoolean(
+ (string) $root->getAttribute('cacheTokens'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('columns')) {
+ $columns = (string) $root->getAttribute('columns');
+
+ if ($columns == 'max') {
+ $result['columns'] = 'max';
+ } else {
+ $result['columns'] = $this->getInteger($columns, 80);
+ }
+ }
+
+ if ($root->hasAttribute('colors')) {
+ /* only allow boolean for compatibility with previous versions
+ 'always' only allowed from command line */
+ if ($this->getBoolean($root->getAttribute('colors'), false)) {
+ $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_AUTO;
+ } else {
+ $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_NEVER;
+ }
+ }
+
+ /*
+ * Issue #657
+ */
+ if ($root->hasAttribute('stderr')) {
+ $result['stderr'] = $this->getBoolean(
+ (string) $root->getAttribute('stderr'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('backupGlobals')) {
+ $result['backupGlobals'] = $this->getBoolean(
+ (string) $root->getAttribute('backupGlobals'),
+ true
+ );
+ }
+
+ if ($root->hasAttribute('backupStaticAttributes')) {
+ $result['backupStaticAttributes'] = $this->getBoolean(
+ (string) $root->getAttribute('backupStaticAttributes'),
+ false
+ );
+ }
+
+ if ($root->getAttribute('bootstrap')) {
+ $result['bootstrap'] = $this->toAbsolutePath(
+ (string) $root->getAttribute('bootstrap')
+ );
+ }
+
+ if ($root->hasAttribute('convertErrorsToExceptions')) {
+ $result['convertErrorsToExceptions'] = $this->getBoolean(
+ (string) $root->getAttribute('convertErrorsToExceptions'),
+ true
+ );
+ }
+
+ if ($root->hasAttribute('convertNoticesToExceptions')) {
+ $result['convertNoticesToExceptions'] = $this->getBoolean(
+ (string) $root->getAttribute('convertNoticesToExceptions'),
+ true
+ );
+ }
+
+ if ($root->hasAttribute('convertWarningsToExceptions')) {
+ $result['convertWarningsToExceptions'] = $this->getBoolean(
+ (string) $root->getAttribute('convertWarningsToExceptions'),
+ true
+ );
+ }
+
+ if ($root->hasAttribute('forceCoversAnnotation')) {
+ $result['forceCoversAnnotation'] = $this->getBoolean(
+ (string) $root->getAttribute('forceCoversAnnotation'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('disableCodeCoverageIgnore')) {
+ $result['disableCodeCoverageIgnore'] = $this->getBoolean(
+ (string) $root->getAttribute('disableCodeCoverageIgnore'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('processIsolation')) {
+ $result['processIsolation'] = $this->getBoolean(
+ (string) $root->getAttribute('processIsolation'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('stopOnError')) {
+ $result['stopOnError'] = $this->getBoolean(
+ (string) $root->getAttribute('stopOnError'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('stopOnFailure')) {
+ $result['stopOnFailure'] = $this->getBoolean(
+ (string) $root->getAttribute('stopOnFailure'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('stopOnWarning')) {
+ $result['stopOnWarning'] = $this->getBoolean(
+ (string) $root->getAttribute('stopOnWarning'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('stopOnIncomplete')) {
+ $result['stopOnIncomplete'] = $this->getBoolean(
+ (string) $root->getAttribute('stopOnIncomplete'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('stopOnRisky')) {
+ $result['stopOnRisky'] = $this->getBoolean(
+ (string) $root->getAttribute('stopOnRisky'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('stopOnSkipped')) {
+ $result['stopOnSkipped'] = $this->getBoolean(
+ (string) $root->getAttribute('stopOnSkipped'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('failOnWarning')) {
+ $result['failOnWarning'] = $this->getBoolean(
+ (string) $root->getAttribute('failOnWarning'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('failOnRisky')) {
+ $result['failOnRisky'] = $this->getBoolean(
+ (string) $root->getAttribute('failOnRisky'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('testSuiteLoaderClass')) {
+ $result['testSuiteLoaderClass'] = (string) $root->getAttribute(
+ 'testSuiteLoaderClass'
+ );
+ }
+
+ if ($root->getAttribute('testSuiteLoaderFile')) {
+ $result['testSuiteLoaderFile'] = $this->toAbsolutePath(
+ (string) $root->getAttribute('testSuiteLoaderFile')
+ );
+ }
+
+ if ($root->hasAttribute('printerClass')) {
+ $result['printerClass'] = (string) $root->getAttribute(
+ 'printerClass'
+ );
+ }
+
+ if ($root->getAttribute('printerFile')) {
+ $result['printerFile'] = $this->toAbsolutePath(
+ (string) $root->getAttribute('printerFile')
+ );
+ }
+
+ if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) {
+ $result['beStrictAboutChangesToGlobalState'] = $this->getBoolean(
+ (string) $root->getAttribute('beStrictAboutChangesToGlobalState'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('beStrictAboutOutputDuringTests')) {
+ $result['disallowTestOutput'] = $this->getBoolean(
+ (string) $root->getAttribute('beStrictAboutOutputDuringTests'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) {
+ $result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean(
+ (string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) {
+ $result['reportUselessTests'] = $this->getBoolean(
+ (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) {
+ $result['disallowTodoAnnotatedTests'] = $this->getBoolean(
+ (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('beStrictAboutCoversAnnotation')) {
+ $result['strictCoverage'] = $this->getBoolean(
+ (string) $root->getAttribute('beStrictAboutCoversAnnotation'),
+ false
+ );
+ } elseif ($root->hasAttribute('checkForUnintentionallyCoveredCode')) {
+ $result['strictCoverage'] = $this->getBoolean(
+ (string) $root->getAttribute('checkForUnintentionallyCoveredCode'),
+ false
+ );
+
+ $result['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'] = true;
+ }
+
+ if ($root->hasAttribute('enforceTimeLimit')) {
+ $result['enforceTimeLimit'] = $this->getBoolean(
+ (string) $root->getAttribute('enforceTimeLimit'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('timeoutForSmallTests')) {
+ $result['timeoutForSmallTests'] = $this->getInteger(
+ (string) $root->getAttribute('timeoutForSmallTests'),
+ 1
+ );
+ }
+
+ if ($root->hasAttribute('timeoutForMediumTests')) {
+ $result['timeoutForMediumTests'] = $this->getInteger(
+ (string) $root->getAttribute('timeoutForMediumTests'),
+ 10
+ );
+ }
+
+ if ($root->hasAttribute('timeoutForLargeTests')) {
+ $result['timeoutForLargeTests'] = $this->getInteger(
+ (string) $root->getAttribute('timeoutForLargeTests'),
+ 60
+ );
+ }
+
+ if ($root->hasAttribute('reverseDefectList')) {
+ $result['reverseDefectList'] = $this->getBoolean(
+ (string) $root->getAttribute('reverseDefectList'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('verbose')) {
+ $result['verbose'] = $this->getBoolean(
+ (string) $root->getAttribute('verbose'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) {
+ $result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean(
+ (string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'),
+ false
+ );
+ }
+
+ if ($root->hasAttribute('extensionsDirectory')) {
+ $result['extensionsDirectory'] = $this->toAbsolutePath(
+ (string) $root->getAttribute(
+ 'extensionsDirectory'
+ )
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the test suite configuration.
+ *
+ * @return PHPUnit_Framework_TestSuite
+ */
+ public function getTestSuiteConfiguration($testSuiteFilter = null)
+ {
+ $testSuiteNodes = $this->xpath->query('testsuites/testsuite');
+
+ if ($testSuiteNodes->length == 0) {
+ $testSuiteNodes = $this->xpath->query('testsuite');
+ }
+
+ if ($testSuiteNodes->length == 1) {
+ return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter);
+ }
+
+ if ($testSuiteNodes->length > 1) {
+ $suite = new PHPUnit_Framework_TestSuite;
+
+ foreach ($testSuiteNodes as $testSuiteNode) {
+ $suite->addTestSuite(
+ $this->getTestSuite($testSuiteNode, $testSuiteFilter)
+ );
+ }
+
+ return $suite;
+ }
+ }
+
+ /**
+ * Returns the test suite names from the configuration.
+ *
+ * @return array
+ */
+ public function getTestSuiteNames()
+ {
+ $names = [];
+ $nodes = $this->xpath->query('*/testsuite');
+ foreach ($nodes as $node) {
+ $names[] = $node->getAttribute('name');
+ }
+
+ return $names;
+ }
+
+ /**
+ * @param DOMElement $testSuiteNode
+ *
+ * @return PHPUnit_Framework_TestSuite
+ */
+ protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter = null)
+ {
+ if ($testSuiteNode->hasAttribute('name')) {
+ $suite = new PHPUnit_Framework_TestSuite(
+ (string) $testSuiteNode->getAttribute('name')
+ );
+ } else {
+ $suite = new PHPUnit_Framework_TestSuite;
+ }
+
+ $exclude = [];
+
+ foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) {
+ $excludeFile = (string) $excludeNode->textContent;
+ if ($excludeFile) {
+ $exclude[] = $this->toAbsolutePath($excludeFile);
+ }
+ }
+
+ $fileIteratorFacade = new File_Iterator_Facade;
+
+ foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) {
+ if ($testSuiteFilter && $directoryNode->parentNode->getAttribute('name') != $testSuiteFilter) {
+ continue;
+ }
+
+ $directory = (string) $directoryNode->textContent;
+
+ if (empty($directory)) {
+ continue;
+ }
+
+ if ($directoryNode->hasAttribute('phpVersion')) {
+ $phpVersion = (string) $directoryNode->getAttribute('phpVersion');
+ } else {
+ $phpVersion = PHP_VERSION;
+ }
+
+ if ($directoryNode->hasAttribute('phpVersionOperator')) {
+ $phpVersionOperator = (string) $directoryNode->getAttribute('phpVersionOperator');
+ } else {
+ $phpVersionOperator = '>=';
+ }
+
+ if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) {
+ continue;
+ }
+
+ if ($directoryNode->hasAttribute('prefix')) {
+ $prefix = (string) $directoryNode->getAttribute('prefix');
+ } else {
+ $prefix = '';
+ }
+
+ if ($directoryNode->hasAttribute('suffix')) {
+ $suffix = (string) $directoryNode->getAttribute('suffix');
+ } else {
+ $suffix = 'Test.php';
+ }
+
+ $files = $fileIteratorFacade->getFilesAsArray(
+ $this->toAbsolutePath($directory),
+ $suffix,
+ $prefix,
+ $exclude
+ );
+ $suite->addTestFiles($files);
+ }
+
+ foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) {
+ if ($testSuiteFilter && $fileNode->parentNode->getAttribute('name') != $testSuiteFilter) {
+ continue;
+ }
+
+ $file = (string) $fileNode->textContent;
+
+ if (empty($file)) {
+ continue;
+ }
+
+ // Get the absolute path to the file
+ $file = $fileIteratorFacade->getFilesAsArray(
+ $this->toAbsolutePath($file)
+ );
+
+ if (!isset($file[0])) {
+ continue;
+ }
+
+ $file = $file[0];
+
+ if ($fileNode->hasAttribute('phpVersion')) {
+ $phpVersion = (string) $fileNode->getAttribute('phpVersion');
+ } else {
+ $phpVersion = PHP_VERSION;
+ }
+
+ if ($fileNode->hasAttribute('phpVersionOperator')) {
+ $phpVersionOperator = (string) $fileNode->getAttribute('phpVersionOperator');
+ } else {
+ $phpVersionOperator = '>=';
+ }
+
+ if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) {
+ continue;
+ }
+
+ $suite->addTestFile($file);
+ }
+
+ return $suite;
+ }
+
+ /**
+ * @param string $value
+ * @param bool $default
+ *
+ * @return bool
+ */
+ protected function getBoolean($value, $default)
+ {
+ if (strtolower($value) == 'false') {
+ return false;
+ } elseif (strtolower($value) == 'true') {
+ return true;
+ }
+
+ return $default;
+ }
+
+ /**
+ * @param string $value
+ * @param bool $default
+ *
+ * @return bool
+ */
+ protected function getInteger($value, $default)
+ {
+ if (is_numeric($value)) {
+ return (int) $value;
+ }
+
+ return $default;
+ }
+
+ /**
+ * @param string $query
+ *
+ * @return array
+ */
+ protected function readFilterDirectories($query)
+ {
+ $directories = [];
+
+ foreach ($this->xpath->query($query) as $directory) {
+ $directoryPath = (string) $directory->textContent;
+
+ if (!$directoryPath) {
+ continue;
+ }
+
+ if ($directory->hasAttribute('prefix')) {
+ $prefix = (string) $directory->getAttribute('prefix');
+ } else {
+ $prefix = '';
+ }
+
+ if ($directory->hasAttribute('suffix')) {
+ $suffix = (string) $directory->getAttribute('suffix');
+ } else {
+ $suffix = '.php';
+ }
+
+ if ($directory->hasAttribute('group')) {
+ $group = (string) $directory->getAttribute('group');
+ } else {
+ $group = 'DEFAULT';
+ }
+
+ $directories[] = [
+ 'path' => $this->toAbsolutePath($directoryPath),
+ 'prefix' => $prefix,
+ 'suffix' => $suffix,
+ 'group' => $group
+ ];
+ }
+
+ return $directories;
+ }
+
+ /**
+ * @param string $query
+ *
+ * @return array
+ */
+ protected function readFilterFiles($query)
+ {
+ $files = [];
+
+ foreach ($this->xpath->query($query) as $file) {
+ $filePath = (string) $file->textContent;
+
+ if ($filePath) {
+ $files[] = $this->toAbsolutePath($filePath);
+ }
+ }
+
+ return $files;
+ }
+
+ /**
+ * @param string $path
+ * @param bool $useIncludePath
+ *
+ * @return string
+ */
+ protected function toAbsolutePath($path, $useIncludePath = false)
+ {
+ $path = trim($path);
+
+ if ($path[0] === '/') {
+ return $path;
+ }
+
+ // Matches the following on Windows:
+ // - \\NetworkComputer\Path
+ // - \\.\D:
+ // - \\.\c:
+ // - C:\Windows
+ // - C:\windows
+ // - C:/windows
+ // - c:/windows
+ if (defined('PHP_WINDOWS_VERSION_BUILD') &&
+ ($path[0] === '\\' ||
+ (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))))) {
+ return $path;
+ }
+
+ // Stream
+ if (strpos($path, '://') !== false) {
+ return $path;
+ }
+
+ $file = dirname($this->filename) . DIRECTORY_SEPARATOR . $path;
+
+ if ($useIncludePath && !file_exists($file)) {
+ $includePathFile = stream_resolve_include_path($path);
+
+ if ($includePathFile) {
+ $file = $includePathFile;
+ }
+ }
+
+ return $file;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Util_ConfigurationGenerator
+{
+ /**
+ * @var string
+ */
+ private $defaultTemplate = <<<EOT
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/{phpunit_version}/phpunit.xsd"
+ bootstrap="{bootstrap_script}"
+ backupGlobals="false"
+ beStrictAboutCoversAnnotation="true"
+ beStrictAboutOutputDuringTests="true"
+ beStrictAboutTestsThatDoNotTestAnything="true"
+ beStrictAboutTodoAnnotatedTests="true"
+ verbose="true">
+ <testsuite>
+ <directory suffix="Test.php">{tests_directory}</directory>
+ </testsuite>
+
+ <filter>
+ <whitelist processUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">{src_directory}</directory>
+ </whitelist>
+ </filter>
+</phpunit>
+
+EOT;
+
+ /**
+ * @param string $phpunitVersion
+ * @param string $bootstrapScript
+ * @param string $testsDirectory
+ * @param string $srcDirectory
+ *
+ * @return string
+ */
+ public function generateDefaultConfiguration($phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory)
+ {
+ return str_replace(
+ [
+ '{phpunit_version}',
+ '{bootstrap_script}',
+ '{tests_directory}',
+ '{src_directory}'
+ ],
+ [
+ $phpunitVersion,
+ $bootstrapScript,
+ $testsDirectory,
+ $srcDirectory
+ ],
+ $this->defaultTemplate
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+// Workaround for http://bugs.php.net/bug.php?id=47987,
+// see https://github.com/sebastianbergmann/phpunit/issues#issue/125 for details
+// Use dirname(__DIR__) instead of using /../ because of https://github.com/facebook/hhvm/issues/5215
+require_once dirname(__DIR__) . '/Framework/Error.php';
+require_once dirname(__DIR__) . '/Framework/Error/Notice.php';
+require_once dirname(__DIR__) . '/Framework/Error/Warning.php';
+require_once dirname(__DIR__) . '/Framework/Error/Deprecated.php';
+
+/**
+ * Error handler that converts PHP errors and warnings to exceptions.
+ */
+class PHPUnit_Util_ErrorHandler
+{
+ protected static $errorStack = [];
+
+ /**
+ * Returns the error stack.
+ *
+ * @return array
+ */
+ public static function getErrorStack()
+ {
+ return self::$errorStack;
+ }
+
+ /**
+ * @param int $errno
+ * @param string $errstr
+ * @param string $errfile
+ * @param int $errline
+ *
+ * @throws PHPUnit_Framework_Error
+ */
+ public static function handleError($errno, $errstr, $errfile, $errline)
+ {
+ if (!($errno & error_reporting())) {
+ return false;
+ }
+
+ self::$errorStack[] = [$errno, $errstr, $errfile, $errline];
+
+ $trace = debug_backtrace(false);
+ array_shift($trace);
+
+ foreach ($trace as $frame) {
+ if ($frame['function'] == '__toString') {
+ return false;
+ }
+ }
+
+ if ($errno == E_NOTICE || $errno == E_USER_NOTICE || $errno == E_STRICT) {
+ if (PHPUnit_Framework_Error_Notice::$enabled !== true) {
+ return false;
+ }
+
+ $exception = 'PHPUnit_Framework_Error_Notice';
+ } elseif ($errno == E_WARNING || $errno == E_USER_WARNING) {
+ if (PHPUnit_Framework_Error_Warning::$enabled !== true) {
+ return false;
+ }
+
+ $exception = 'PHPUnit_Framework_Error_Warning';
+ } elseif ($errno == E_DEPRECATED || $errno == E_USER_DEPRECATED) {
+ if (PHPUnit_Framework_Error_Deprecated::$enabled !== true) {
+ return false;
+ }
+
+ $exception = 'PHPUnit_Framework_Error_Deprecated';
+ } else {
+ $exception = 'PHPUnit_Framework_Error';
+ }
+
+ throw new $exception($errstr, $errno, $errfile, $errline);
+ }
+
+ /**
+ * Registers an error handler and returns a function that will restore
+ * the previous handler when invoked
+ *
+ * @param int $severity PHP predefined error constant
+ *
+ * @throws Exception if event of specified severity is emitted
+ */
+ public static function handleErrorOnce($severity = E_WARNING)
+ {
+ $terminator = function () {
+ static $expired = false;
+ if (!$expired) {
+ $expired = true;
+ // cleans temporary error handler
+ return restore_error_handler();
+ }
+ };
+
+ set_error_handler(function ($errno, $errstr) use ($severity) {
+ if ($errno === $severity) {
+ return;
+ }
+
+ return false;
+ });
+
+ return $terminator;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Utility methods to load PHP sourcefiles.
+ */
+class PHPUnit_Util_Fileloader
+{
+ /**
+ * Checks if a PHP sourcefile is readable.
+ * The sourcefile is loaded through the load() method.
+ *
+ * @param string $filename
+ *
+ * @return string
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public static function checkAndLoad($filename)
+ {
+ $includePathFilename = stream_resolve_include_path($filename);
+
+ if (!$includePathFilename || !is_readable($includePathFilename)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf('Cannot open file "%s".' . "\n", $filename)
+ );
+ }
+
+ self::load($includePathFilename);
+
+ return $includePathFilename;
+ }
+
+ /**
+ * Loads a PHP sourcefile.
+ *
+ * @param string $filename
+ *
+ * @return mixed
+ */
+ public static function load($filename)
+ {
+ $oldVariableNames = array_keys(get_defined_vars());
+
+ include_once $filename;
+
+ $newVariables = get_defined_vars();
+ $newVariableNames = array_diff(
+ array_keys($newVariables),
+ $oldVariableNames
+ );
+
+ foreach ($newVariableNames as $variableName) {
+ if ($variableName != 'oldVariableNames') {
+ $GLOBALS[$variableName] = $newVariables[$variableName];
+ }
+ }
+
+ return $filename;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Filesystem helpers.
+ */
+class PHPUnit_Util_Filesystem
+{
+ /**
+ * @var array
+ */
+ protected static $buffer = [];
+
+ /**
+ * Maps class names to source file names:
+ * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php
+ * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php
+ *
+ * @param string $className
+ *
+ * @return string
+ */
+ public static function classNameToFilename($className)
+ {
+ return str_replace(
+ ['_', '\\'],
+ DIRECTORY_SEPARATOR,
+ $className
+ ) . '.php';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Utility class for code filtering.
+ */
+class PHPUnit_Util_Filter
+{
+ /**
+ * Filters stack frames from PHPUnit classes.
+ *
+ * @param Exception $e
+ * @param bool $asString
+ *
+ * @return string
+ */
+ public static function getFilteredStacktrace($e, $asString = true)
+ {
+ $prefix = false;
+ $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']);
+
+ if (defined('__PHPUNIT_PHAR_ROOT__')) {
+ $prefix = __PHPUNIT_PHAR_ROOT__;
+ }
+
+ if ($asString === true) {
+ $filteredStacktrace = '';
+ } else {
+ $filteredStacktrace = [];
+ }
+
+ if ($e instanceof PHPUnit_Framework_SyntheticError) {
+ $eTrace = $e->getSyntheticTrace();
+ $eFile = $e->getSyntheticFile();
+ $eLine = $e->getSyntheticLine();
+ } elseif ($e instanceof PHPUnit_Framework_Exception) {
+ $eTrace = $e->getSerializableTrace();
+ $eFile = $e->getFile();
+ $eLine = $e->getLine();
+ } else {
+ if ($e->getPrevious()) {
+ $e = $e->getPrevious();
+ }
+ $eTrace = $e->getTrace();
+ $eFile = $e->getFile();
+ $eLine = $e->getLine();
+ }
+
+ if (!self::frameExists($eTrace, $eFile, $eLine)) {
+ array_unshift(
+ $eTrace,
+ ['file' => $eFile, 'line' => $eLine]
+ );
+ }
+
+ $blacklist = new PHPUnit_Util_Blacklist;
+
+ foreach ($eTrace as $frame) {
+ if (isset($frame['file']) && is_file($frame['file']) &&
+ !$blacklist->isBlacklisted($frame['file']) &&
+ ($prefix === false || strpos($frame['file'], $prefix) !== 0) &&
+ $frame['file'] !== $script) {
+ if ($asString === true) {
+ $filteredStacktrace .= sprintf(
+ "%s:%s\n",
+ $frame['file'],
+ isset($frame['line']) ? $frame['line'] : '?'
+ );
+ } else {
+ $filteredStacktrace[] = $frame;
+ }
+ }
+ }
+
+ return $filteredStacktrace;
+ }
+
+ /**
+ * @param array $trace
+ * @param string $file
+ * @param int $line
+ *
+ * @return bool
+ */
+ private static function frameExists(array $trace, $file, $line)
+ {
+ foreach ($trace as $frame) {
+ if (isset($frame['file']) && $frame['file'] == $file &&
+ isset($frame['line']) && $frame['line'] == $line) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Command-line options parsing class.
+ */
+class PHPUnit_Util_Getopt
+{
+ public static function getopt(array $args, $short_options, $long_options = null)
+ {
+ if (empty($args)) {
+ return [[], []];
+ }
+
+ $opts = [];
+ $non_opts = [];
+
+ if ($long_options) {
+ sort($long_options);
+ }
+
+ if (isset($args[0][0]) && $args[0][0] != '-') {
+ array_shift($args);
+ }
+
+ reset($args);
+
+ while (list($i, $arg) = @each($args)) {
+ if ($arg == '') {
+ continue;
+ }
+
+ if ($arg == '--') {
+ $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
+ break;
+ }
+
+ if ($arg[0] != '-' || (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) {
+ $non_opts[] = $args[$i];
+ continue;
+ } elseif (strlen($arg) > 1 && $arg[1] == '-') {
+ self::parseLongOption(
+ substr($arg, 2),
+ $long_options,
+ $opts,
+ $args
+ );
+ } else {
+ self::parseShortOption(
+ substr($arg, 1),
+ $short_options,
+ $opts,
+ $args
+ );
+ }
+ }
+
+ return [$opts, $non_opts];
+ }
+
+ protected static function parseShortOption($arg, $short_options, &$opts, &$args)
+ {
+ $argLen = strlen($arg);
+
+ for ($i = 0; $i < $argLen; $i++) {
+ $opt = $arg[$i];
+ $opt_arg = null;
+
+ if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') {
+ throw new PHPUnit_Framework_Exception(
+ "unrecognized option -- $opt"
+ );
+ }
+
+ if (strlen($spec) > 1 && $spec[1] == ':') {
+ if (strlen($spec) > 2 && $spec[2] == ':') {
+ if ($i + 1 < $argLen) {
+ $opts[] = [$opt, substr($arg, $i + 1)];
+ break;
+ }
+ } else {
+ if ($i + 1 < $argLen) {
+ $opts[] = [$opt, substr($arg, $i + 1)];
+ break;
+ } elseif (list(, $opt_arg) = @each($args)) {
+ } else {
+ throw new PHPUnit_Framework_Exception(
+ "option requires an argument -- $opt"
+ );
+ }
+ }
+ }
+
+ $opts[] = [$opt, $opt_arg];
+ }
+ }
+
+ protected static function parseLongOption($arg, $long_options, &$opts, &$args)
+ {
+ $count = count($long_options);
+ $list = explode('=', $arg);
+ $opt = $list[0];
+ $opt_arg = null;
+
+ if (count($list) > 1) {
+ $opt_arg = $list[1];
+ }
+
+ $opt_len = strlen($opt);
+
+ for ($i = 0; $i < $count; $i++) {
+ $long_opt = $long_options[$i];
+ $opt_start = substr($long_opt, 0, $opt_len);
+
+ if ($opt_start != $opt) {
+ continue;
+ }
+
+ $opt_rest = substr($long_opt, $opt_len);
+
+ if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < $count &&
+ $opt == substr($long_options[$i + 1], 0, $opt_len)) {
+ throw new PHPUnit_Framework_Exception(
+ "option --$opt is ambiguous"
+ );
+ }
+
+ if (substr($long_opt, -1) == '=') {
+ if (substr($long_opt, -2) != '==') {
+ if (!strlen($opt_arg) && !(list(, $opt_arg) = @each($args))) {
+ throw new PHPUnit_Framework_Exception(
+ "option --$opt requires an argument"
+ );
+ }
+ }
+ } elseif ($opt_arg) {
+ throw new PHPUnit_Framework_Exception(
+ "option --$opt doesn't allow an argument"
+ );
+ }
+
+ $full_option = '--' . preg_replace('/={1,2}$/', '', $long_opt);
+ $opts[] = [$full_option, $opt_arg];
+
+ return;
+ }
+
+ throw new PHPUnit_Framework_Exception("unrecognized option --$opt");
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Util_GlobalState
+{
+ /**
+ * @var array
+ */
+ protected static $superGlobalArrays = [
+ '_ENV',
+ '_POST',
+ '_GET',
+ '_COOKIE',
+ '_SERVER',
+ '_FILES',
+ '_REQUEST'
+ ];
+
+ /**
+ * @var array
+ */
+ protected static $superGlobalArraysLong = [
+ 'HTTP_ENV_VARS',
+ 'HTTP_POST_VARS',
+ 'HTTP_GET_VARS',
+ 'HTTP_COOKIE_VARS',
+ 'HTTP_SERVER_VARS',
+ 'HTTP_POST_FILES'
+ ];
+
+ /**
+ * @return string
+ */
+ public static function getIncludedFilesAsString()
+ {
+ return static::processIncludedFilesAsString(get_included_files());
+ }
+
+ /**
+ * @param array $files
+ *
+ * @return string
+ */
+ public static function processIncludedFilesAsString(array $files)
+ {
+ $blacklist = new PHPUnit_Util_Blacklist;
+ $prefix = false;
+ $result = '';
+
+ if (defined('__PHPUNIT_PHAR__')) {
+ $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/';
+ }
+
+ for ($i = count($files) - 1; $i > 0; $i--) {
+ $file = $files[$i];
+
+ if ($prefix !== false && strpos($file, $prefix) === 0) {
+ continue;
+ }
+
+ // Skip virtual file system protocols
+ if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) {
+ continue;
+ }
+
+ if (!$blacklist->isBlacklisted($file) && is_file($file)) {
+ $result = 'require_once \'' . $file . "';\n" . $result;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getIniSettingsAsString()
+ {
+ $result = '';
+ $iniSettings = ini_get_all(null, false);
+
+ foreach ($iniSettings as $key => $value) {
+ $result .= sprintf(
+ '@ini_set(%s, %s);' . "\n",
+ self::exportVariable($key),
+ self::exportVariable($value)
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getConstantsAsString()
+ {
+ $constants = get_defined_constants(true);
+ $result = '';
+
+ if (isset($constants['user'])) {
+ foreach ($constants['user'] as $name => $value) {
+ $result .= sprintf(
+ 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n",
+ $name,
+ $name,
+ self::exportVariable($value)
+ );
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getGlobalsAsString()
+ {
+ $result = '';
+ $superGlobalArrays = self::getSuperGlobalArrays();
+
+ foreach ($superGlobalArrays as $superGlobalArray) {
+ if (isset($GLOBALS[$superGlobalArray]) &&
+ is_array($GLOBALS[$superGlobalArray])) {
+ foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) {
+ if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) {
+ continue;
+ }
+
+ $result .= sprintf(
+ '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n",
+ $superGlobalArray,
+ $key,
+ self::exportVariable($GLOBALS[$superGlobalArray][$key])
+ );
+ }
+ }
+ }
+
+ $blacklist = $superGlobalArrays;
+ $blacklist[] = 'GLOBALS';
+
+ foreach (array_keys($GLOBALS) as $key) {
+ if (!in_array($key, $blacklist) && !$GLOBALS[$key] instanceof Closure) {
+ $result .= sprintf(
+ '$GLOBALS[\'%s\'] = %s;' . "\n",
+ $key,
+ self::exportVariable($GLOBALS[$key])
+ );
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return array
+ */
+ protected static function getSuperGlobalArrays()
+ {
+ if (ini_get('register_long_arrays') == '1') {
+ return array_merge(
+ self::$superGlobalArrays,
+ self::$superGlobalArraysLong
+ );
+ } else {
+ return self::$superGlobalArrays;
+ }
+ }
+
+ protected static function exportVariable($variable)
+ {
+ if (is_scalar($variable) || is_null($variable) ||
+ (is_array($variable) && self::arrayOnlyContainsScalars($variable))) {
+ return var_export($variable, true);
+ }
+
+ return 'unserialize(' .
+ var_export(serialize($variable), true) .
+ ')';
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return bool
+ */
+ protected static function arrayOnlyContainsScalars(array $array)
+ {
+ $result = true;
+
+ foreach ($array as $element) {
+ if (is_array($element)) {
+ $result = self::arrayOnlyContainsScalars($element);
+ } elseif (!is_scalar($element) && !is_null($element)) {
+ $result = false;
+ }
+
+ if ($result === false) {
+ break;
+ }
+ }
+
+ return $result;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Factory for PHPUnit_Framework_Exception objects that are used to describe
+ * invalid arguments passed to a function or method.
+ */
+class PHPUnit_Util_InvalidArgumentHelper
+{
+ /**
+ * @param int $argument
+ * @param string $type
+ * @param mixed $value
+ *
+ * @return PHPUnit_Framework_Exception
+ */
+ public static function factory($argument, $type, $value = null)
+ {
+ $stack = debug_backtrace(false);
+
+ return new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Argument #%d%sof %s::%s() must be a %s',
+ $argument,
+ $value !== null ? ' (' . gettype($value) . '#' . $value . ')' : ' (No Value) ',
+ $stack[1]['class'],
+ $stack[1]['function'],
+ $type
+ )
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TestListener that generates JSON messages.
+ */
+class PHPUnit_Util_Log_JSON extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
+{
+ /**
+ * @var string
+ */
+ protected $currentTestSuiteName = '';
+
+ /**
+ * @var string
+ */
+ protected $currentTestName = '';
+
+ /**
+ * @var bool
+ */
+ protected $currentTestPass = true;
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeCase(
+ 'error',
+ $time,
+ PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
+ PHPUnit_Framework_TestFailure::exceptionToString($e),
+ $test
+ );
+
+ $this->currentTestPass = false;
+ }
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ $this->writeCase(
+ 'warning',
+ $time,
+ PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
+ PHPUnit_Framework_TestFailure::exceptionToString($e),
+ $test
+ );
+
+ $this->currentTestPass = false;
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ $this->writeCase(
+ 'fail',
+ $time,
+ PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
+ PHPUnit_Framework_TestFailure::exceptionToString($e),
+ $test
+ );
+
+ $this->currentTestPass = false;
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeCase(
+ 'error',
+ $time,
+ PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
+ 'Incomplete Test: ' . $e->getMessage(),
+ $test
+ );
+
+ $this->currentTestPass = false;
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeCase(
+ 'error',
+ $time,
+ PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
+ 'Risky Test: ' . $e->getMessage(),
+ $test
+ );
+
+ $this->currentTestPass = false;
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeCase(
+ 'error',
+ $time,
+ PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
+ 'Skipped Test: ' . $e->getMessage(),
+ $test
+ );
+
+ $this->currentTestPass = false;
+ }
+
+ /**
+ * A testsuite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ $this->currentTestSuiteName = $suite->getName();
+ $this->currentTestName = '';
+
+ $this->write(
+ [
+ 'event' => 'suiteStart',
+ 'suite' => $this->currentTestSuiteName,
+ 'tests' => count($suite)
+ ]
+ );
+ }
+
+ /**
+ * A testsuite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ $this->currentTestSuiteName = '';
+ $this->currentTestName = '';
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ $this->currentTestName = PHPUnit_Util_Test::describe($test);
+ $this->currentTestPass = true;
+
+ $this->write(
+ [
+ 'event' => 'testStart',
+ 'suite' => $this->currentTestSuiteName,
+ 'test' => $this->currentTestName
+ ]
+ );
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ if ($this->currentTestPass) {
+ $this->writeCase('pass', $time, [], '', $test);
+ }
+ }
+
+ /**
+ * @param string $status
+ * @param float $time
+ * @param array $trace
+ * @param string $message
+ * @param PHPUnit_Framework_TestCase|null $test
+ */
+ protected function writeCase($status, $time, array $trace = [], $message = '', $test = null)
+ {
+ $output = '';
+ // take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput
+ if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) {
+ $output = $test->getActualOutput();
+ }
+ $this->write(
+ [
+ 'event' => 'test',
+ 'suite' => $this->currentTestSuiteName,
+ 'test' => $this->currentTestName,
+ 'status' => $status,
+ 'time' => $time,
+ 'trace' => $trace,
+ 'message' => PHPUnit_Util_String::convertToUtf8($message),
+ 'output' => $output,
+ ]
+ );
+ }
+
+ /**
+ * @param string $buffer
+ */
+ public function write($buffer)
+ {
+ array_walk_recursive($buffer, function (&$input) {
+ if (is_string($input)) {
+ $input = PHPUnit_Util_String::convertToUtf8($input);
+ }
+ });
+
+ parent::write(json_encode($buffer, JSON_PRETTY_PRINT));
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TestListener that generates a logfile of the test execution in XML markup.
+ *
+ * The XML markup used is the same as the one that is used by the JUnit Ant task.
+ */
+class PHPUnit_Util_Log_JUnit extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
+{
+ /**
+ * @var DOMDocument
+ */
+ protected $document;
+
+ /**
+ * @var DOMElement
+ */
+ protected $root;
+
+ /**
+ * @var bool
+ */
+ protected $logIncompleteSkipped = false;
+
+ /**
+ * @var bool
+ */
+ protected $writeDocument = true;
+
+ /**
+ * @var DOMElement[]
+ */
+ protected $testSuites = [];
+
+ /**
+ * @var int[]
+ */
+ protected $testSuiteTests = [0];
+
+ /**
+ * @var int[]
+ */
+ protected $testSuiteAssertions = [0];
+
+ /**
+ * @var int[]
+ */
+ protected $testSuiteErrors = [0];
+
+ /**
+ * @var int[]
+ */
+ protected $testSuiteFailures = [0];
+
+ /**
+ * @var int[]
+ */
+ protected $testSuiteTimes = [0];
+
+ /**
+ * @var int
+ */
+ protected $testSuiteLevel = 0;
+
+ /**
+ * @var DOMElement
+ */
+ protected $currentTestCase = null;
+
+ /**
+ * @var bool
+ */
+ protected $attachCurrentTestCase = true;
+
+ /**
+ * Constructor.
+ *
+ * @param mixed $out
+ * @param bool $logIncompleteSkipped
+ */
+ public function __construct($out = null, $logIncompleteSkipped = false)
+ {
+ $this->document = new DOMDocument('1.0', 'UTF-8');
+ $this->document->formatOutput = true;
+
+ $this->root = $this->document->createElement('testsuites');
+ $this->document->appendChild($this->root);
+
+ parent::__construct($out);
+
+ $this->logIncompleteSkipped = $logIncompleteSkipped;
+ }
+
+ /**
+ * Flush buffer and close output.
+ */
+ public function flush()
+ {
+ if ($this->writeDocument === true) {
+ $this->write($this->getXML());
+ }
+
+ parent::flush();
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->doAddFault($test, $e, $time, 'error');
+ $this->testSuiteErrors[$this->testSuiteLevel]++;
+ }
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ if (!$this->logIncompleteSkipped) {
+ return;
+ }
+
+ $this->doAddFault($test, $e, $time, 'warning');
+ $this->testSuiteFailures[$this->testSuiteLevel]++;
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ $this->doAddFault($test, $e, $time, 'failure');
+ $this->testSuiteFailures[$this->testSuiteLevel]++;
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
+ $error = $this->document->createElement(
+ 'error',
+ PHPUnit_Util_XML::prepareString(
+ "Incomplete Test\n" .
+ PHPUnit_Util_Filter::getFilteredStacktrace($e)
+ )
+ );
+
+ $error->setAttribute('type', get_class($e));
+
+ $this->currentTestCase->appendChild($error);
+
+ $this->testSuiteErrors[$this->testSuiteLevel]++;
+ } else {
+ $this->attachCurrentTestCase = false;
+ }
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
+ $error = $this->document->createElement(
+ 'error',
+ PHPUnit_Util_XML::prepareString(
+ "Risky Test\n" .
+ PHPUnit_Util_Filter::getFilteredStacktrace($e)
+ )
+ );
+
+ $error->setAttribute('type', get_class($e));
+
+ $this->currentTestCase->appendChild($error);
+
+ $this->testSuiteErrors[$this->testSuiteLevel]++;
+ } else {
+ $this->attachCurrentTestCase = false;
+ }
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
+ $error = $this->document->createElement(
+ 'error',
+ PHPUnit_Util_XML::prepareString(
+ "Skipped Test\n" .
+ PHPUnit_Util_Filter::getFilteredStacktrace($e)
+ )
+ );
+
+ $error->setAttribute('type', get_class($e));
+
+ $this->currentTestCase->appendChild($error);
+
+ $this->testSuiteErrors[$this->testSuiteLevel]++;
+ } else {
+ $this->attachCurrentTestCase = false;
+ }
+ }
+
+ /**
+ * A testsuite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ $testSuite = $this->document->createElement('testsuite');
+ $testSuite->setAttribute('name', $suite->getName());
+
+ if (class_exists($suite->getName(), false)) {
+ try {
+ $class = new ReflectionClass($suite->getName());
+
+ $testSuite->setAttribute('file', $class->getFileName());
+ } catch (ReflectionException $e) {
+ }
+ }
+
+ if ($this->testSuiteLevel > 0) {
+ $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
+ } else {
+ $this->root->appendChild($testSuite);
+ }
+
+ $this->testSuiteLevel++;
+ $this->testSuites[$this->testSuiteLevel] = $testSuite;
+ $this->testSuiteTests[$this->testSuiteLevel] = 0;
+ $this->testSuiteAssertions[$this->testSuiteLevel] = 0;
+ $this->testSuiteErrors[$this->testSuiteLevel] = 0;
+ $this->testSuiteFailures[$this->testSuiteLevel] = 0;
+ $this->testSuiteTimes[$this->testSuiteLevel] = 0;
+ }
+
+ /**
+ * A testsuite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ $this->testSuites[$this->testSuiteLevel]->setAttribute(
+ 'tests',
+ $this->testSuiteTests[$this->testSuiteLevel]
+ );
+
+ $this->testSuites[$this->testSuiteLevel]->setAttribute(
+ 'assertions',
+ $this->testSuiteAssertions[$this->testSuiteLevel]
+ );
+
+ $this->testSuites[$this->testSuiteLevel]->setAttribute(
+ 'failures',
+ $this->testSuiteFailures[$this->testSuiteLevel]
+ );
+
+ $this->testSuites[$this->testSuiteLevel]->setAttribute(
+ 'errors',
+ $this->testSuiteErrors[$this->testSuiteLevel]
+ );
+
+ $this->testSuites[$this->testSuiteLevel]->setAttribute(
+ 'time',
+ sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])
+ );
+
+ if ($this->testSuiteLevel > 1) {
+ $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
+ $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
+ $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
+ $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
+ $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
+ }
+
+ $this->testSuiteLevel--;
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ $testCase = $this->document->createElement('testcase');
+ $testCase->setAttribute('name', $test->getName());
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $class = new ReflectionClass($test);
+ $methodName = $test->getName();
+
+ if ($class->hasMethod($methodName)) {
+ $method = $class->getMethod($test->getName());
+
+ $testCase->setAttribute('class', $class->getName());
+ $testCase->setAttribute('file', $class->getFileName());
+ $testCase->setAttribute('line', $method->getStartLine());
+ }
+ }
+
+ $this->currentTestCase = $testCase;
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ if ($this->attachCurrentTestCase) {
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $numAssertions = $test->getNumAssertions();
+ $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
+
+ $this->currentTestCase->setAttribute(
+ 'assertions',
+ $numAssertions
+ );
+ }
+
+ $this->currentTestCase->setAttribute(
+ 'time',
+ sprintf('%F', $time)
+ );
+
+ $this->testSuites[$this->testSuiteLevel]->appendChild(
+ $this->currentTestCase
+ );
+
+ $this->testSuiteTests[$this->testSuiteLevel]++;
+ $this->testSuiteTimes[$this->testSuiteLevel] += $time;
+
+ if (method_exists($test, 'hasOutput') && $test->hasOutput()) {
+ $systemOut = $this->document->createElement('system-out');
+ $systemOut->appendChild(
+ $this->document->createTextNode($test->getActualOutput())
+ );
+ $this->currentTestCase->appendChild($systemOut);
+ }
+ }
+
+ $this->attachCurrentTestCase = true;
+ $this->currentTestCase = null;
+ }
+
+ /**
+ * Returns the XML as a string.
+ *
+ * @return string
+ */
+ public function getXML()
+ {
+ return $this->document->saveXML();
+ }
+
+ /**
+ * Enables or disables the writing of the document
+ * in flush().
+ *
+ * This is a "hack" needed for the integration of
+ * PHPUnit with Phing.
+ *
+ * @return string
+ */
+ public function setWriteDocument($flag)
+ {
+ if (is_bool($flag)) {
+ $this->writeDocument = $flag;
+ }
+ }
+
+ /**
+ * Method which generalizes addError() and addFailure()
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ * @param string $type
+ */
+ private function doAddFault(PHPUnit_Framework_Test $test, Exception $e, $time, $type)
+ {
+ if ($this->currentTestCase === null) {
+ return;
+ }
+
+ if ($test instanceof PHPUnit_Framework_SelfDescribing) {
+ $buffer = $test->toString() . PHP_EOL;
+ } else {
+ $buffer = '';
+ }
+
+ $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . PHP_EOL .
+ PHPUnit_Util_Filter::getFilteredStacktrace($e);
+
+ $fault = $this->document->createElement(
+ $type,
+ PHPUnit_Util_XML::prepareString($buffer)
+ );
+
+ if ($e instanceof PHPUnit_Framework_ExceptionWrapper) {
+ $fault->setAttribute('type', $e->getClassName());
+ } else {
+ $fault->setAttribute('type', get_class($e));
+ }
+
+ $this->currentTestCase->appendChild($fault);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A TestListener that generates a logfile of the
+ * test execution using the Test Anything Protocol (TAP).
+ */
+class PHPUnit_Util_Log_TAP extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
+{
+ /**
+ * @var int
+ */
+ protected $testNumber = 0;
+
+ /**
+ * @var int
+ */
+ protected $testSuiteLevel = 0;
+
+ /**
+ * @var bool
+ */
+ protected $testSuccessful = true;
+
+ /**
+ * Constructor.
+ *
+ * @param mixed $out
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($out = null)
+ {
+ parent::__construct($out);
+ $this->write("TAP version 13\n");
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeNotOk($test, 'Error');
+ }
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ $this->writeNotOk($test, 'Warning');
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ $this->writeNotOk($test, 'Failure');
+
+ $message = explode(
+ "\n",
+ PHPUnit_Framework_TestFailure::exceptionToString($e)
+ );
+
+ $diagnostic = [
+ 'message' => $message[0],
+ 'severity' => 'fail'
+ ];
+
+ if ($e instanceof PHPUnit_Framework_ExpectationFailedException) {
+ $cf = $e->getComparisonFailure();
+
+ if ($cf !== null) {
+ $diagnostic['data'] = [
+ 'got' => $cf->getActual(),
+ 'expected' => $cf->getExpected()
+ ];
+ }
+ }
+
+ $yaml = new Symfony\Component\Yaml\Dumper;
+
+ $this->write(
+ sprintf(
+ " ---\n%s ...\n",
+ $yaml->dump($diagnostic, 2, 2)
+ )
+ );
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->writeNotOk($test, '', 'TODO Incomplete Test');
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->write(
+ sprintf(
+ "ok %d - # RISKY%s\n",
+ $this->testNumber,
+ $e->getMessage() != '' ? ' ' . $e->getMessage() : ''
+ )
+ );
+
+ $this->testSuccessful = false;
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->write(
+ sprintf(
+ "ok %d - # SKIP%s\n",
+ $this->testNumber,
+ $e->getMessage() != '' ? ' ' . $e->getMessage() : ''
+ )
+ );
+
+ $this->testSuccessful = false;
+ }
+
+ /**
+ * A testsuite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ $this->testSuiteLevel++;
+ }
+
+ /**
+ * A testsuite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ $this->testSuiteLevel--;
+
+ if ($this->testSuiteLevel == 0) {
+ $this->write(sprintf("1..%d\n", $this->testNumber));
+ }
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ $this->testNumber++;
+ $this->testSuccessful = true;
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ if ($this->testSuccessful === true) {
+ $this->write(
+ sprintf(
+ "ok %d - %s\n",
+ $this->testNumber,
+ PHPUnit_Util_Test::describe($test)
+ )
+ );
+ }
+
+ $this->writeDiagnostics($test);
+ }
+
+ /**
+ * @param PHPUnit_Framework_Test $test
+ * @param string $prefix
+ * @param string $directive
+ */
+ protected function writeNotOk(PHPUnit_Framework_Test $test, $prefix = '', $directive = '')
+ {
+ $this->write(
+ sprintf(
+ "not ok %d - %s%s%s\n",
+ $this->testNumber,
+ $prefix != '' ? $prefix . ': ' : '',
+ PHPUnit_Util_Test::describe($test),
+ $directive != '' ? ' # ' . $directive : ''
+ )
+ );
+
+ $this->testSuccessful = false;
+ }
+
+ /**
+ * @param PHPUnit_Framework_Test $test
+ */
+ private function writeDiagnostics(PHPUnit_Framework_Test $test)
+ {
+ if (!$test instanceof PHPUnit_Framework_TestCase) {
+ return;
+ }
+
+ if (!$test->hasOutput()) {
+ return;
+ }
+
+ foreach (explode("\n", trim($test->getActualOutput())) as $line) {
+ $this->write(
+ sprintf(
+ "# %s\n",
+ $line
+ )
+ );
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Comparator\ComparisonFailure;
+
+/**
+ * A TestListener that generates a logfile of the test execution using the
+ * TeamCity format (for use with PhpStorm, for instance).
+ */
+class PHPUnit_Util_Log_TeamCity extends PHPUnit_TextUI_ResultPrinter
+{
+ /**
+ * @var bool
+ */
+ private $isSummaryTestCountPrinted = false;
+
+ /**
+ * @var string
+ */
+ private $startedTestName;
+
+ /**
+ * @var string
+ */
+ private $flowId;
+
+ /**
+ * @param string $progress
+ */
+ protected function writeProgress($progress)
+ {
+ }
+
+ /**
+ * @param PHPUnit_Framework_TestResult $result
+ */
+ public function printResult(PHPUnit_Framework_TestResult $result)
+ {
+ $this->printHeader();
+ $this->printFooter($result);
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->printEvent(
+ 'testFailed',
+ [
+ 'name' => $test->getName(),
+ 'message' => self::getMessage($e),
+ 'details' => self::getDetails($e),
+ ]
+ );
+ }
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ $this->printEvent(
+ 'testFailed',
+ [
+ 'name' => $test->getName(),
+ 'message' => self::getMessage($e),
+ 'details' => self::getDetails($e)
+ ]
+ );
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ $parameters = [
+ 'name' => $test->getName(),
+ 'message' => self::getMessage($e),
+ 'details' => self::getDetails($e),
+ ];
+
+ if ($e instanceof PHPUnit_Framework_ExpectationFailedException) {
+ $comparisonFailure = $e->getComparisonFailure();
+
+ if ($comparisonFailure instanceof ComparisonFailure) {
+ $expectedString = $comparisonFailure->getExpectedAsString();
+
+ if (is_null($expectedString) || empty($expectedString)) {
+ $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
+ }
+
+ $actualString = $comparisonFailure->getActualAsString();
+
+ if (is_null($actualString) || empty($actualString)) {
+ $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
+ }
+
+ if (!is_null($actualString) && !is_null($expectedString)) {
+ $parameters['type'] = 'comparisonFailure';
+ $parameters['actual'] = $actualString;
+ $parameters['expected'] = $expectedString;
+ }
+ }
+ }
+
+ $this->printEvent('testFailed', $parameters);
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->printIgnoredTest($test->getName(), $e);
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->addError($test, $e, $time);
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $testName = $test->getName();
+ if ($this->startedTestName != $testName) {
+ $this->startTest($test);
+ $this->printIgnoredTest($testName, $e);
+ $this->endTest($test, $time);
+ } else {
+ $this->printIgnoredTest($testName, $e);
+ }
+ }
+
+ public function printIgnoredTest($testName, Exception $e)
+ {
+ $this->printEvent(
+ 'testIgnored',
+ [
+ 'name' => $testName,
+ 'message' => self::getMessage($e),
+ 'details' => self::getDetails($e),
+ ]
+ );
+ }
+
+ /**
+ * A testsuite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ if (stripos(ini_get('disable_functions'), 'getmypid') === false) {
+ $this->flowId = getmypid();
+ } else {
+ $this->flowId = false;
+ }
+
+ if (!$this->isSummaryTestCountPrinted) {
+ $this->isSummaryTestCountPrinted = true;
+
+ $this->printEvent(
+ 'testCount',
+ ['count' => count($suite)]
+ );
+ }
+
+ $suiteName = $suite->getName();
+
+ if (empty($suiteName)) {
+ return;
+ }
+
+ $parameters = ['name' => $suiteName];
+
+ if (class_exists($suiteName, false)) {
+ $fileName = self::getFileName($suiteName);
+ $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
+ } else {
+ $split = preg_split('/::/', $suiteName);
+
+ if (count($split) == 2 && method_exists($split[0], $split[1])) {
+ $fileName = self::getFileName($split[0]);
+ $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName";
+ $parameters['name'] = $split[1];
+ }
+ }
+
+ $this->printEvent('testSuiteStarted', $parameters);
+ }
+
+ /**
+ * A testsuite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ $suiteName = $suite->getName();
+
+ if (empty($suiteName)) {
+ return;
+ }
+
+ $parameters = ['name' => $suiteName];
+
+ if (!class_exists($suiteName, false)) {
+ $split = preg_split('/::/', $suiteName);
+
+ if (count($split) == 2 && method_exists($split[0], $split[1])) {
+ $parameters['name'] = $split[1];
+ }
+ }
+
+ $this->printEvent('testSuiteFinished', $parameters);
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ $testName = $test->getName();
+ $this->startedTestName = $testName;
+ $params = ['name' => $testName];
+
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ $className = get_class($test);
+ $fileName = self::getFileName($className);
+ $params['locationHint'] = "php_qn://$fileName::\\$className::$testName";
+ }
+
+ $this->printEvent('testStarted', $params);
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ parent::endTest($test, $time);
+
+ $this->printEvent(
+ 'testFinished',
+ [
+ 'name' => $test->getName(),
+ 'duration' => (int) (round($time, 2) * 1000)
+ ]
+ );
+ }
+
+ /**
+ * @param string $eventName
+ * @param array $params
+ */
+ private function printEvent($eventName, $params = [])
+ {
+ $this->write("\n##teamcity[$eventName");
+
+ if ($this->flowId) {
+ $params['flowId'] = $this->flowId;
+ }
+
+ foreach ($params as $key => $value) {
+ $escapedValue = self::escapeValue($value);
+ $this->write(" $key='$escapedValue'");
+ }
+
+ $this->write("]\n");
+ }
+
+ /**
+ * @param Exception $e
+ *
+ * @return string
+ */
+ private static function getMessage(Exception $e)
+ {
+ $message = '';
+
+ if (!$e instanceof PHPUnit_Framework_Exception) {
+ if (strlen(get_class($e)) != 0) {
+ $message = $message . get_class($e);
+ }
+
+ if (strlen($message) != 0 && strlen($e->getMessage()) != 0) {
+ $message = $message . ' : ';
+ }
+ }
+
+ return $message . $e->getMessage();
+ }
+
+ /**
+ * @param Exception $e
+ *
+ * @return string
+ */
+ private static function getDetails(Exception $e)
+ {
+ $stackTrace = PHPUnit_Util_Filter::getFilteredStacktrace($e);
+ $previous = $e->getPrevious();
+
+ while ($previous) {
+ $stackTrace .= "\nCaused by\n" .
+ PHPUnit_Framework_TestFailure::exceptionToString($previous) . "\n" .
+ PHPUnit_Util_Filter::getFilteredStacktrace($previous);
+
+ $previous = $previous->getPrevious();
+ }
+
+ return ' ' . str_replace("\n", "\n ", $stackTrace);
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return string
+ */
+ private static function getPrimitiveValueAsString($value)
+ {
+ if (is_null($value)) {
+ return 'null';
+ } elseif (is_bool($value)) {
+ return $value == true ? 'true' : 'false';
+ } elseif (is_scalar($value)) {
+ return print_r($value, true);
+ }
+
+ return;
+ }
+
+ /**
+ * @param $text
+ *
+ * @return string
+ */
+ private static function escapeValue($text)
+ {
+ $text = str_replace('|', '||', $text);
+ $text = str_replace("'", "|'", $text);
+ $text = str_replace("\n", '|n', $text);
+ $text = str_replace("\r", '|r', $text);
+ $text = str_replace(']', '|]', $text);
+ $text = str_replace('[', '|[', $text);
+
+ return $text;
+ }
+
+ /**
+ * @param string $className
+ *
+ * @return string
+ */
+ private static function getFileName($className)
+ {
+ $reflectionClass = new ReflectionClass($className);
+ $fileName = $reflectionClass->getFileName();
+
+ return $fileName;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use SebastianBergmann\Environment\Runtime;
+
+/**
+ * Utility methods for PHP sub-processes.
+ */
+abstract class PHPUnit_Util_PHP
+{
+ /**
+ * @var Runtime
+ */
+ protected $runtime;
+
+ /**
+ * @var bool
+ */
+ protected $stderrRedirection = false;
+
+ /**
+ * @var string
+ */
+ protected $stdin = '';
+
+ /**
+ * @var string
+ */
+ protected $args = '';
+
+ /**
+ * @var array
+ */
+ protected $env = [];
+
+ /**
+ * @var int
+ */
+ protected $timeout = 0;
+
+ /**
+ * Creates internal Runtime instance.
+ */
+ public function __construct()
+ {
+ $this->runtime = new Runtime();
+ }
+
+ /**
+ * Defines if should use STDERR redirection or not.
+ *
+ * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT.
+ *
+ * @throws PHPUnit_Framework_Exception
+ *
+ * @param bool $stderrRedirection
+ */
+ public function setUseStderrRedirection($stderrRedirection)
+ {
+ if (!is_bool($stderrRedirection)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+
+ $this->stderrRedirection = $stderrRedirection;
+ }
+
+ /**
+ * Returns TRUE if uses STDERR redirection or FALSE if not.
+ *
+ * @return bool
+ */
+ public function useStderrRedirection()
+ {
+ return $this->stderrRedirection;
+ }
+
+ /**
+ * Sets the input string to be sent via STDIN
+ *
+ * @param string $stdin
+ */
+ public function setStdin($stdin)
+ {
+ $this->stdin = (string) $stdin;
+ }
+
+ /**
+ * Returns the input string to be sent via STDIN
+ *
+ * @return string
+ */
+ public function getStdin()
+ {
+ return $this->stdin;
+ }
+
+ /**
+ * Sets the string of arguments to pass to the php job
+ *
+ * @param string $args
+ */
+ public function setArgs($args)
+ {
+ $this->args = (string) $args;
+ }
+
+ /**
+ * Returns the string of arguments to pass to the php job
+ *
+ * @retrun string
+ */
+ public function getArgs()
+ {
+ return $this->args;
+ }
+
+ /**
+ * Sets the array of environment variables to start the child process with
+ *
+ * @param array $env
+ */
+ public function setEnv(array $env)
+ {
+ $this->env = $env;
+ }
+
+ /**
+ * Returns the array of environment variables to start the child process with
+ *
+ * @return array
+ */
+ public function getEnv()
+ {
+ return $this->env;
+ }
+
+ /**
+ * Sets the amount of seconds to wait before timing out
+ *
+ * @param int $timeout
+ */
+ public function setTimeout($timeout)
+ {
+ $this->timeout = (int) $timeout;
+ }
+
+ /**
+ * Returns the amount of seconds to wait before timing out
+ *
+ * @return int
+ */
+ public function getTimeout()
+ {
+ return $this->timeout;
+ }
+
+ /**
+ * @return PHPUnit_Util_PHP
+ */
+ public static function factory()
+ {
+ if (DIRECTORY_SEPARATOR == '\\') {
+ return new PHPUnit_Util_PHP_Windows;
+ }
+
+ return new PHPUnit_Util_PHP_Default;
+ }
+
+ /**
+ * Runs a single test in a separate PHP process.
+ *
+ * @param string $job
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_TestResult $result
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function runTestJob($job, PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result)
+ {
+ $result->startTest($test);
+
+ $_result = $this->runJob($job);
+
+ $this->processChildResult(
+ $test,
+ $result,
+ $_result['stdout'],
+ $_result['stderr']
+ );
+ }
+
+ /**
+ * Returns the command based into the configurations.
+ *
+ * @param array $settings
+ * @param string|null $file
+ *
+ * @return string
+ */
+ public function getCommand(array $settings, $file = null)
+ {
+ $command = $this->runtime->getBinary();
+ $command .= $this->settingsToParameters($settings);
+
+ if ('phpdbg' === PHP_SAPI) {
+ $command .= ' -qrr ';
+
+ if ($file) {
+ $command .= '-e ' . escapeshellarg($file);
+ } else {
+ $command .= escapeshellarg(__DIR__ . '/PHP/eval-stdin.php');
+ }
+ } elseif ($file) {
+ $command .= ' -f ' . escapeshellarg($file);
+ }
+
+ if ($this->args) {
+ $command .= ' -- ' . $this->args;
+ }
+
+ if (true === $this->stderrRedirection) {
+ $command .= ' 2>&1';
+ }
+
+ return $command;
+ }
+
+ /**
+ * Runs a single job (PHP code) using a separate PHP process.
+ *
+ * @param string $job
+ * @param array $settings
+ *
+ * @return array
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ abstract public function runJob($job, array $settings = []);
+
+ /**
+ * @param array $settings
+ *
+ * @return string
+ */
+ protected function settingsToParameters(array $settings)
+ {
+ $buffer = '';
+
+ foreach ($settings as $setting) {
+ $buffer .= ' -d ' . $setting;
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Processes the TestResult object from an isolated process.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_TestResult $result
+ * @param string $stdout
+ * @param string $stderr
+ */
+ private function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result, $stdout, $stderr)
+ {
+ $time = 0;
+
+ if (!empty($stderr)) {
+ $result->addError(
+ $test,
+ new PHPUnit_Framework_Exception(trim($stderr)),
+ $time
+ );
+ } else {
+ set_error_handler(function ($errno, $errstr, $errfile, $errline) {
+ throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
+ });
+ try {
+ if (strpos($stdout, "#!/usr/bin/env php\n") === 0) {
+ $stdout = substr($stdout, 19);
+ }
+
+ $childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout));
+ restore_error_handler();
+ } catch (ErrorException $e) {
+ restore_error_handler();
+ $childResult = false;
+
+ $result->addError(
+ $test,
+ new PHPUnit_Framework_Exception(trim($stdout), 0, $e),
+ $time
+ );
+ }
+
+ if ($childResult !== false) {
+ if (!empty($childResult['output'])) {
+ $output = $childResult['output'];
+ }
+
+ $test->setResult($childResult['testResult']);
+ $test->addToAssertionCount($childResult['numAssertions']);
+
+ $childResult = $childResult['result'];
+ /* @var $childResult PHPUnit_Framework_TestResult */
+
+ if ($result->getCollectCodeCoverageInformation()) {
+ $result->getCodeCoverage()->merge(
+ $childResult->getCodeCoverage()
+ );
+ }
+
+ $time = $childResult->time();
+ $notImplemented = $childResult->notImplemented();
+ $risky = $childResult->risky();
+ $skipped = $childResult->skipped();
+ $errors = $childResult->errors();
+ $warnings = $childResult->warnings();
+ $failures = $childResult->failures();
+
+ if (!empty($notImplemented)) {
+ $result->addError(
+ $test,
+ $this->getException($notImplemented[0]),
+ $time
+ );
+ } elseif (!empty($risky)) {
+ $result->addError(
+ $test,
+ $this->getException($risky[0]),
+ $time
+ );
+ } elseif (!empty($skipped)) {
+ $result->addError(
+ $test,
+ $this->getException($skipped[0]),
+ $time
+ );
+ } elseif (!empty($errors)) {
+ $result->addError(
+ $test,
+ $this->getException($errors[0]),
+ $time
+ );
+ } elseif (!empty($warnings)) {
+ $result->addWarning(
+ $test,
+ $this->getException($warnings[0]),
+ $time
+ );
+ } elseif (!empty($failures)) {
+ $result->addFailure(
+ $test,
+ $this->getException($failures[0]),
+ $time
+ );
+ }
+ }
+ }
+
+ $result->endTest($test, $time);
+
+ if (!empty($output)) {
+ print $output;
+ }
+ }
+
+ /**
+ * Gets the thrown exception from a PHPUnit_Framework_TestFailure.
+ *
+ * @param PHPUnit_Framework_TestFailure $error
+ *
+ * @return Exception
+ *
+ * @see https://github.com/sebastianbergmann/phpunit/issues/74
+ */
+ private function getException(PHPUnit_Framework_TestFailure $error)
+ {
+ $exception = $error->thrownException();
+
+ if ($exception instanceof __PHP_Incomplete_Class) {
+ $exceptionArray = [];
+ foreach ((array) $exception as $key => $value) {
+ $key = substr($key, strrpos($key, "\0") + 1);
+ $exceptionArray[$key] = $value;
+ }
+
+ $exception = new PHPUnit_Framework_SyntheticError(
+ sprintf(
+ '%s: %s',
+ $exceptionArray['_PHP_Incomplete_Class_Name'],
+ $exceptionArray['message']
+ ),
+ $exceptionArray['code'],
+ $exceptionArray['file'],
+ $exceptionArray['line'],
+ $exceptionArray['trace']
+ );
+ }
+
+ return $exception;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Default utility for PHP sub-processes.
+ */
+class PHPUnit_Util_PHP_Default extends PHPUnit_Util_PHP
+{
+ /**
+ * @var string
+ */
+ protected $tempFile;
+
+ /**
+ * @var bool
+ */
+ protected $useTempFile = false;
+
+ /**
+ * Runs a single job (PHP code) using a separate PHP process.
+ *
+ * @param string $job
+ * @param array $settings
+ *
+ * @return array
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function runJob($job, array $settings = [])
+ {
+ if ($this->useTempFile || $this->stdin) {
+ if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'PHPUnit')) ||
+ file_put_contents($this->tempFile, $job) === false) {
+ throw new PHPUnit_Framework_Exception(
+ 'Unable to write temporary file'
+ );
+ }
+
+ $job = $this->stdin;
+ }
+
+ return $this->runProcess($job, $settings);
+ }
+
+ /**
+ * Returns an array of file handles to be used in place of pipes
+ *
+ * @return array
+ */
+ protected function getHandles()
+ {
+ return [];
+ }
+
+ /**
+ * Handles creating the child process and returning the STDOUT and STDERR
+ *
+ * @param string $job
+ * @param array $settings
+ *
+ * @return array
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function runProcess($job, $settings)
+ {
+ $handles = $this->getHandles();
+
+ $env = null;
+ if ($this->env) {
+ $env = isset($_SERVER) ? $_SERVER : [];
+ unset($env['argv'], $env['argc']);
+ $env = array_merge($env, $this->env);
+
+ foreach ($env as $envKey => $envVar) {
+ if (is_array($envVar)) {
+ unset($env[$envKey]);
+ }
+ }
+ }
+
+ $pipeSpec = [
+ 0 => isset($handles[0]) ? $handles[0] : ['pipe', 'r'],
+ 1 => isset($handles[1]) ? $handles[1] : ['pipe', 'w'],
+ 2 => isset($handles[2]) ? $handles[2] : ['pipe', 'w'],
+ ];
+ $process = proc_open(
+ $this->getCommand($settings, $this->tempFile),
+ $pipeSpec,
+ $pipes,
+ null,
+ $env
+ );
+
+ if (!is_resource($process)) {
+ throw new PHPUnit_Framework_Exception(
+ 'Unable to spawn worker process'
+ );
+ }
+
+ if ($job) {
+ $this->process($pipes[0], $job);
+ }
+ fclose($pipes[0]);
+
+ if ($this->timeout) {
+ $stderr = $stdout = '';
+ unset($pipes[0]);
+
+ while (true) {
+ $r = $pipes;
+ $w = null;
+ $e = null;
+
+ $n = @stream_select($r, $w, $e, $this->timeout);
+
+ if ($n === false) {
+ break;
+ } elseif ($n === 0) {
+ proc_terminate($process, 9);
+ throw new PHPUnit_Framework_Exception(sprintf('Job execution aborted after %d seconds', $this->timeout));
+ } elseif ($n > 0) {
+ foreach ($r as $pipe) {
+ $pipeOffset = 0;
+ foreach ($pipes as $i => $origPipe) {
+ if ($pipe == $origPipe) {
+ $pipeOffset = $i;
+ break;
+ }
+ }
+
+ if (!$pipeOffset) {
+ break;
+ }
+
+ $line = fread($pipe, 8192);
+ if (strlen($line) == 0) {
+ fclose($pipes[$pipeOffset]);
+ unset($pipes[$pipeOffset]);
+ } else {
+ if ($pipeOffset == 1) {
+ $stdout .= $line;
+ } else {
+ $stderr .= $line;
+ }
+ }
+ }
+
+ if (empty($pipes)) {
+ break;
+ }
+ }
+ }
+ } else {
+ if (isset($pipes[1])) {
+ $stdout = stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ }
+
+ if (isset($pipes[2])) {
+ $stderr = stream_get_contents($pipes[2]);
+ fclose($pipes[2]);
+ }
+ }
+
+ if (isset($handles[1])) {
+ rewind($handles[1]);
+ $stdout = stream_get_contents($handles[1]);
+ fclose($handles[1]);
+ }
+
+ if (isset($handles[2])) {
+ rewind($handles[2]);
+ $stderr = stream_get_contents($handles[2]);
+ fclose($handles[2]);
+ }
+
+ proc_close($process);
+ $this->cleanup();
+
+ return ['stdout' => $stdout, 'stderr' => $stderr];
+ }
+
+ /**
+ * @param resource $pipe
+ * @param string $job
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function process($pipe, $job)
+ {
+ fwrite($pipe, $job);
+ }
+
+ protected function cleanup()
+ {
+ if ($this->tempFile) {
+ unlink($this->tempFile);
+ }
+ }
+}
+<?php
+use SebastianBergmann\CodeCoverage\CodeCoverage;
+
+if (!defined('STDOUT')) {
+ // php://stdout does not obey output buffering. Any output would break
+ // unserialization of child process results in the parent process.
+ define('STDOUT', fopen('php://temp', 'w+b'));
+ define('STDERR', fopen('php://stderr', 'wb'));
+}
+
+{iniSettings}
+ini_set('display_errors', 'stderr');
+set_include_path('{include_path}');
+
+$composerAutoload = {composerAutoload};
+$phar = {phar};
+
+ob_start();
+
+if ($composerAutoload) {
+ require_once $composerAutoload;
+ define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload);
+} else if ($phar) {
+ require $phar;
+}
+
+function __phpunit_run_isolated_test()
+{
+ if (!class_exists('{className}')) {
+ require_once '{filename}';
+ }
+
+ $result = new PHPUnit_Framework_TestResult;
+
+ if ({collectCodeCoverageInformation}) {
+ $result->setCodeCoverage(
+ new CodeCoverage(
+ null,
+ unserialize('{codeCoverageFilter}')
+ )
+ );
+ }
+
+ $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything});
+ $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests});
+ $result->enforceTimeLimit({enforcesTimeLimit});
+ $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests});
+ $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests});
+
+ $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}');
+ $test->setDependencyInput(unserialize('{dependencyInput}'));
+ $test->setInIsolation(TRUE);
+
+ ob_end_clean();
+ $test->run($result);
+ $output = '';
+ if (!$test->hasExpectationOnOutput()) {
+ $output = $test->getActualOutput();
+ }
+
+ @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */
+ if ($stdout = stream_get_contents(STDOUT)) {
+ $output = $stdout . $output;
+ }
+
+ print serialize(
+ array(
+ 'testResult' => $test->getResult(),
+ 'numAssertions' => $test->getNumAssertions(),
+ 'result' => $result,
+ 'output' => $output
+ )
+ );
+}
+
+$configurationFilePath = '{configurationFilePath}';
+
+if ('' !== $configurationFilePath) {
+ $configuration = PHPUnit_Util_Configuration::getInstance($configurationFilePath);
+ $configuration->handlePHPConfiguration();
+ unset($configuration);
+}
+
+function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
+{
+ return true;
+}
+
+set_error_handler("__phpunit_error_handler");
+
+{constants}
+{included_files}
+{globals}
+
+restore_error_handler();
+
+if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) {
+ require_once $GLOBALS['__PHPUNIT_BOOTSTRAP'];
+ unset($GLOBALS['__PHPUNIT_BOOTSTRAP']);
+}
+
+__phpunit_run_isolated_test();
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Windows utility for PHP sub-processes.
+ *
+ * Reading from STDOUT or STDERR hangs forever on Windows if the output is
+ * too large.
+ *
+ * @see https://bugs.php.net/bug.php?id=51800
+ */
+class PHPUnit_Util_PHP_Windows extends PHPUnit_Util_PHP_Default
+{
+ protected $useTempFile = true;
+
+ protected function getHandles()
+ {
+ if (false === $stdout_handle = tmpfile()) {
+ throw new PHPUnit_Framework_Exception(
+ 'A temporary file could not be created; verify that your TEMP environment variable is writable'
+ );
+ }
+
+ return [
+ 1 => $stdout_handle
+ ];
+ }
+
+ public function getCommand(array $settings, $file = null)
+ {
+ return '"' . parent::getCommand($settings, $file) . '"';
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+eval('?>' . file_get_contents('php://stdin'));
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Utility class that can print to STDOUT or write to a file.
+ */
+class PHPUnit_Util_Printer
+{
+ /**
+ * If true, flush output after every write.
+ *
+ * @var bool
+ */
+ protected $autoFlush = false;
+
+ /**
+ * @var resource
+ */
+ protected $out;
+
+ /**
+ * @var string
+ */
+ protected $outTarget;
+
+ /**
+ * Constructor.
+ *
+ * @param mixed $out
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function __construct($out = null)
+ {
+ if ($out !== null) {
+ if (is_string($out)) {
+ if (strpos($out, 'socket://') === 0) {
+ $out = explode(':', str_replace('socket://', '', $out));
+
+ if (count($out) != 2) {
+ throw new PHPUnit_Framework_Exception;
+ }
+
+ $this->out = fsockopen($out[0], $out[1]);
+ } else {
+ if (strpos($out, 'php://') === false &&
+ !is_dir(dirname($out))) {
+ mkdir(dirname($out), 0777, true);
+ }
+
+ $this->out = fopen($out, 'wt');
+ }
+
+ $this->outTarget = $out;
+ } else {
+ $this->out = $out;
+ }
+ }
+ }
+
+ /**
+ * Flush buffer and close output if it's not to a PHP stream
+ */
+ public function flush()
+ {
+ if ($this->out && strncmp($this->outTarget, 'php://', 6) !== 0) {
+ fclose($this->out);
+ }
+ }
+
+ /**
+ * Performs a safe, incremental flush.
+ *
+ * Do not confuse this function with the flush() function of this class,
+ * since the flush() function may close the file being written to, rendering
+ * the current object no longer usable.
+ */
+ public function incrementalFlush()
+ {
+ if ($this->out) {
+ fflush($this->out);
+ } else {
+ flush();
+ }
+ }
+
+ /**
+ * @param string $buffer
+ */
+ public function write($buffer)
+ {
+ if ($this->out) {
+ fwrite($this->out, $buffer);
+
+ if ($this->autoFlush) {
+ $this->incrementalFlush();
+ }
+ } else {
+ if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') {
+ $buffer = htmlspecialchars($buffer, ENT_SUBSTITUTE);
+ }
+
+ print $buffer;
+
+ if ($this->autoFlush) {
+ $this->incrementalFlush();
+ }
+ }
+ }
+
+ /**
+ * Check auto-flush mode.
+ *
+ * @return bool
+ */
+ public function getAutoFlush()
+ {
+ return $this->autoFlush;
+ }
+
+ /**
+ * Set auto-flushing mode.
+ *
+ * If set, *incremental* flushes will be done after each write. This should
+ * not be confused with the different effects of this class' flush() method.
+ *
+ * @param bool $autoFlush
+ */
+ public function setAutoFlush($autoFlush)
+ {
+ if (is_bool($autoFlush)) {
+ $this->autoFlush = $autoFlush;
+ } else {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
+ }
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Error handler that converts PHP errors and warnings to exceptions.
+ */
+class PHPUnit_Util_Regex
+{
+ /**
+ * @param string $pattern
+ * @param string $subject
+ * @param null $matches
+ * @param int $flags
+ * @param int $offset
+ *
+ * @return int
+ */
+ public static function pregMatchSafe($pattern, $subject, $matches = null, $flags = 0, $offset = 0)
+ {
+ $handler_terminator = PHPUnit_Util_ErrorHandler::handleErrorOnce(E_WARNING);
+ $match = preg_match($pattern, $subject, $matches, $flags, $offset);
+ $handler_terminator(); // cleaning
+
+ return $match;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * String helpers.
+ */
+class PHPUnit_Util_String
+{
+ /**
+ * Converts a string to UTF-8 encoding.
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function convertToUtf8($string)
+ {
+ return mb_convert_encoding($string, 'UTF-8');
+ }
+
+ /**
+ * Checks a string for UTF-8 encoding.
+ *
+ * @param string $string
+ *
+ * @return bool
+ */
+ protected static function isUtf8($string)
+ {
+ $length = strlen($string);
+
+ for ($i = 0; $i < $length; $i++) {
+ if (ord($string[$i]) < 0x80) {
+ $n = 0;
+ } elseif ((ord($string[$i]) & 0xE0) == 0xC0) {
+ $n = 1;
+ } elseif ((ord($string[$i]) & 0xF0) == 0xE0) {
+ $n = 2;
+ } elseif ((ord($string[$i]) & 0xF0) == 0xF0) {
+ $n = 3;
+ } else {
+ return false;
+ }
+
+ for ($j = 0; $j < $n; $j++) {
+ if ((++$i == $length) || ((ord($string[$i]) & 0xC0) != 0x80)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Test helpers.
+ */
+class PHPUnit_Util_Test
+{
+ const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';
+ const REGEX_TEST_WITH = '/@testWith\s+/';
+ const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
+ const REGEX_REQUIRES_VERSION = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
+ const REGEX_REQUIRES_OS = '/@requires\s+OS\s+(?P<value>.+?)[ \t]*\r?$/m';
+ const REGEX_REQUIRES = '/@requires\s+(?P<name>function|extension)\s+(?P<value>([^ ]+?))\s*(?P<operator>[<>=!]{0,2})\s*(?P<version>[\d\.-]+[\d\.]?)?[ \t]*\r?$/m';
+
+ const UNKNOWN = -1;
+ const SMALL = 0;
+ const MEDIUM = 1;
+ const LARGE = 2;
+
+ private static $annotationCache = [];
+
+ private static $hookMethods = [];
+
+ /**
+ * @param PHPUnit_Framework_Test $test
+ * @param bool $asString
+ *
+ * @return mixed
+ */
+ public static function describe(PHPUnit_Framework_Test $test, $asString = true)
+ {
+ if ($asString) {
+ if ($test instanceof PHPUnit_Framework_SelfDescribing) {
+ return $test->toString();
+ } else {
+ return get_class($test);
+ }
+ } else {
+ if ($test instanceof PHPUnit_Framework_TestCase) {
+ return [
+ get_class($test), $test->getName()
+ ];
+ } elseif ($test instanceof PHPUnit_Framework_SelfDescribing) {
+ return ['', $test->toString()];
+ } else {
+ return ['', get_class($test)];
+ }
+ }
+ }
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array|bool
+ *
+ * @throws PHPUnit_Framework_CodeCoverageException
+ */
+ public static function getLinesToBeCovered($className, $methodName)
+ {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) {
+ return false;
+ }
+
+ return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers');
+ }
+
+ /**
+ * Returns lines of code specified with the @uses annotation.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getLinesToBeUsed($className, $methodName)
+ {
+ return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses');
+ }
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ * @param string $mode
+ *
+ * @return array
+ *
+ * @throws PHPUnit_Framework_CodeCoverageException
+ */
+ private static function getLinesToBeCoveredOrUsed($className, $methodName, $mode)
+ {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ $classShortcut = null;
+
+ if (!empty($annotations['class'][$mode . 'DefaultClass'])) {
+ if (count($annotations['class'][$mode . 'DefaultClass']) > 1) {
+ throw new PHPUnit_Framework_CodeCoverageException(
+ sprintf(
+ 'More than one @%sClass annotation in class or interface "%s".',
+ $mode,
+ $className
+ )
+ );
+ }
+
+ $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0];
+ }
+
+ $list = [];
+
+ if (isset($annotations['class'][$mode])) {
+ $list = $annotations['class'][$mode];
+ }
+
+ if (isset($annotations['method'][$mode])) {
+ $list = array_merge($list, $annotations['method'][$mode]);
+ }
+
+ $codeList = [];
+
+ foreach (array_unique($list) as $element) {
+ if ($classShortcut && strncmp($element, '::', 2) === 0) {
+ $element = $classShortcut . $element;
+ }
+
+ $element = preg_replace('/[\s()]+$/', '', $element);
+ $element = explode(' ', $element);
+ $element = $element[0];
+
+ $codeList = array_merge(
+ $codeList,
+ self::resolveElementToReflectionObjects($element)
+ );
+ }
+
+ return self::resolveReflectionObjectsToLines($codeList);
+ }
+
+ /**
+ * Returns the requirements for a test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getRequirements($className, $methodName)
+ {
+ $reflector = new ReflectionClass($className);
+ $docComment = $reflector->getDocComment();
+ $reflector = new ReflectionMethod($className, $methodName);
+ $docComment .= "\n" . $reflector->getDocComment();
+ $requires = [];
+
+ if ($count = preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) {
+ $requires['OS'] = sprintf(
+ '/%s/i',
+ addcslashes($matches['value'][$count - 1], '/')
+ );
+ }
+ if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) {
+ for ($i = 0; $i < $count; $i++) {
+ $requires[$matches['name'][$i]] = [
+ 'version' => $matches['version'][$i],
+ 'operator' => $matches['operator'][$i]
+ ];
+ }
+ }
+
+ // https://bugs.php.net/bug.php?id=63055
+ $matches = [];
+
+ if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) {
+ for ($i = 0; $i < $count; $i++) {
+ $name = $matches['name'][$i] . 's';
+ if (!isset($requires[$name])) {
+ $requires[$name] = [];
+ }
+ $requires[$name][] = $matches['value'][$i];
+ if (empty($matches['version'][$i]) || $name != 'extensions') {
+ continue;
+ }
+ $requires['extension_versions'][$matches['value'][$i]] = [
+ 'version' => $matches['version'][$i],
+ 'operator' => $matches['operator'][$i]
+ ];
+ }
+ }
+
+ return $requires;
+ }
+
+ /**
+ * Returns the missing requirements for a test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getMissingRequirements($className, $methodName)
+ {
+ $required = static::getRequirements($className, $methodName);
+ $missing = [];
+
+ $operator = empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator'];
+ if (!empty($required['PHP']) && !version_compare(PHP_VERSION, $required['PHP']['version'], $operator)) {
+ $missing[] = sprintf('PHP %s %s is required.', $operator, $required['PHP']['version']);
+ }
+
+ if (!empty($required['PHPUnit'])) {
+ $phpunitVersion = PHPUnit_Runner_Version::id();
+
+ $operator = empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator'];
+ if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) {
+ $missing[] = sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']);
+ }
+ }
+
+ if (!empty($required['OS']) && !preg_match($required['OS'], PHP_OS)) {
+ $missing[] = sprintf('Operating system matching %s is required.', $required['OS']);
+ }
+
+ if (!empty($required['functions'])) {
+ foreach ($required['functions'] as $function) {
+ $pieces = explode('::', $function);
+ if (2 === count($pieces) && method_exists($pieces[0], $pieces[1])) {
+ continue;
+ }
+ if (function_exists($function)) {
+ continue;
+ }
+ $missing[] = sprintf('Function %s is required.', $function);
+ }
+ }
+
+ if (!empty($required['extensions'])) {
+ foreach ($required['extensions'] as $extension) {
+ if (isset($required['extension_versions'][$extension])) {
+ continue;
+ }
+ if (!extension_loaded($extension)) {
+ $missing[] = sprintf('Extension %s is required.', $extension);
+ }
+ }
+ }
+
+ if (!empty($required['extension_versions'])) {
+ foreach ($required['extension_versions'] as $extension => $required) {
+ $actualVersion = phpversion($extension);
+
+ $operator = empty($required['operator']) ? '>=' : $required['operator'];
+ if (false === $actualVersion || !version_compare($actualVersion, $required['version'], $operator)) {
+ $missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator, $required['version']);
+ }
+ }
+ }
+
+ return $missing;
+ }
+
+ /**
+ * Returns the expected exception for a test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getExpectedException($className, $methodName)
+ {
+ $reflector = new ReflectionMethod($className, $methodName);
+ $docComment = $reflector->getDocComment();
+ $docComment = substr($docComment, 3, -2);
+
+ if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ $class = $matches[1];
+ $code = null;
+ $message = '';
+ $messageRegExp = '';
+
+ if (isset($matches[2])) {
+ $message = trim($matches[2]);
+ } elseif (isset($annotations['method']['expectedExceptionMessage'])) {
+ $message = self::parseAnnotationContent(
+ $annotations['method']['expectedExceptionMessage'][0]
+ );
+ }
+
+ if (isset($annotations['method']['expectedExceptionMessageRegExp'])) {
+ $messageRegExp = self::parseAnnotationContent(
+ $annotations['method']['expectedExceptionMessageRegExp'][0]
+ );
+ }
+
+ if (isset($matches[3])) {
+ $code = $matches[3];
+ } elseif (isset($annotations['method']['expectedExceptionCode'])) {
+ $code = self::parseAnnotationContent(
+ $annotations['method']['expectedExceptionCode'][0]
+ );
+ }
+
+ if (is_numeric($code)) {
+ $code = (int) $code;
+ } elseif (is_string($code) && defined($code)) {
+ $code = (int) constant($code);
+ }
+
+ return [
+ 'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp
+ ];
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse annotation content to use constant/class constant values
+ *
+ * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME
+ *
+ * If the constant is not found the string is used as is to ensure maximum BC.
+ *
+ * @param string $message
+ *
+ * @return string
+ */
+ private static function parseAnnotationContent($message)
+ {
+ if (strpos($message, '::') !== false && count(explode('::', $message)) == 2) {
+ if (defined($message)) {
+ $message = constant($message);
+ }
+ }
+
+ return $message;
+ }
+
+ /**
+ * Returns the provided data for a method.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array When a data provider is specified and exists
+ * null When no data provider is specified
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ public static function getProvidedData($className, $methodName)
+ {
+ $reflector = new ReflectionMethod($className, $methodName);
+ $docComment = $reflector->getDocComment();
+
+ $data = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName);
+
+ if ($data === null) {
+ $data = self::getDataFromTestWithAnnotation($docComment);
+ }
+
+ if (is_array($data) && empty($data)) {
+ throw new PHPUnit_Framework_SkippedTestError;
+ }
+
+ if ($data !== null) {
+ foreach ($data as $key => $value) {
+ if (!is_array($value)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Data set %s is invalid.',
+ is_int($key) ? '#' . $key : '"' . $key . '"'
+ )
+ );
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Returns the provided data for a method.
+ *
+ * @param string $docComment
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array|Iterator when a data provider is specified and exists
+ * null when no data provider is specified
+ *
+ * @throws PHPUnit_Framework_Exception
+ */
+ private static function getDataFromDataProviderAnnotation($docComment, $className, $methodName)
+ {
+ if (preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
+ $result = [];
+
+ foreach ($matches[1] as $match) {
+ $dataProviderMethodNameNamespace = explode('\\', $match);
+ $leaf = explode('::', array_pop($dataProviderMethodNameNamespace));
+ $dataProviderMethodName = array_pop($leaf);
+
+ if (!empty($dataProviderMethodNameNamespace)) {
+ $dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\';
+ } else {
+ $dataProviderMethodNameNamespace = '';
+ }
+
+ if (!empty($leaf)) {
+ $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf);
+ } else {
+ $dataProviderClassName = $className;
+ }
+
+ $dataProviderClass = new ReflectionClass($dataProviderClassName);
+ $dataProviderMethod = $dataProviderClass->getMethod(
+ $dataProviderMethodName
+ );
+
+ if ($dataProviderMethod->isStatic()) {
+ $object = null;
+ } else {
+ $object = $dataProviderClass->newInstance();
+ }
+
+ if ($dataProviderMethod->getNumberOfParameters() == 0) {
+ $data = $dataProviderMethod->invoke($object);
+ } else {
+ $data = $dataProviderMethod->invoke($object, $methodName);
+ }
+
+ if ($data instanceof Iterator) {
+ $data = iterator_to_array($data);
+ }
+
+ if (is_array($data)) {
+ $result = array_merge($result, $data);
+ }
+ }
+
+ return $result;
+ }
+ }
+
+ /**
+ * @param string $docComment full docComment string
+ *
+ * @return array when @testWith annotation is defined
+ * null when @testWith annotation is omitted
+ *
+ * @throws PHPUnit_Framework_Exception when @testWith annotation is defined but cannot be parsed
+ */
+ public static function getDataFromTestWithAnnotation($docComment)
+ {
+ $docComment = self::cleanUpMultiLineAnnotation($docComment);
+
+ if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) {
+ $offset = strlen($matches[0][0]) + $matches[0][1];
+ $annotationContent = substr($docComment, $offset);
+ $data = [];
+
+ foreach (explode("\n", $annotationContent) as $candidateRow) {
+ $candidateRow = trim($candidateRow);
+
+ if ($candidateRow[0] !== '[') {
+ break;
+ }
+
+ $dataSet = json_decode($candidateRow, true);
+
+ if (json_last_error() != JSON_ERROR_NONE) {
+ throw new PHPUnit_Framework_Exception(
+ 'The dataset for the @testWith annotation cannot be parsed: ' . json_last_error_msg()
+ );
+ }
+
+ $data[] = $dataSet;
+ }
+
+ if (!$data) {
+ throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.');
+ }
+
+ return $data;
+ }
+ }
+
+ private static function cleanUpMultiLineAnnotation($docComment)
+ {
+ //removing initial ' * ' for docComment
+ $docComment = str_replace("\r\n", "\n", $docComment);
+ $docComment = preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment);
+ $docComment = substr($docComment, 0, -1);
+ $docComment = rtrim($docComment, "\n");
+
+ return $docComment;
+ }
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ *
+ * @throws ReflectionException
+ */
+ public static function parseTestMethodAnnotations($className, $methodName = '')
+ {
+ if (!isset(self::$annotationCache[$className])) {
+ $class = new ReflectionClass($className);
+ self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
+ }
+
+ if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) {
+ try {
+ $method = new ReflectionMethod($className, $methodName);
+ $annotations = self::parseAnnotations($method->getDocComment());
+ } catch (ReflectionException $e) {
+ $annotations = [];
+ }
+ self::$annotationCache[$className . '::' . $methodName] = $annotations;
+ }
+
+ return [
+ 'class' => self::$annotationCache[$className],
+ 'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : []
+ ];
+ }
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getInlineAnnotations($className, $methodName)
+ {
+ $method = new ReflectionMethod($className, $methodName);
+ $code = file($method->getFileName());
+ $lineNumber = $method->getStartLine();
+ $startLine = $method->getStartLine() - 1;
+ $endLine = $method->getEndLine() - 1;
+ $methodLines = array_slice($code, $startLine, $endLine - $startLine + 1);
+ $annotations = [];
+
+ foreach ($methodLines as $line) {
+ if (preg_match('#/\*\*?\s*@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?\*/$#m', $line, $matches)) {
+ $annotations[strtolower($matches['name'])] = [
+ 'line' => $lineNumber,
+ 'value' => $matches['value']
+ ];
+ }
+
+ $lineNumber++;
+ }
+
+ return $annotations;
+ }
+
+ /**
+ * @param string $docblock
+ *
+ * @return array
+ */
+ private static function parseAnnotations($docblock)
+ {
+ $annotations = [];
+ // Strip away the docblock header and footer to ease parsing of one line annotations
+ $docblock = substr($docblock, 3, -2);
+
+ if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docblock, $matches)) {
+ $numMatches = count($matches[0]);
+
+ for ($i = 0; $i < $numMatches; ++$i) {
+ $annotations[$matches['name'][$i]][] = (string) $matches['value'][$i];
+ }
+ }
+
+ return $annotations;
+ }
+
+ /**
+ * Returns the backup settings for a test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getBackupSettings($className, $methodName)
+ {
+ return [
+ 'backupGlobals' => self::getBooleanAnnotationSetting(
+ $className,
+ $methodName,
+ 'backupGlobals'
+ ),
+ 'backupStaticAttributes' => self::getBooleanAnnotationSetting(
+ $className,
+ $methodName,
+ 'backupStaticAttributes'
+ )
+ ];
+ }
+
+ /**
+ * Returns the dependencies for a test class or method.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getDependencies($className, $methodName)
+ {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ $dependencies = [];
+
+ if (isset($annotations['class']['depends'])) {
+ $dependencies = $annotations['class']['depends'];
+ }
+
+ if (isset($annotations['method']['depends'])) {
+ $dependencies = array_merge(
+ $dependencies,
+ $annotations['method']['depends']
+ );
+ }
+
+ return array_unique($dependencies);
+ }
+
+ /**
+ * Returns the error handler settings for a test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return bool
+ */
+ public static function getErrorHandlerSettings($className, $methodName)
+ {
+ return self::getBooleanAnnotationSetting(
+ $className,
+ $methodName,
+ 'errorHandler'
+ );
+ }
+
+ /**
+ * Returns the groups for a test class or method.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getGroups($className, $methodName = '')
+ {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ $groups = [];
+
+ if (isset($annotations['method']['author'])) {
+ $groups = $annotations['method']['author'];
+ } elseif (isset($annotations['class']['author'])) {
+ $groups = $annotations['class']['author'];
+ }
+
+ if (isset($annotations['class']['group'])) {
+ $groups = array_merge($groups, $annotations['class']['group']);
+ }
+
+ if (isset($annotations['method']['group'])) {
+ $groups = array_merge($groups, $annotations['method']['group']);
+ }
+
+ if (isset($annotations['class']['ticket'])) {
+ $groups = array_merge($groups, $annotations['class']['ticket']);
+ }
+
+ if (isset($annotations['method']['ticket'])) {
+ $groups = array_merge($groups, $annotations['method']['ticket']);
+ }
+
+ foreach (['method', 'class'] as $element) {
+ foreach (['small', 'medium', 'large'] as $size) {
+ if (isset($annotations[$element][$size])) {
+ $groups[] = $size;
+ break 2;
+ }
+ }
+ }
+
+ return array_unique($groups);
+ }
+
+ /**
+ * Returns the size of the test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return int
+ */
+ public static function getSize($className, $methodName)
+ {
+ $groups = array_flip(self::getGroups($className, $methodName));
+ $size = self::UNKNOWN;
+ $class = new ReflectionClass($className);
+
+ if (isset($groups['large']) ||
+ (class_exists('PHPUnit_Extensions_Database_TestCase', false) &&
+ $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase'))) {
+ $size = self::LARGE;
+ } elseif (isset($groups['medium'])) {
+ $size = self::MEDIUM;
+ } elseif (isset($groups['small'])) {
+ $size = self::SMALL;
+ }
+
+ return $size;
+ }
+
+ /**
+ * Returns the tickets for a test class or method.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return array
+ */
+ public static function getTickets($className, $methodName)
+ {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ $tickets = [];
+
+ if (isset($annotations['class']['ticket'])) {
+ $tickets = $annotations['class']['ticket'];
+ }
+
+ if (isset($annotations['method']['ticket'])) {
+ $tickets = array_merge($tickets, $annotations['method']['ticket']);
+ }
+
+ return array_unique($tickets);
+ }
+
+ /**
+ * Returns the process isolation settings for a test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return bool
+ */
+ public static function getProcessIsolationSettings($className, $methodName)
+ {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ if (isset($annotations['class']['runTestsInSeparateProcesses']) ||
+ isset($annotations['method']['runInSeparateProcess'])) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the preserve global state settings for a test.
+ *
+ * @param string $className
+ * @param string $methodName
+ *
+ * @return bool
+ */
+ public static function getPreserveGlobalStateSettings($className, $methodName)
+ {
+ return self::getBooleanAnnotationSetting(
+ $className,
+ $methodName,
+ 'preserveGlobalState'
+ );
+ }
+
+ /**
+ * @param string $className
+ *
+ * @return array
+ */
+ public static function getHookMethods($className)
+ {
+ if (!class_exists($className, false)) {
+ return self::emptyHookMethodsArray();
+ }
+
+ if (!isset(self::$hookMethods[$className])) {
+ self::$hookMethods[$className] = self::emptyHookMethodsArray();
+
+ try {
+ $class = new ReflectionClass($className);
+
+ foreach ($class->getMethods() as $method) {
+ if (self::isBeforeClassMethod($method)) {
+ self::$hookMethods[$className]['beforeClass'][] = $method->getName();
+ }
+
+ if (self::isBeforeMethod($method)) {
+ self::$hookMethods[$className]['before'][] = $method->getName();
+ }
+
+ if (self::isAfterMethod($method)) {
+ self::$hookMethods[$className]['after'][] = $method->getName();
+ }
+
+ if (self::isAfterClassMethod($method)) {
+ self::$hookMethods[$className]['afterClass'][] = $method->getName();
+ }
+ }
+ } catch (ReflectionException $e) {
+ }
+ }
+
+ return self::$hookMethods[$className];
+ }
+
+ /**
+ * @return array
+ */
+ private static function emptyHookMethodsArray()
+ {
+ return [
+ 'beforeClass' => ['setUpBeforeClass'],
+ 'before' => ['setUp'],
+ 'after' => ['tearDown'],
+ 'afterClass' => ['tearDownAfterClass']
+ ];
+ }
+
+ /**
+ * @param string $className
+ * @param string $methodName
+ * @param string $settingName
+ *
+ * @return bool
+ */
+ private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
+ {
+ $annotations = self::parseTestMethodAnnotations(
+ $className,
+ $methodName
+ );
+
+ $result = null;
+
+ if (isset($annotations['class'][$settingName])) {
+ if ($annotations['class'][$settingName][0] == 'enabled') {
+ $result = true;
+ } elseif ($annotations['class'][$settingName][0] == 'disabled') {
+ $result = false;
+ }
+ }
+
+ if (isset($annotations['method'][$settingName])) {
+ if ($annotations['method'][$settingName][0] == 'enabled') {
+ $result = true;
+ } elseif ($annotations['method'][$settingName][0] == 'disabled') {
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param string $element
+ *
+ * @return array
+ *
+ * @throws PHPUnit_Framework_InvalidCoversTargetException
+ */
+ private static function resolveElementToReflectionObjects($element)
+ {
+ $codeToCoverList = [];
+
+ if (strpos($element, '\\') !== false && function_exists($element)) {
+ $codeToCoverList[] = new ReflectionFunction($element);
+ } elseif (strpos($element, '::') !== false) {
+ list($className, $methodName) = explode('::', $element);
+
+ if (isset($methodName[0]) && $methodName[0] == '<') {
+ $classes = [$className];
+
+ foreach ($classes as $className) {
+ if (!class_exists($className) &&
+ !interface_exists($className) &&
+ !trait_exists($className)) {
+ throw new PHPUnit_Framework_InvalidCoversTargetException(
+ sprintf(
+ 'Trying to @cover or @use not existing class or ' .
+ 'interface "%s".',
+ $className
+ )
+ );
+ }
+
+ $class = new ReflectionClass($className);
+ $methods = $class->getMethods();
+ $inverse = isset($methodName[1]) && $methodName[1] == '!';
+
+ if (strpos($methodName, 'protected')) {
+ $visibility = 'isProtected';
+ } elseif (strpos($methodName, 'private')) {
+ $visibility = 'isPrivate';
+ } elseif (strpos($methodName, 'public')) {
+ $visibility = 'isPublic';
+ }
+
+ foreach ($methods as $method) {
+ if ($inverse && !$method->$visibility()) {
+ $codeToCoverList[] = $method;
+ } elseif (!$inverse && $method->$visibility()) {
+ $codeToCoverList[] = $method;
+ }
+ }
+ }
+ } else {
+ $classes = [$className];
+
+ foreach ($classes as $className) {
+ if ($className == '' && function_exists($methodName)) {
+ $codeToCoverList[] = new ReflectionFunction(
+ $methodName
+ );
+ } else {
+ if (!((class_exists($className) ||
+ interface_exists($className) ||
+ trait_exists($className)) &&
+ method_exists($className, $methodName))) {
+ throw new PHPUnit_Framework_InvalidCoversTargetException(
+ sprintf(
+ 'Trying to @cover or @use not existing method "%s::%s".',
+ $className,
+ $methodName
+ )
+ );
+ }
+
+ $codeToCoverList[] = new ReflectionMethod(
+ $className,
+ $methodName
+ );
+ }
+ }
+ }
+ } else {
+ $extended = false;
+
+ if (strpos($element, '<extended>') !== false) {
+ $element = str_replace('<extended>', '', $element);
+ $extended = true;
+ }
+
+ $classes = [$element];
+
+ if ($extended) {
+ $classes = array_merge(
+ $classes,
+ class_implements($element),
+ class_parents($element)
+ );
+ }
+
+ foreach ($classes as $className) {
+ if (!class_exists($className) &&
+ !interface_exists($className) &&
+ !trait_exists($className)) {
+ throw new PHPUnit_Framework_InvalidCoversTargetException(
+ sprintf(
+ 'Trying to @cover or @use not existing class or ' .
+ 'interface "%s".',
+ $className
+ )
+ );
+ }
+
+ $codeToCoverList[] = new ReflectionClass($className);
+ }
+ }
+
+ return $codeToCoverList;
+ }
+
+ /**
+ * @param array $reflectors
+ *
+ * @return array
+ */
+ private static function resolveReflectionObjectsToLines(array $reflectors)
+ {
+ $result = [];
+
+ foreach ($reflectors as $reflector) {
+ $filename = $reflector->getFileName();
+
+ if (!isset($result[$filename])) {
+ $result[$filename] = [];
+ }
+
+ $result[$filename] = array_unique(
+ array_merge(
+ $result[$filename],
+ range($reflector->getStartLine(), $reflector->getEndLine())
+ )
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ private static function isBeforeClassMethod(ReflectionMethod $method)
+ {
+ return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false;
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ private static function isBeforeMethod(ReflectionMethod $method)
+ {
+ return preg_match('/@before\b/', $method->getDocComment());
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ private static function isAfterClassMethod(ReflectionMethod $method)
+ {
+ return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false;
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ *
+ * @return bool
+ */
+ private static function isAfterMethod(ReflectionMethod $method)
+ {
+ return preg_match('/@after\b/', $method->getDocComment());
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Prettifies class and method names for use in TestDox documentation.
+ */
+class PHPUnit_Util_TestDox_NamePrettifier
+{
+ /**
+ * @var string
+ */
+ protected $prefix = 'Test';
+
+ /**
+ * @var string
+ */
+ protected $suffix = 'Test';
+
+ /**
+ * @var array
+ */
+ protected $strings = [];
+
+ /**
+ * Prettifies the name of a test class.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function prettifyTestClass($name)
+ {
+ $title = $name;
+
+ if ($this->suffix !== null &&
+ $this->suffix == substr($name, -1 * strlen($this->suffix))) {
+ $title = substr($title, 0, strripos($title, $this->suffix));
+ }
+
+ if ($this->prefix !== null &&
+ $this->prefix == substr($name, 0, strlen($this->prefix))) {
+ $title = substr($title, strlen($this->prefix));
+ }
+
+ if (substr($title, 0, 1) == '\\') {
+ $title = substr($title, 1);
+ }
+
+ return $title;
+ }
+
+ /**
+ * Prettifies the name of a test method.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function prettifyTestMethod($name)
+ {
+ $buffer = '';
+
+ if (!is_string($name) || strlen($name) == 0) {
+ return $buffer;
+ }
+
+ $string = preg_replace('#\d+$#', '', $name, -1, $count);
+
+ if (in_array($string, $this->strings)) {
+ $name = $string;
+ } elseif ($count == 0) {
+ $this->strings[] = $string;
+ }
+
+ if (substr($name, 0, 4) == 'test') {
+ $name = substr($name, 4);
+ }
+
+ if (strlen($name) == 0) {
+ return $buffer;
+ }
+
+ $name[0] = strtoupper($name[0]);
+
+ if (strpos($name, '_') !== false) {
+ return trim(str_replace('_', ' ', $name));
+ }
+
+ $max = strlen($name);
+ $wasNumeric = false;
+
+ for ($i = 0; $i < $max; $i++) {
+ if ($i > 0 &&
+ ord($name[$i]) >= 65 &&
+ ord($name[$i]) <= 90) {
+ $buffer .= ' ' . strtolower($name[$i]);
+ } else {
+ $isNumeric = is_numeric($name[$i]);
+
+ if (!$wasNumeric && $isNumeric) {
+ $buffer .= ' ';
+ $wasNumeric = true;
+ }
+
+ if ($wasNumeric && !$isNumeric) {
+ $wasNumeric = false;
+ }
+
+ $buffer .= $name[$i];
+ }
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Sets the prefix of test names.
+ *
+ * @param string $prefix
+ */
+ public function setPrefix($prefix)
+ {
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * Sets the suffix of test names.
+ *
+ * @param string $suffix
+ */
+ public function setSuffix($suffix)
+ {
+ $this->suffix = $suffix;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Base class for printers of TestDox documentation.
+ */
+abstract class PHPUnit_Util_TestDox_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
+{
+ /**
+ * @var PHPUnit_Util_TestDox_NamePrettifier
+ */
+ protected $prettifier;
+
+ /**
+ * @var string
+ */
+ protected $testClass = '';
+
+ /**
+ * @var int
+ */
+ protected $testStatus = false;
+
+ /**
+ * @var array
+ */
+ protected $tests = [];
+
+ /**
+ * @var int
+ */
+ protected $successful = 0;
+
+ /**
+ * @var int
+ */
+ protected $warned = 0;
+
+ /**
+ * @var int
+ */
+ protected $failed = 0;
+
+ /**
+ * @var int
+ */
+ protected $risky = 0;
+
+ /**
+ * @var int
+ */
+ protected $skipped = 0;
+
+ /**
+ * @var int
+ */
+ protected $incomplete = 0;
+
+ /**
+ * @var string
+ */
+ protected $currentTestClassPrettified;
+
+ /**
+ * @var string
+ */
+ protected $currentTestMethodPrettified;
+
+ /**
+ * @var array
+ */
+ private $groups;
+
+ /**
+ * @var array
+ */
+ private $excludeGroups;
+
+ /**
+ * @param resource $out
+ * @param array $groups
+ * @param array $excludeGroups
+ */
+ public function __construct($out = null, array $groups = [], array $excludeGroups = [])
+ {
+ parent::__construct($out);
+
+ $this->groups = $groups;
+ $this->excludeGroups = $excludeGroups;
+
+ $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier;
+ $this->startRun();
+ }
+
+ /**
+ * Flush buffer and close output.
+ */
+ public function flush()
+ {
+ $this->doEndClass();
+ $this->endRun();
+
+ parent::flush();
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR;
+ $this->failed++;
+ }
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_WARNING;
+ $this->warned++;
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE;
+ $this->failed++;
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE;
+ $this->incomplete++;
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY;
+ $this->risky++;
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED;
+ $this->skipped++;
+ }
+
+ /**
+ * A testsuite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ /**
+ * A testsuite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ $class = get_class($test);
+
+ if ($this->testClass != $class) {
+ if ($this->testClass != '') {
+ $this->doEndClass();
+ }
+
+ $classAnnotations = PHPUnit_Util_Test::parseTestMethodAnnotations($class);
+ if (isset($classAnnotations['class']['testdox'][0])) {
+ $this->currentTestClassPrettified = $classAnnotations['class']['testdox'][0];
+ } else {
+ $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class);
+ }
+
+ $this->startClass($class);
+
+ $this->testClass = $class;
+ $this->tests = [];
+ }
+
+ $annotations = $test->getAnnotations();
+
+ if (isset($annotations['method']['testdox'][0])) {
+ $this->currentTestMethodPrettified = $annotations['method']['testdox'][0];
+ } else {
+ $this->currentTestMethodPrettified = $this->prettifier->prettifyTestMethod($test->getName(false));
+ }
+
+ if ($test instanceof PHPUnit_Framework_TestCase && $test->usesDataProvider()) {
+ $this->currentTestMethodPrettified .= ' ' . $test->dataDescription();
+ }
+
+ $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED;
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ if (!$this->isOfInterest($test)) {
+ return;
+ }
+
+ if (!isset($this->tests[$this->currentTestMethodPrettified])) {
+ if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
+ $this->tests[$this->currentTestMethodPrettified]['success'] = 1;
+ $this->tests[$this->currentTestMethodPrettified]['failure'] = 0;
+ } else {
+ $this->tests[$this->currentTestMethodPrettified]['success'] = 0;
+ $this->tests[$this->currentTestMethodPrettified]['failure'] = 1;
+ }
+ } else {
+ if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
+ $this->tests[$this->currentTestMethodPrettified]['success']++;
+ } else {
+ $this->tests[$this->currentTestMethodPrettified]['failure']++;
+ }
+ }
+
+ $this->currentTestClassPrettified = null;
+ $this->currentTestMethodPrettified = null;
+ }
+
+ protected function doEndClass()
+ {
+ foreach ($this->tests as $name => $data) {
+ $this->onTest($name, $data['failure'] == 0);
+ }
+
+ $this->endClass($this->testClass);
+ }
+
+ /**
+ * Handler for 'start run' event.
+ */
+ protected function startRun()
+ {
+ }
+
+ /**
+ * Handler for 'start class' event.
+ *
+ * @param string $name
+ */
+ protected function startClass($name)
+ {
+ }
+
+ /**
+ * Handler for 'on test' event.
+ *
+ * @param string $name
+ * @param bool $success
+ */
+ protected function onTest($name, $success = true)
+ {
+ }
+
+ /**
+ * Handler for 'end class' event.
+ *
+ * @param string $name
+ */
+ protected function endClass($name)
+ {
+ }
+
+ /**
+ * Handler for 'end run' event.
+ */
+ protected function endRun()
+ {
+ }
+
+ /**
+ * @param PHPUnit_Framework_Test $test
+ *
+ * @return bool
+ */
+ private function isOfInterest(PHPUnit_Framework_Test $test)
+ {
+ if (!$test instanceof PHPUnit_Framework_TestCase) {
+ return false;
+ }
+
+ if ($test instanceof PHPUnit_Framework_WarningTestCase) {
+ return false;
+ }
+
+ if (!empty($this->groups)) {
+ foreach ($test->getGroups() as $group) {
+ if (in_array($group, $this->groups)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (!empty($this->excludeGroups)) {
+ foreach ($test->getGroups() as $group) {
+ if (in_array($group, $this->excludeGroups)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return true;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Prints TestDox documentation in HTML format.
+ */
+class PHPUnit_Util_TestDox_ResultPrinter_HTML extends PHPUnit_Util_TestDox_ResultPrinter
+{
+ /**
+ * @var string
+ */
+ private $pageHeader = <<<EOT
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Test Documentation</title>
+ <style>
+ body {
+ text-rendering: optimizeLegibility;
+ font-variant-ligatures: common-ligatures;
+ font-kerning: normal;
+ margin-left: 2em;
+ }
+
+ body > ul > li {
+ font-family: Source Serif Pro, PT Sans, Trebuchet MS, Helvetica, Arial;
+ font-size: 2em;
+ }
+
+ h2 {
+ font-family: Tahoma, Helvetica, Arial;
+ font-size: 3em;
+ }
+
+ ul {
+ list-style: none;
+ margin-bottom: 1em;
+ }
+ </style>
+ </head>
+ <body>
+EOT;
+
+ /**
+ * @var string
+ */
+ private $classHeader = <<<EOT
+
+ <h2 id="%s">%s</h2>
+ <ul>
+
+EOT;
+
+ /**
+ * @var string
+ */
+ private $classFooter = <<<EOT
+ </ul>
+EOT;
+
+ /**
+ * @var string
+ */
+ private $pageFooter = <<<EOT
+
+ </body>
+</html>
+EOT;
+
+ /**
+ * Handler for 'start run' event.
+ */
+ protected function startRun()
+ {
+ $this->write($this->pageHeader);
+ }
+
+ /**
+ * Handler for 'start class' event.
+ *
+ * @param string $name
+ */
+ protected function startClass($name)
+ {
+ $this->write(
+ sprintf(
+ $this->classHeader,
+ $name,
+ $this->currentTestClassPrettified
+ )
+ );
+ }
+
+ /**
+ * Handler for 'on test' event.
+ *
+ * @param string $name
+ * @param bool $success
+ */
+ protected function onTest($name, $success = true)
+ {
+ $this->write(
+ sprintf(
+ " <li style=\"color: %s;\">%s %s</li>\n",
+ $success ? '#555753' : '#ef2929',
+ $success ? '✓' : '❌',
+ $name
+ )
+ );
+ }
+
+ /**
+ * Handler for 'end class' event.
+ *
+ * @param string $name
+ */
+ protected function endClass($name)
+ {
+ $this->write($this->classFooter);
+ }
+
+ /**
+ * Handler for 'end run' event.
+ */
+ protected function endRun()
+ {
+ $this->write($this->pageFooter);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Prints TestDox documentation in text format.
+ */
+class PHPUnit_Util_TestDox_ResultPrinter_Text extends PHPUnit_Util_TestDox_ResultPrinter
+{
+ /**
+ * Handler for 'start class' event.
+ *
+ * @param string $name
+ */
+ protected function startClass($name)
+ {
+ $this->write($this->currentTestClassPrettified . "\n");
+ }
+
+ /**
+ * Handler for 'on test' event.
+ *
+ * @param string $name
+ * @param bool $success
+ */
+ protected function onTest($name, $success = true)
+ {
+ if ($success) {
+ $this->write(' [x] ');
+ } else {
+ $this->write(' [ ] ');
+ }
+
+ $this->write($name . "\n");
+ }
+
+ /**
+ * Handler for 'end class' event.
+ *
+ * @param string $name
+ */
+ protected function endClass($name)
+ {
+ $this->write("\n");
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class PHPUnit_Util_TestDox_ResultPrinter_XML extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
+{
+ /**
+ * @var DOMDocument
+ */
+ private $document;
+
+ /**
+ * @var DOMElement
+ */
+ private $root;
+
+ /**
+ * @var PHPUnit_Util_TestDox_NamePrettifier
+ */
+ private $prettifier;
+
+ /**
+ * @var Exception
+ */
+ private $exception;
+
+ /**
+ * @param string|resource $out
+ */
+ public function __construct($out = null)
+ {
+ $this->document = new DOMDocument('1.0', 'UTF-8');
+ $this->document->formatOutput = true;
+
+ $this->root = $this->document->createElement('tests');
+ $this->document->appendChild($this->root);
+
+ $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier;
+
+ parent::__construct($out);
+ }
+
+ /**
+ * Flush buffer and close output.
+ */
+ public function flush()
+ {
+ $this->write($this->document->saveXML());
+
+ parent::flush();
+ }
+
+ /**
+ * An error occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->exception = $e;
+ }
+
+ /**
+ * A warning occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_Warning $e
+ * @param float $time
+ */
+ public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
+ {
+ }
+
+ /**
+ * A failure occurred.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param PHPUnit_Framework_AssertionFailedError $e
+ * @param float $time
+ */
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ $this->exception = $e;
+ }
+
+ /**
+ * Incomplete test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ /**
+ * Risky test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ /**
+ * Skipped test.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param Exception $e
+ * @param float $time
+ */
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ }
+
+ /**
+ * A test suite started.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ /**
+ * A test suite ended.
+ *
+ * @param PHPUnit_Framework_TestSuite $suite
+ */
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ /**
+ * A test started.
+ *
+ * @param PHPUnit_Framework_Test $test
+ */
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ $this->exception = null;
+ }
+
+ /**
+ * A test ended.
+ *
+ * @param PHPUnit_Framework_Test $test
+ * @param float $time
+ */
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ if (!$test instanceof PHPUnit_Framework_TestCase) {
+ return;
+ }
+
+ /* @var PHPUnit_Framework_TestCase $test */
+
+ $groups = array_filter(
+ $test->getGroups(),
+ function ($group) {
+ if ($group == 'small' || $group == 'medium' || $group == 'large') {
+ return false;
+ }
+
+ return true;
+ }
+ );
+
+ $node = $this->document->createElement('test');
+
+ $node->setAttribute('className', get_class($test));
+ $node->setAttribute('methodName', $test->getName());
+ $node->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(get_class($test)));
+ $node->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestMethod($test->getName()));
+ $node->setAttribute('status', $test->getStatus());
+ $node->setAttribute('time', $time);
+ $node->setAttribute('size', $test->getSize());
+ $node->setAttribute('groups', implode(',', $groups));
+
+ $inlineAnnotations = PHPUnit_Util_Test::getInlineAnnotations(get_class($test), $test->getName());
+
+ if (isset($inlineAnnotations['given']) && isset($inlineAnnotations['when']) && isset($inlineAnnotations['then'])) {
+ $node->setAttribute('given', $inlineAnnotations['given']['value']);
+ $node->setAttribute('givenStartLine', $inlineAnnotations['given']['line']);
+ $node->setAttribute('when', $inlineAnnotations['when']['value']);
+ $node->setAttribute('whenStartLine', $inlineAnnotations['when']['line']);
+ $node->setAttribute('then', $inlineAnnotations['then']['value']);
+ $node->setAttribute('thenStartLine', $inlineAnnotations['then']['line']);
+ }
+
+ if ($this->exception !== null) {
+ if ($this->exception instanceof PHPUnit_Framework_Exception) {
+ $steps = $this->exception->getSerializableTrace();
+ } else {
+ $steps = $this->exception->getTrace();
+ }
+
+ $class = new ReflectionClass($test);
+ $file = $class->getFileName();
+
+ foreach ($steps as $step) {
+ if (isset($step['file']) && $step['file'] == $file) {
+ $node->setAttribute('exceptionLine', $step['line']);
+
+ break;
+ }
+ }
+
+ $node->setAttribute('exceptionMessage', $this->exception->getMessage());
+ }
+
+ $this->root->appendChild($node);
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Iterator for test suites.
+ */
+class PHPUnit_Util_TestSuiteIterator implements RecursiveIterator
+{
+ /**
+ * @var int
+ */
+ protected $position;
+
+ /**
+ * @var PHPUnit_Framework_Test[]
+ */
+ protected $tests;
+
+ /**
+ * @param PHPUnit_Framework_TestSuite $testSuite
+ */
+ public function __construct(PHPUnit_Framework_TestSuite $testSuite)
+ {
+ $this->tests = $testSuite->tests();
+ }
+
+ /**
+ * Rewinds the Iterator to the first element.
+ */
+ public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ /**
+ * Checks if there is a current element after calls to rewind() or next().
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return $this->position < count($this->tests);
+ }
+
+ /**
+ * Returns the key of the current element.
+ *
+ * @return int
+ */
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * Returns the current element.
+ *
+ * @return PHPUnit_Framework_Test
+ */
+ public function current()
+ {
+ return $this->valid() ? $this->tests[$this->position] : null;
+ }
+
+ /**
+ * Moves forward to next element.
+ */
+ public function next()
+ {
+ $this->position++;
+ }
+
+ /**
+ * Returns the sub iterator for the current element.
+ *
+ * @return PHPUnit_Util_TestSuiteIterator
+ */
+ public function getChildren()
+ {
+ return new self(
+ $this->tests[$this->position]
+ );
+ }
+
+ /**
+ * Checks whether the current element has children.
+ *
+ * @return bool
+ */
+ public function hasChildren()
+ {
+ return $this->tests[$this->position] instanceof PHPUnit_Framework_TestSuite;
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Utility class for textual type (and value) representation.
+ */
+class PHPUnit_Util_Type
+{
+ /**
+ * @param string $type
+ *
+ * @return bool
+ */
+ public static function isType($type)
+ {
+ return in_array(
+ $type,
+ [
+ 'numeric',
+ 'integer',
+ 'int',
+ 'float',
+ 'string',
+ 'boolean',
+ 'bool',
+ 'null',
+ 'array',
+ 'object',
+ 'resource',
+ 'scalar'
+ ]
+ );
+ }
+}
+<?php
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * XML helpers.
+ */
+class PHPUnit_Util_XML
+{
+ /**
+ * Load an $actual document into a DOMDocument. This is called
+ * from the selector assertions.
+ *
+ * If $actual is already a DOMDocument, it is returned with
+ * no changes. Otherwise, $actual is loaded into a new DOMDocument
+ * as either HTML or XML, depending on the value of $isHtml. If $isHtml is
+ * false and $xinclude is true, xinclude is performed on the loaded
+ * DOMDocument.
+ *
+ * Note: prior to PHPUnit 3.3.0, this method loaded a file and
+ * not a string as it currently does. To load a file into a
+ * DOMDocument, use loadFile() instead.
+ *
+ * @param string|DOMDocument $actual
+ * @param bool $isHtml
+ * @param string $filename
+ * @param bool $xinclude
+ * @param bool $strict
+ *
+ * @return DOMDocument
+ */
+ public static function load($actual, $isHtml = false, $filename = '', $xinclude = false, $strict = false)
+ {
+ if ($actual instanceof DOMDocument) {
+ return $actual;
+ }
+
+ if (!is_string($actual)) {
+ throw new PHPUnit_Framework_Exception('Could not load XML from ' . gettype($actual));
+ }
+
+ if ($actual === '') {
+ throw new PHPUnit_Framework_Exception('Could not load XML from empty string');
+ }
+
+ // Required for XInclude on Windows.
+ if ($xinclude) {
+ $cwd = getcwd();
+ @chdir(dirname($filename));
+ }
+
+ $document = new DOMDocument;
+ $document->preserveWhiteSpace = false;
+
+ $internal = libxml_use_internal_errors(true);
+ $message = '';
+ $reporting = error_reporting(0);
+
+ if ('' !== $filename) {
+ // Necessary for xinclude
+ $document->documentURI = $filename;
+ }
+
+ if ($isHtml) {
+ $loaded = $document->loadHTML($actual);
+ } else {
+ $loaded = $document->loadXML($actual);
+ }
+
+ if (!$isHtml && $xinclude) {
+ $document->xinclude();
+ }
+
+ foreach (libxml_get_errors() as $error) {
+ $message .= "\n" . $error->message;
+ }
+
+ libxml_use_internal_errors($internal);
+ error_reporting($reporting);
+
+ if ($xinclude) {
+ @chdir($cwd);
+ }
+
+ if ($loaded === false || ($strict && $message !== '')) {
+ if ($filename !== '') {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Could not load "%s".%s',
+ $filename,
+ $message != '' ? "\n" . $message : ''
+ )
+ );
+ } else {
+ if ($message === '') {
+ $message = 'Could not load XML for unknown reason';
+ }
+ throw new PHPUnit_Framework_Exception($message);
+ }
+ }
+
+ return $document;
+ }
+
+ /**
+ * Loads an XML (or HTML) file into a DOMDocument object.
+ *
+ * @param string $filename
+ * @param bool $isHtml
+ * @param bool $xinclude
+ * @param bool $strict
+ *
+ * @return DOMDocument
+ */
+ public static function loadFile($filename, $isHtml = false, $xinclude = false, $strict = false)
+ {
+ $reporting = error_reporting(0);
+ $contents = file_get_contents($filename);
+ error_reporting($reporting);
+
+ if ($contents === false) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Could not read "%s".',
+ $filename
+ )
+ );
+ }
+
+ return self::load($contents, $isHtml, $filename, $xinclude, $strict);
+ }
+
+ /**
+ * @param DOMNode $node
+ */
+ public static function removeCharacterDataNodes(DOMNode $node)
+ {
+ if ($node->hasChildNodes()) {
+ for ($i = $node->childNodes->length - 1; $i >= 0; $i--) {
+ if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) {
+ $node->removeChild($child);
+ }
+ }
+ }
+ }
+
+ /**
+ * Escapes a string for the use in XML documents
+ * Any Unicode character is allowed, excluding the surrogate blocks, FFFE,
+ * and FFFF (not even as character reference).
+ * See http://www.w3.org/TR/xml/#charsets
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function prepareString($string)
+ {
+ return preg_replace(
+ '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/',
+ '',
+ htmlspecialchars(
+ PHPUnit_Util_String::convertToUtf8($string),
+ ENT_QUOTES,
+ 'UTF-8'
+ )
+ );
+ }
+
+ /**
+ * "Convert" a DOMElement object into a PHP variable.
+ *
+ * @param DOMElement $element
+ *
+ * @return mixed
+ */
+ public static function xmlToVariable(DOMElement $element)
+ {
+ $variable = null;
+
+ switch ($element->tagName) {
+ case 'array':
+ $variable = [];
+
+ foreach ($element->childNodes as $entry) {
+ if (!$entry instanceof DOMElement || $entry->tagName !== 'element') {
+ continue;
+ }
+ $item = $entry->childNodes->item(0);
+
+ if ($item instanceof DOMText) {
+ $item = $entry->childNodes->item(1);
+ }
+
+ $value = self::xmlToVariable($item);
+
+ if ($entry->hasAttribute('key')) {
+ $variable[(string) $entry->getAttribute('key')] = $value;
+ } else {
+ $variable[] = $value;
+ }
+ }
+ break;
+
+ case 'object':
+ $className = $element->getAttribute('class');
+
+ if ($element->hasChildNodes()) {
+ $arguments = $element->childNodes->item(1)->childNodes;
+ $constructorArgs = [];
+
+ foreach ($arguments as $argument) {
+ if ($argument instanceof DOMElement) {
+ $constructorArgs[] = self::xmlToVariable($argument);
+ }
+ }
+
+ $class = new ReflectionClass($className);
+ $variable = $class->newInstanceArgs($constructorArgs);
+ } else {
+ $variable = new $className;
+ }
+ break;
+
+ case 'boolean':
+ $variable = $element->textContent == 'true' ? true : false;
+ break;
+
+ case 'integer':
+ case 'double':
+ case 'string':
+ $variable = $element->textContent;
+
+ settype($variable, $element->tagName);
+ break;
+ }
+
+ return $variable;
+ }
+}
+R<?`XdHŽ