summaryrefslogtreecommitdiff
path: root/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch')
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php48
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php76
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php63
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php68
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php105
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php113
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php57
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php123
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ThrowablePatch.php95
-rw-r--r--vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php98
10 files changed, 846 insertions, 0 deletions
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php
new file mode 100644
index 000000000..d6d196850
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php
@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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 <[email protected]>
+ */
+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();
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php
new file mode 100644
index 000000000..9d843099d
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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 <[email protected]>
+ */
+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->isExtendable('__construct')) {
+ return;
+ }
+
+ 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;
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php
new file mode 100644
index 000000000..fa38fc0d1
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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 <[email protected]>
+ */
+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;
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php
new file mode 100644
index 000000000..ab99f74be
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php
@@ -0,0 +1,68 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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 <[email protected]>
+ */
+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 ['__halt_compiler'];
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php
new file mode 100644
index 000000000..a545eeff5
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php
@@ -0,0 +1,105 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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\ArgumentNode;
+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 <[email protected]>
+ * @author Kévin Dunglas <[email protected]>
+ * @author Théo FIDRY <[email protected]>
+ */
+class MagicCallPatch implements ClassPatchInterface
+{
+ const MAGIC_METHODS_WITH_ARGUMENTS = ['__call', '__callStatic', '__get', '__isset', '__set', '__set_state', '__unserialize', '__unset'];
+
+ 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);
+
+ while ($reflectionClass) {
+ $tagList = $this->tagRetriever->getTagList($reflectionClass);
+
+ foreach ($tagList as $tag) {
+ $methodName = $tag->getMethodName();
+
+ if (empty($methodName)) {
+ continue;
+ }
+
+ if (!$reflectionClass->hasMethod($methodName)) {
+ $methodNode = new MethodNode($methodName);
+
+ // only magic methods can have a contract that needs to be enforced
+ if (in_array($methodName, self::MAGIC_METHODS_WITH_ARGUMENTS)) {
+ foreach($tag->getArguments() as $argument) {
+ $argumentNode = new ArgumentNode($argument['name']);
+ $methodNode->addArgument($argumentNode);
+ }
+ }
+
+ $methodNode->setStatic($tag->isStatic());
+ $node->addMethod($methodNode);
+ }
+ }
+
+ $reflectionClass = $reflectionClass->getParentClass();
+ }
+ }
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return integer Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 50;
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php
new file mode 100644
index 000000000..7573ca50e
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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\ArgumentTypeNode;
+use Prophecy\Doubler\Generator\Node\ClassNode;
+use Prophecy\Doubler\Generator\Node\MethodNode;
+use Prophecy\Doubler\Generator\Node\ArgumentNode;
+use Prophecy\Doubler\Generator\Node\ReturnTypeNode;
+
+/**
+ * Add Prophecy functionality to the double.
+ * This is a core class patch for Prophecy.
+ *
+ * @author Konstantin Kudryashov <[email protected]>
+ */
+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('objectProphecyClosure', 'private');
+
+ foreach ($node->getMethods() as $name => $method) {
+ if ('__construct' === strtolower($name)) {
+ continue;
+ }
+
+ if (!$method->getReturnTypeNode()->hasReturnStatement()) {
+ $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->setTypeNode(new ArgumentTypeNode('Prophecy\Prophecy\ProphecyInterface'));
+ $prophecySetter->addArgument($prophecyArgument);
+ $prophecySetter->setCode(<<<PHP
+if (null === \$this->objectProphecyClosure) {
+ \$this->objectProphecyClosure = static function () use (\$prophecy) {
+ return \$prophecy;
+ };
+}
+PHP
+ );
+
+ $prophecyGetter = new MethodNode('getProphecy');
+ $prophecyGetter->setCode('return \call_user_func($this->objectProphecyClosure);');
+
+ 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, true);
+ }
+
+ $__call->setCode(<<<PHP
+throw new \Prophecy\Exception\Doubler\MethodNotFoundException(
+ sprintf('Method `%s::%s()` not found.', get_class(\$this), func_get_arg(0)),
+ get_class(\$this), func_get_arg(0)
+);
+PHP
+ );
+
+ $node->addMethod($prophecySetter, true);
+ $node->addMethod($prophecyGetter, true);
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 0;
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php
new file mode 100644
index 000000000..9166aeefa
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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 <[email protected]>
+ */
+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;
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php
new file mode 100644
index 000000000..ceee94a2e
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php
@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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 <[email protected]>
+ */
+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)) {
+ $filePath = str_replace('\\','\\\\',__FILE__);
+ $constructor->setCode('return parent::__construct("' . $filePath .'");');
+
+ return;
+ }
+
+ if ($this->nodeIsSymfonySplFileInfo($node)) {
+ $filePath = str_replace('\\','\\\\',__FILE__);
+ $constructor->setCode('return parent::__construct("' . $filePath .'", "", "");');
+
+ 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');
+ }
+
+ /**
+ * @param ClassNode $node
+ * @return boolean
+ */
+ private function nodeIsSymfonySplFileInfo(ClassNode $node)
+ {
+ $parent = $node->getParentClass();
+
+ return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent;
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ThrowablePatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ThrowablePatch.php
new file mode 100644
index 000000000..b98e94327
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ThrowablePatch.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Prophecy\Doubler\ClassPatch;
+
+use Prophecy\Doubler\Generator\Node\ClassNode;
+use Prophecy\Exception\Doubler\ClassCreatorException;
+
+class ThrowablePatch implements ClassPatchInterface
+{
+ /**
+ * Checks if patch supports specific class node.
+ *
+ * @param ClassNode $node
+ * @return bool
+ */
+ public function supports(ClassNode $node)
+ {
+ return $this->implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node);
+ }
+
+ /**
+ * @param ClassNode $node
+ * @return bool
+ */
+ private function implementsAThrowableInterface(ClassNode $node)
+ {
+ foreach ($node->getInterfaces() as $type) {
+ if (is_a($type, 'Throwable', true)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param ClassNode $node
+ * @return bool
+ */
+ private function doesNotExtendAThrowableClass(ClassNode $node)
+ {
+ return !is_a($node->getParentClass(), 'Throwable', true);
+ }
+
+ /**
+ * Applies patch to the specific class node.
+ *
+ * @param ClassNode $node
+ *
+ * @return void
+ */
+ public function apply(ClassNode $node)
+ {
+ $this->checkItCanBeDoubled($node);
+ $this->setParentClassToException($node);
+ }
+
+ private function checkItCanBeDoubled(ClassNode $node)
+ {
+ $className = $node->getParentClass();
+ if ($className !== 'stdClass') {
+ throw new ClassCreatorException(
+ sprintf(
+ 'Cannot double concrete class %s as well as implement Traversable',
+ $className
+ ),
+ $node
+ );
+ }
+ }
+
+ private function setParentClassToException(ClassNode $node)
+ {
+ $node->setParentClass('Exception');
+
+ $node->removeMethod('getMessage');
+ $node->removeMethod('getCode');
+ $node->removeMethod('getFile');
+ $node->removeMethod('getLine');
+ $node->removeMethod('getTrace');
+ $node->removeMethod('getPrevious');
+ $node->removeMethod('getNext');
+ $node->removeMethod('getTraceAsString');
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 100;
+ }
+}
diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php
new file mode 100644
index 000000000..0e2e04700
--- /dev/null
+++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php
@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * This file is part of the Prophecy.
+ * (c) Konstantin Kudryashov <[email protected]>
+ * Marcello Duarte <[email protected]>
+ *
+ * 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\ReturnTypeNode;
+
+/**
+ * Traversable interface patch.
+ * Forces classes that implement interfaces, that extend Traversable to also implement Iterator.
+ *
+ * @author Konstantin Kudryashov <[email protected]>
+ */
+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');
+
+ $currentMethod = new MethodNode('current');
+ (\PHP_VERSION_ID >= 80100) && $currentMethod->setReturnTypeNode(new ReturnTypeNode('mixed'));
+ $node->addMethod($currentMethod);
+
+ $keyMethod = new MethodNode('key');
+ (\PHP_VERSION_ID >= 80100) && $keyMethod->setReturnTypeNode(new ReturnTypeNode('mixed'));
+ $node->addMethod($keyMethod);
+
+ $nextMethod = new MethodNode('next');
+ (\PHP_VERSION_ID >= 80100) && $nextMethod->setReturnTypeNode(new ReturnTypeNode('void'));
+ $node->addMethod($nextMethod);
+
+ $rewindMethod = new MethodNode('rewind');
+ (\PHP_VERSION_ID >= 80100) && $rewindMethod->setReturnTypeNode(new ReturnTypeNode('void'));
+ $node->addMethod($rewindMethod);
+
+ $validMethod = new MethodNode('valid');
+ (\PHP_VERSION_ID >= 80100) && $validMethod->setReturnTypeNode(new ReturnTypeNode('bool'));
+ $node->addMethod($validMethod);
+ }
+
+ /**
+ * Returns patch priority, which determines when patch will be applied.
+ *
+ * @return int Priority number (higher - earlier)
+ */
+ public function getPriority()
+ {
+ return 100;
+ }
+}