diff options
Diffstat (limited to 'vendor/phpspec')
98 files changed, 8345 insertions, 0 deletions
diff --git a/vendor/phpspec/prophecy/CHANGES.md b/vendor/phpspec/prophecy/CHANGES.md new file mode 100644 index 000000000..c383a0880 --- /dev/null +++ b/vendor/phpspec/prophecy/CHANGES.md @@ -0,0 +1,302 @@ +1.15.0 / 2021/12/08 +=================== + +* [added] Support for the `static` return type [@denis-rolling-scopes] +* [fixed] Add return types for Comparator implementations to avoid deprecation warnings from Symfony's DebugClassLoader [@stof] + +1.14.0 / 2021/09/16 +=================== + +* [added] Support for static closures in will and should [@ntzm] +* [added] Allow install on PHP 8.1 (with test suite fixes) [@javer] +* [added] Support for the 'never' return type [@ciaranmcnulty] +* [fixed] Better error message when doubling intersection return types [@ciaranmcnulty] + +1.13.0 / 2021/03/17 +=================== + +* [added] willYield can now specify a return value [@camilledejoye] +* [added] Prophecy exception interfaces are explicitly Throwable [@ciaranmcnulty] +* [fixed] Argument::in() and notIn() now marked as static [@tyteen4a03] +* [fixed] Can now double unions containing false [@ciaranmcnulty] +* [fixed] Virtual magic methods with arguments are now doublable in PHP 8 [@ciaranmcnulty] + +1.12.2 / 2020/12/19 +=================== + +* [fixed] MethodNotFoundException sometimes thrown with wrong class attached [@ciaranmcnulty] + +1.12.1 / 2020/10/29 +=================== + +* [fixed] Incorrect handling of inherited 'self' return types [@ciaranmcnulty] + +1.12.0 / 2020/10/28 +=================== + +* [added] PHP 8 support [@ciaranmcnulty] +* [added] Argument::in() and Argument::notIn() [@viniciusalonso] +* [added] Support for union and mixed types [@ciaranmcnulty] +* [fixed] Issues caused by introduction of named parameters [@ciaranmcnulty] +* [fixed] Issues caused by stricter rounding [@ciaranmcnulty] + +1.11.1 / 2020/07/08 +=================== + +* [fixed] can't double objects with `self` type hints (@greg0ire) +* [fixed] cloned doubes were not loosely comparable (@tkulka) + +1.11.0 / 2020/07/07 +=================== + +* [changed] dropped support for PHP versions earlier than 7.2 (@ciaranmcnulty) +* [fixed] removed use of Reflection APIs deprecated in PHP 8.0 (@Ayesh) + +1.10.3 / 2020/03/05 +=================== + +* [fixed] removed fatal error when phpdocumentor/reflection-docblock 5 parses an invalid `@method` tag (@stof) + +1.10.2 / 2020/01/20 +=================== + +* [added] support for new versions of `sebastian/comparator` and `sebastian/recursion-context` (@sebastianbergmann) + +1.10.1 / 2019/12/22 +=================== + +* [fixed] identical callables no longer match as arguments (@ciaranmcnulty) + +1.10.0 / 2019/12/17 +=================== + +* [added] shouldHaveBeenCalled evaluation happens later so un-stubbed calls don't throw (@elvetemedve) +* [added] methods can now be doubled case-insensitively to match PHP semantics (@michalbundyra) +* [fixed] reduced memory usage by optimising CachedDoubler (@DonCallisto) +* [fixed] removed fatal error nesting level when comparing large objects (@scroach) + +1.9.0 / 2019/10/03 +================== + +* [added] Add willYield feature to Method Prophecy(@tkotosz) +* [fixed] Allow `MethodProphecy::willThrow()` to accept Throwable as string (@timoschinkel ) +* [fixed] Allow new version of phpdocumentor/reflection-docblock (@ricpelo) + +1.8.1 / 2019/06/13 +================== + +* [fixed] Don't try to patch final constructors (@NiR) + +1.8.0 / 2018/08/05 +================== + +* Support for void return types without explicit will (@crellbar) +* Clearer error message for unexpected method calls (@meridius) +* Clearer error message for aggregate exceptions (@meridius) +* More verbose `shouldBeCalledOnce` expectation (@olvlvl) +* Ability to double Throwable, or methods that extend it (@ciaranmcnulty) +* [fixed] Doubling methods where class has additional arguments to interface (@webimpress) +* [fixed] Doubling methods where arguments are nullable but default is not null (@webimpress) +* [fixed] Doubling magic methods on parent class (@dsnopek) +* [fixed] Check method predictions only once (@dontub) +* [fixed] Argument::containingString throwing error when called with non-string (@dcabrejas) + +1.7.6 / 2018/04/18 +================== + +* Allow sebastian/comparator ^3.0 (@sebastianbergmann) + +1.7.5 / 2018/02/11 +================== + +* Support for object return type hints (thanks @greg0ire) + +1.7.4 / 2018/02/11 +================== + +* Fix issues with PHP 7.2 (thanks @greg0ire) +* Support object type hints in PHP 7.2 (thanks @@jansvoboda11) + +1.7.3 / 2017/11/24 +================== + +* Fix SplInfo ClassPatch to work with Symfony 4 (Thanks @gnugat) + +1.7.2 / 2017-10-04 +================== + +* Reverted "check method predictions only once" due to it breaking Spies + +1.7.1 / 2017-10-03 +================== + +* Allow PHP5 keywords methods generation on PHP7 (thanks @bycosta) +* Allow reflection-docblock v4 (thanks @GrahamCampbell) +* Check method predictions only once (thanks @dontub) +* Escape file path sent to \SplFileObjectConstructor when running on Windows (thanks @danmartin-epiphany) + +1.7.0 / 2017-03-02 +================== + +* Add full PHP 7.1 Support (thanks @prolic) +* Allow `sebastian/comparator ^2.0` (thanks @sebastianbergmann) +* Allow `sebastian/recursion-context ^3.0` (thanks @sebastianbergmann) +* Allow `\Error` instances in `ThrowPromise` (thanks @jameshalsall) +* Support `phpspec/phpspect ^3.2` (thanks @Sam-Burns) +* Fix failing builds (thanks @Sam-Burns) + +1.6.2 / 2016-11-21 +================== + +* Added support for detecting @method on interfaces that the class itself implements, or when the stubbed class is an interface itself (thanks @Seldaek) +* Added support for sebastian/recursion-context 2 (thanks @sebastianbergmann) +* Added testing on PHP 7.1 on Travis (thanks @danizord) +* Fixed the usage of the phpunit comparator (thanks @Anyqax) + +1.6.1 / 2016-06-07 +================== + + * Ignored empty method names in invalid `@method` phpdoc + * Fixed the mocking of SplFileObject + * Added compatibility with phpdocumentor/reflection-docblock 3 + +1.6.0 / 2016-02-15 +================== + + * Add Variadics support (thanks @pamil) + * Add ProphecyComparator for comparing objects that need revealing (thanks @jon-acker) + * Add ApproximateValueToken (thanks @dantleech) + * Add support for 'self' and 'parent' return type (thanks @bendavies) + * Add __invoke to allowed reflectable methods list (thanks @ftrrtf) + * Updated ExportUtil to reflect the latest changes by Sebastian (thanks @jakari) + * Specify the required php version for composer (thanks @jakzal) + * Exclude 'args' in the generated backtrace (thanks @oradwell) + * Fix code generation for scalar parameters (thanks @trowski) + * Fix missing sprintf in InvalidArgumentException __construct call (thanks @emmanuelballery) + * Fix phpdoc for magic methods (thanks @Tobion) + * Fix PhpDoc for interfaces usage (thanks @ImmRanneft) + * Prevent final methods from being manually extended (thanks @kamioftea) + * Enhance exception for invalid argument to ThrowPromise (thanks @Tobion) + +1.5.0 / 2015-04-27 +================== + + * Add support for PHP7 scalar type hints (thanks @trowski) + * Add support for PHP7 return types (thanks @trowski) + * Update internal test suite to support PHP7 + +1.4.1 / 2015-04-27 +================== + + * Fixed bug in closure-based argument tokens (#181) + +1.4.0 / 2015-03-27 +================== + + * Fixed errors in return type phpdocs (thanks @sobit) + * Fixed stringifying of hash containing one value (thanks @avant1) + * Improved clarity of method call expectation exception (thanks @dantleech) + * Add ability to specify which argument is returned in willReturnArgument (thanks @coderbyheart) + * Add more information to MethodNotFound exceptions (thanks @ciaranmcnulty) + * Support for mocking classes with methods that return references (thanks @edsonmedina) + * Improved object comparison (thanks @whatthejeff) + * Adopted '^' in composer dependencies (thanks @GrahamCampbell) + * Fixed non-typehinted arguments being treated as optional (thanks @whatthejeff) + * Magic methods are now filtered for keywords (thanks @seagoj) + * More readable errors for failure when expecting single calls (thanks @dantleech) + +1.3.1 / 2014-11-17 +================== + + * Fix the edge case when failed predictions weren't recorded for `getCheckedPredictions()` + +1.3.0 / 2014-11-14 +================== + + * Add a way to get checked predictions with `MethodProphecy::getCheckedPredictions()` + * Fix HHVM compatibility + * Remove dead code (thanks @stof) + * Add support for DirectoryIterators (thanks @shanethehat) + +1.2.0 / 2014-07-18 +================== + + * Added support for doubling magic methods documented in the class phpdoc (thanks @armetiz) + * Fixed a segfault appearing in some cases (thanks @dmoreaulf) + * Fixed the doubling of methods with typehints on non-existent classes (thanks @gquemener) + * Added support for internal classes using keywords as method names (thanks @milan) + * Added IdenticalValueToken and Argument::is (thanks @florianv) + * Removed the usage of scalar typehints in HHVM as HHVM 3 does not support them anymore in PHP code (thanks @whatthejeff) + +1.1.2 / 2014-01-24 +================== + + * Spy automatically promotes spied method call to an expected one + +1.1.1 / 2014-01-15 +================== + + * Added support for HHVM + +1.1.0 / 2014-01-01 +================== + + * Changed the generated class names to use a static counter instead of a random number + * Added a clss patch for ReflectionClass::newInstance to make its argument optional consistently (thanks @docteurklein) + * Fixed mirroring of classes with typehints on non-existent classes (thanks @docteurklein) + * Fixed the support of array callables in CallbackPromise and CallbackPrediction (thanks @ciaranmcnulty) + * Added support for properties in ObjectStateToken (thanks @adrienbrault) + * Added support for mocking classes with a final constructor (thanks @ciaranmcnulty) + * Added ArrayEveryEntryToken and Argument::withEveryEntry() (thanks @adrienbrault) + * Added an exception when trying to prophesize on a final method instead of ignoring silently (thanks @docteurklein) + * Added StringContainToken and Argument::containingString() (thanks @peterjmit) + * Added ``shouldNotHaveBeenCalled`` on the MethodProphecy (thanks @ciaranmcnulty) + * Fixed the comparison of objects in ExactValuetoken (thanks @sstok) + * Deprecated ``shouldNotBeenCalled`` in favor of ``shouldNotHaveBeenCalled`` + +1.0.4 / 2013-08-10 +================== + + * Better randomness for generated class names (thanks @sstok) + * Add support for interfaces into TypeToken and Argument::type() (thanks @sstok) + * Add support for old-style (method name === class name) constructors (thanks @l310 for report) + +1.0.3 / 2013-07-04 +================== + + * Support callable typehints (thanks @stof) + * Do not attempt to autoload arrays when generating code (thanks @MarcoDeBortoli) + * New ArrayEntryToken (thanks @kagux) + +1.0.2 / 2013-05-19 +================== + + * Logical `AND` token added (thanks @kagux) + * Logical `NOT` token added (thanks @kagux) + * Add support for setting custom constructor arguments + * Properly stringify hashes + * Record calls that throw exceptions + * Migrate spec suite to PhpSpec 2.0 + +1.0.1 / 2013-04-30 +================== + + * Fix broken UnexpectedCallException message + * Trim AggregateException message + +1.0.0 / 2013-04-29 +================== + + * Improve exception messages + +1.0.0-BETA2 / 2013-04-03 +======================== + + * Add more debug information to CallTimes and Call prediction exception messages + * Fix MethodNotFoundException wrong namespace (thanks @gunnarlium) + * Fix some typos in the exception messages (thanks @pborreli) + +1.0.0-BETA1 / 2013-03-25 +======================== + + * Initial release diff --git a/vendor/phpspec/prophecy/LICENSE b/vendor/phpspec/prophecy/LICENSE new file mode 100644 index 000000000..d5b7cd848 --- /dev/null +++ b/vendor/phpspec/prophecy/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013 Konstantin Kudryashov <[email protected]> +Copyright (c) 2013 Marcello Duarte <[email protected]> + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/phpspec/prophecy/README.md b/vendor/phpspec/prophecy/README.md new file mode 100644 index 000000000..fbb58f69b --- /dev/null +++ b/vendor/phpspec/prophecy/README.md @@ -0,0 +1,411 @@ +# Prophecy + +[![Stable release](https://poser.pugx.org/phpspec/prophecy/version.svg)](https://packagist.org/packages/phpspec/prophecy) +[![Build Status](https://travis-ci.org/phpspec/prophecy.svg?branch=master)](https://travis-ci.org/phpspec/prophecy) + +Prophecy is a highly opinionated yet very powerful and flexible PHP object mocking +framework. Though initially it was created to fulfil phpspec2 needs, it is flexible +enough to be used inside any testing framework out there with minimal effort. + +## A simple example + +```php +<?php + +class UserTest extends PHPUnit\Framework\TestCase +{ + private $prophet; + + public function testPasswordHashing() + { + $hasher = $this->prophet->prophesize('App\Security\Hasher'); + $user = new App\Entity\User($hasher->reveal()); + + $hasher->generateHash($user, 'qwerty')->willReturn('hashed_pass'); + + $user->setPassword('qwerty'); + + $this->assertEquals('hashed_pass', $user->getPassword()); + } + + protected function setUp() + { + $this->prophet = new \Prophecy\Prophet; + } + + protected function tearDown() + { + $this->prophet->checkPredictions(); + } +} +``` + +## Installation + +### Prerequisites + +Prophecy requires PHP 7.2.0 or greater. + +### Setup through composer + +First, add Prophecy to the list of dependencies inside your `composer.json`: + +```json +{ + "require-dev": { + "phpspec/prophecy": "~1.0" + } +} +``` + +Then simply install it with composer: + +```bash +$> composer install --prefer-dist +``` + +You can read more about Composer on its [official webpage](http://getcomposer.org). + +## How to use it + +First of all, in Prophecy every word has a logical meaning, even the name of the library +itself (Prophecy). When you start feeling that, you'll become very fluid with this +tool. + +For example, Prophecy has been named that way because it concentrates on describing the future +behavior of objects with very limited knowledge about them. But as with any other prophecy, +those object prophecies can't create themselves - there should be a Prophet: + +```php +$prophet = new Prophecy\Prophet; +``` + +The Prophet creates prophecies by *prophesizing* them: + +```php +$prophecy = $prophet->prophesize(); +``` + +The result of the `prophesize()` method call is a new object of class `ObjectProphecy`. Yes, +that's your specific object prophecy, which describes how your object would behave +in the near future. But first, you need to specify which object you're talking about, +right? + +```php +$prophecy->willExtend('stdClass'); +$prophecy->willImplement('SessionHandlerInterface'); +``` + +There are 2 interesting calls - `willExtend` and `willImplement`. The first one tells +object prophecy that our object should extend a specific class. The second one says that +it should implement some interface. Obviously, objects in PHP can implement multiple +interfaces, but extend only one parent class. + +### Dummies + +Ok, now we have our object prophecy. What can we do with it? First of all, we can get +our object *dummy* by revealing its prophecy: + +```php +$dummy = $prophecy->reveal(); +``` + +The `$dummy` variable now holds a special dummy object. Dummy objects are objects that extend +and/or implement preset classes/interfaces by overriding all their public methods. The key +point about dummies is that they do not hold any logic - they just do nothing. Any method +of the dummy will always return `null` and the dummy will never throw any exceptions. +Dummy is your friend if you don't care about the actual behavior of this double and just need +a token object to satisfy a method typehint. + +You need to understand one thing - a dummy is not a prophecy. Your object prophecy is still +assigned to `$prophecy` variable and in order to manipulate with your expectations, you +should work with it. `$dummy` is a dummy - a simple php object that tries to fulfil your +prophecy. + +### Stubs + +Ok, now we know how to create basic prophecies and reveal dummies from them. That's +awesome if we don't care about our _doubles_ (objects that reflect originals) +interactions. If we do, we need to use *stubs* or *mocks*. + +A stub is an object double, which doesn't have any expectations about the object behavior, +but when put in specific environment, behaves in specific way. Ok, I know, it's cryptic, +but bear with me for a minute. Simply put, a stub is a dummy, which depending on the called +method signature does different things (has logic). To create stubs in Prophecy: + +```php +$prophecy->read('123')->willReturn('value'); +``` + +Oh wow. We've just made an arbitrary call on the object prophecy? Yes, we did. And this +call returned us a new object instance of class `MethodProphecy`. Yep, that's a specific +method with arguments prophecy. Method prophecies give you the ability to create method +promises or predictions. We'll talk about method predictions later in the _Mocks_ section. + +#### Promises + +Promises are logical blocks, that represent your fictional methods in prophecy terms +and they are handled by the `MethodProphecy::will(PromiseInterface $promise)` method. +As a matter of fact, the call that we made earlier (`willReturn('value')`) is a simple +shortcut to: + +```php +$prophecy->read('123')->will(new Prophecy\Promise\ReturnPromise(array('value'))); +``` + +This promise will cause any call to our double's `read()` method with exactly one +argument - `'123'` to always return `'value'`. But that's only for this +promise, there's plenty others you can use: + +- `ReturnPromise` or `->willReturn(1)` - returns a value from a method call +- `ReturnArgumentPromise` or `->willReturnArgument($index)` - returns the nth method argument from call +- `ThrowPromise` or `->willThrow($exception)` - causes the method to throw specific exception +- `CallbackPromise` or `->will($callback)` - gives you a quick way to define your own custom logic + +Keep in mind, that you can always add even more promises by implementing +`Prophecy\Promise\PromiseInterface`. + +#### Method prophecies idempotency + +Prophecy enforces same method prophecies and, as a consequence, same promises and +predictions for the same method calls with the same arguments. This means: + +```php +$methodProphecy1 = $prophecy->read('123'); +$methodProphecy2 = $prophecy->read('123'); +$methodProphecy3 = $prophecy->read('321'); + +$methodProphecy1 === $methodProphecy2; +$methodProphecy1 !== $methodProphecy3; +``` + +That's interesting, right? Now you might ask me how would you define more complex +behaviors where some method call changes behavior of others. In PHPUnit or Mockery +you do that by predicting how many times your method will be called. In Prophecy, +you'll use promises for that: + +```php +$user->getName()->willReturn(null); + +// For PHP 5.4 +$user->setName('everzet')->will(function () { + $this->getName()->willReturn('everzet'); +}); + +// For PHP 5.3 +$user->setName('everzet')->will(function ($args, $user) { + $user->getName()->willReturn('everzet'); +}); + +// Or +$user->setName('everzet')->will(function ($args) use ($user) { + $user->getName()->willReturn('everzet'); +}); +``` + +And now it doesn't matter how many times or in which order your methods are called. +What matters is their behaviors and how well you faked it. + +Note: If the method is called several times, you can use the following syntax to return different +values for each call: + +```php +$prophecy->read('123')->willReturn(1, 2, 3); +``` + +This feature is actually not recommended for most cases. Relying on the order of +calls for the same arguments tends to make test fragile, as adding one more call +can break everything. + +#### Arguments wildcarding + +The previous example is awesome (at least I hope it is for you), but that's not +optimal enough. We hardcoded `'everzet'` in our expectation. Isn't there a better +way? In fact there is, but it involves understanding what this `'everzet'` +actually is. + +You see, even if method arguments used during method prophecy creation look +like simple method arguments, in reality they are not. They are argument token +wildcards. As a matter of fact, `->setName('everzet')` looks like a simple call just +because Prophecy automatically transforms it under the hood into: + +```php +$user->setName(new Prophecy\Argument\Token\ExactValueToken('everzet')); +``` + +Those argument tokens are simple PHP classes, that implement +`Prophecy\Argument\Token\TokenInterface` and tell Prophecy how to compare real arguments +with your expectations. And yes, those classnames are damn big. That's why there's a +shortcut class `Prophecy\Argument`, which you can use to create tokens like that: + +```php +use Prophecy\Argument; + +$user->setName(Argument::exact('everzet')); +``` + +`ExactValueToken` is not very useful in our case as it forced us to hardcode the username. +That's why Prophecy comes bundled with a bunch of other tokens: + +- `IdenticalValueToken` or `Argument::is($value)` - checks that the argument is identical to a specific value +- `ExactValueToken` or `Argument::exact($value)` - checks that the argument matches a specific value +- `TypeToken` or `Argument::type($typeOrClass)` - checks that the argument matches a specific type or + classname +- `ObjectStateToken` or `Argument::which($method, $value)` - checks that the argument method returns + a specific value +- `CallbackToken` or `Argument::that(callback)` - checks that the argument matches a custom callback +- `AnyValueToken` or `Argument::any()` - matches any argument +- `AnyValuesToken` or `Argument::cetera()` - matches any arguments to the rest of the signature +- `StringContainsToken` or `Argument::containingString($value)` - checks that the argument contains a specific string value +- `InArrayToken` or `Argument::in($array)` - checks if value is in array +- `NotInArrayToken` or `Argument::notIn($array)` - checks if value is not in array + +And you can add even more by implementing `TokenInterface` with your own custom classes. + +So, let's refactor our initial `{set,get}Name()` logic with argument tokens: + +```php +use Prophecy\Argument; + +$user->getName()->willReturn(null); + +// For PHP 5.4 +$user->setName(Argument::type('string'))->will(function ($args) { + $this->getName()->willReturn($args[0]); +}); + +// For PHP 5.3 +$user->setName(Argument::type('string'))->will(function ($args, $user) { + $user->getName()->willReturn($args[0]); +}); + +// Or +$user->setName(Argument::type('string'))->will(function ($args) use ($user) { + $user->getName()->willReturn($args[0]); +}); +``` + +That's it. Now our `{set,get}Name()` prophecy will work with any string argument provided to it. +We've just described how our stub object should behave, even though the original object could have +no behavior whatsoever. + +One last bit about arguments now. You might ask, what happens in case of: + +```php +use Prophecy\Argument; + +$user->getName()->willReturn(null); + +// For PHP 5.4 +$user->setName(Argument::type('string'))->will(function ($args) { + $this->getName()->willReturn($args[0]); +}); + +// For PHP 5.3 +$user->setName(Argument::type('string'))->will(function ($args, $user) { + $user->getName()->willReturn($args[0]); +}); + +// Or +$user->setName(Argument::type('string'))->will(function ($args) use ($user) { + $user->getName()->willReturn($args[0]); +}); + +$user->setName(Argument::any())->will(function () { +}); +``` + +Nothing. Your stub will continue behaving the way it did before. That's because of how +arguments wildcarding works. Every argument token type has a different score level, which +wildcard then uses to calculate the final arguments match score and use the method prophecy +promise that has the highest score. In this case, `Argument::type()` in case of success +scores `5` and `Argument::any()` scores `3`. So the type token wins, as does the first +`setName()` method prophecy and its promise. The simple rule of thumb - more precise token +always wins. + +#### Getting stub objects + +Ok, now we know how to define our prophecy method promises, let's get our stub from +it: + +```php +$stub = $prophecy->reveal(); +``` + +As you might see, the only difference between how we get dummies and stubs is that with +stubs we describe every object conversation instead of just agreeing with `null` returns +(object being *dummy*). As a matter of fact, after you define your first promise +(method call), Prophecy will force you to define all the communications - it throws +the `UnexpectedCallException` for any call you didn't describe with object prophecy before +calling it on a stub. + +### Mocks + +Now we know how to define doubles without behavior (dummies) and doubles with behavior, but +no expectations (stubs). What's left is doubles for which we have some expectations. These +are called mocks and in Prophecy they look almost exactly the same as stubs, except that +they define *predictions* instead of *promises* on method prophecies: + +```php +$entityManager->flush()->shouldBeCalled(); +``` + +#### Predictions + +The `shouldBeCalled()` method here assigns `CallPrediction` to our method prophecy. +Predictions are a delayed behavior check for your prophecies. You see, during the entire lifetime +of your doubles, Prophecy records every single call you're making against it inside your +code. After that, Prophecy can use this collected information to check if it matches defined +predictions. You can assign predictions to method prophecies using the +`MethodProphecy::should(PredictionInterface $prediction)` method. As a matter of fact, +the `shouldBeCalled()` method we used earlier is just a shortcut to: + +```php +$entityManager->flush()->should(new Prophecy\Prediction\CallPrediction()); +``` + +It checks if your method of interest (that matches both the method name and the arguments wildcard) +was called 1 or more times. If the prediction failed then it throws an exception. When does this +check happen? Whenever you call `checkPredictions()` on the main Prophet object: + +```php +$prophet->checkPredictions(); +``` + +In PHPUnit, you would want to put this call into the `tearDown()` method. If no predictions +are defined, it would do nothing. So it won't harm to call it after every test. + +There are plenty more predictions you can play with: + +- `CallPrediction` or `shouldBeCalled()` - checks that the method has been called 1 or more times +- `NoCallsPrediction` or `shouldNotBeCalled()` - checks that the method has not been called +- `CallTimesPrediction` or `shouldBeCalledTimes($count)` - checks that the method has been called + `$count` times +- `CallbackPrediction` or `should($callback)` - checks the method against your own custom callback + +Of course, you can always create your own custom prediction any time by implementing +`PredictionInterface`. + +### Spies + +The last bit of awesomeness in Prophecy is out-of-the-box spies support. As I said in the previous +section, Prophecy records every call made during the double's entire lifetime. This means +you don't need to record predictions in order to check them. You can also do it +manually by using the `MethodProphecy::shouldHave(PredictionInterface $prediction)` method: + +```php +$em = $prophet->prophesize('Doctrine\ORM\EntityManager'); + +$controller->createUser($em->reveal()); + +$em->flush()->shouldHaveBeenCalled(); +``` + +Such manipulation with doubles is called spying. And with Prophecy it just works. + + +## FAQ + +### Can I call the original methods on a prophesized class? + +Prophecy does not support calling the original methods on a phrophesized class. If you find yourself needing to mock some methods of a class while calling the original version of other methods, it's likely a sign that your class violates the [single-responsibility principle](https://en.wikipedia.org/wiki/Single-responsibility_principle) and should be refactored. diff --git a/vendor/phpspec/prophecy/composer.json b/vendor/phpspec/prophecy/composer.json new file mode 100644 index 000000000..129a4cf6c --- /dev/null +++ b/vendor/phpspec/prophecy/composer.json @@ -0,0 +1,50 @@ +{ + "name": "phpspec/prophecy", + "description": "Highly opinionated mocking framework for PHP 5.3+", + "keywords": ["Mock", "Stub", "Dummy", "Double", "Fake", "Spy"], + "homepage": "https://github.com/phpspec/prophecy", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "[email protected]", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "[email protected]" + } + ], + + "require": { + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "doctrine/instantiator": "^1.2", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + + "autoload-dev": { + "psr-4": { + "Fixtures\\Prophecy\\": "fixtures" + } + }, + + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument.php b/vendor/phpspec/prophecy/src/Prophecy/Argument.php new file mode 100644 index 000000000..674255a27 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument.php @@ -0,0 +1,239 @@ +<?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; + +use Prophecy\Argument\Token; + +/** + * Argument tokens shortcuts. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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); + } + + /** + * Checks that argument is in array. + * + * @param array $value + * + * @return Token\InArrayToken + */ + + public static function in($value) + { + return new Token\InArrayToken($value); + } + + /** + * Checks that argument is not in array. + * + * @param array $value + * + * @return Token\NotInArrayToken + */ + + public static function notIn($value) + { + return new Token\NotInArrayToken($value); + } + +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php new file mode 100644 index 000000000..a088f21d2 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php @@ -0,0 +1,101 @@ +<?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\Argument; + +/** + * Arguments wildcarding. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php new file mode 100644 index 000000000..50988112c --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php @@ -0,0 +1,52 @@ +<?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\Argument\Token; + +/** + * Any single value token. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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 '*'; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.php new file mode 100644 index 000000000..f76b17bc0 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.php @@ -0,0 +1,52 @@ +<?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\Argument\Token; + +/** + * Any values token. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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 '* [, ...]'; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ApproximateValueToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ApproximateValueToken.php new file mode 100644 index 000000000..901744ab0 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ApproximateValueToken.php @@ -0,0 +1,55 @@ +<?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\Argument\Token; + +/** + * Approximate value token + * + * @author Daniel Leech <[email protected]> + */ +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((float)$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)); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.php new file mode 100644 index 000000000..96b4befd7 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.php @@ -0,0 +1,86 @@ +<?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\Argument\Token; + +/** + * Array elements count token. + * + * @author Boris Mikhaylov <[email protected]> + */ + +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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.php new file mode 100644 index 000000000..0305fc720 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.php @@ -0,0 +1,143 @@ +<?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\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Array entry token. + * + * @author Boris Mikhaylov <[email protected]> + */ +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(); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php new file mode 100644 index 000000000..5d41fa487 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php @@ -0,0 +1,82 @@ +<?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\Argument\Token; + +/** + * Array every entry token. + * + * @author Adrien Brault <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.php new file mode 100644 index 000000000..f45ba20be --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.php @@ -0,0 +1,75 @@ +<?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\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Callback-verified token. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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()'; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.php new file mode 100644 index 000000000..045a1b90f --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.php @@ -0,0 +1,118 @@ +<?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\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Exact value token. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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) { + return false; + } + } + + // 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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.php new file mode 100644 index 000000000..0b6d23ab6 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.php @@ -0,0 +1,74 @@ +<?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\Argument\Token; + +use Prophecy\Util\StringUtil; + +/** + * Identical value token. + * + * @author Florian Voutzinos <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/InArrayToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/InArrayToken.php new file mode 100644 index 000000000..f727aea42 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/InArrayToken.php @@ -0,0 +1,74 @@ +<?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\Argument\Token; + +/** + * Check if values is in array + * + * @author VinÃcius Alonso <[email protected]> + */ +class InArrayToken implements TokenInterface +{ + private $token = array(); + private $strict; + + /** + * @param array $arguments tokens + * @param bool $strict + */ + public function __construct(array $arguments, $strict = true) + { + $this->token = $arguments; + $this->strict = $strict; + } + + /** + * Return scores 8 score if argument is in array. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (count($this->token) === 0) { + return false; + } + + if (\in_array($argument, $this->token, $this->strict)) { + return 8; + } + + return false; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + $arrayAsString = implode(', ', $this->token); + return "[{$arrayAsString}]"; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.php new file mode 100644 index 000000000..4ee1b25e1 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.php @@ -0,0 +1,80 @@ +<?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\Argument\Token; + +/** + * Logical AND token. + * + * @author Boris Mikhaylov <[email protected]> + */ +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)); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.php new file mode 100644 index 000000000..623efa57a --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.php @@ -0,0 +1,73 @@ +<?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\Argument\Token; + +/** + * Logical NOT token. + * + * @author Boris Mikhaylov <[email protected]> + */ +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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/NotInArrayToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/NotInArrayToken.php new file mode 100644 index 000000000..6aed8aa50 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/NotInArrayToken.php @@ -0,0 +1,75 @@ +<?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\Argument\Token; + +/** + * Check if values is not in array + * + * @author VinÃcius Alonso <[email protected]> + */ +class NotInArrayToken implements TokenInterface +{ + private $token = array(); + private $strict; + + /** + * @param array $arguments tokens + * @param bool $strict + */ + public function __construct(array $arguments, $strict = true) + { + $this->token = $arguments; + $this->strict = $strict; + } + + /** + * Return scores 8 score if argument is in array. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (count($this->token) === 0) { + return false; + } + + if (!\in_array($argument, $this->token, $this->strict)) { + return 8; + } + + return false; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + $arrayAsString = implode(', ', $this->token); + return "[{$arrayAsString}]"; + } +} + diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php new file mode 100644 index 000000000..d77107767 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php @@ -0,0 +1,104 @@ +<?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\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Object state-checker token. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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) + ); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php new file mode 100644 index 000000000..bd8d423f9 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php @@ -0,0 +1,67 @@ +<?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\Argument\Token; + +/** + * String contains token. + * + * @author Peter Mitchell <[email protected]> + */ +class StringContainsToken implements TokenInterface +{ + private $value; + + /** + * Initializes token. + * + * @param string $value + */ + public function __construct($value) + { + $this->value = $value; + } + + public function scoreArgument($argument) + { + return is_string($argument) && 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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.php new file mode 100644 index 000000000..625d3bad2 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.php @@ -0,0 +1,43 @@ +<?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\Argument\Token; + +/** + * Argument token interface. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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(); +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.php b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.php new file mode 100644 index 000000000..cb65132ca --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.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\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Value type token. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Call/Call.php b/vendor/phpspec/prophecy/src/Prophecy/Call/Call.php new file mode 100644 index 000000000..265223545 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Call/Call.php @@ -0,0 +1,162 @@ +<?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\Call; + +use Exception; +use Prophecy\Argument\ArgumentsWildcard; + +/** + * Call object. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class Call +{ + private $methodName; + private $arguments; + private $returnValue; + private $exception; + private $file; + private $line; + private $scores; + + /** + * 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; + $this->scores = new \SplObjectStorage(); + + 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); + } + + /** + * Adds the wildcard match score for the provided wildcard. + * + * @param ArgumentsWildcard $wildcard + * @param false|int $score + * + * @return $this + */ + public function addScore(ArgumentsWildcard $wildcard, $score) + { + $this->scores[$wildcard] = $score; + + return $this; + } + + /** + * Returns wildcard match score for the provided wildcard. The score is + * calculated if not already done. + * + * @param ArgumentsWildcard $wildcard + * + * @return false|int False OR integer score (higher - better) + */ + public function getScore(ArgumentsWildcard $wildcard) + { + if (isset($this->scores[$wildcard])) { + return $this->scores[$wildcard]; + } + + return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Call/CallCenter.php b/vendor/phpspec/prophecy/src/Prophecy/Call/CallCenter.php new file mode 100644 index 000000000..00c526d7e --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Call/CallCenter.php @@ -0,0 +1,240 @@ +<?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\Call; + +use Prophecy\Exception\Prophecy\MethodProphecyException; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Call\UnexpectedCallException; +use SplObjectStorage; + +/** + * Calls receiver & manager. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class CallCenter +{ + private $util; + + /** + * @var Call[] + */ + private $recordedCalls = array(); + + /** + * @var SplObjectStorage + */ + private $unexpectedCalls; + + /** + * Initializes call center. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + $this->unexpectedCalls = new SplObjectStorage(); + } + + /** + * 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 + // Limit backtrace to last 3 calls as we don't use the rest + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); + + $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' === strtolower($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 = $this->findMethodProphecies($prophecy, $methodName, $arguments); + + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!count($matches)) { + $this->unexpectedCalls->attach(new Call($methodName, $arguments, null, null, $file, $line), $prophecy); + $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); + + return null; + } + + // Sort matches by their score value + @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; }); + + $score = $matches[0][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[] = $call = new Call( + $methodName, $arguments, $returnValue, $exception, $file, $line + ); + $call->addScore($methodProphecy->getArgumentsWildcard(), $score); + + 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) + { + $methodName = strtolower($methodName); + + return array_values( + array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) { + return $methodName === strtolower($call->getMethodName()) + && 0 < $call->getScore($wildcard) + ; + }) + ); + } + + /** + * @throws UnexpectedCallException + */ + public function checkUnexpectedCalls() + { + /** @var Call $call */ + foreach ($this->unexpectedCalls as $call) { + $prophecy = $this->unexpectedCalls[$call]; + + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!count($this->findMethodProphecies($prophecy, $call->getMethodName(), $call->getArguments()))) { + throw $this->createUnexpectedCallException($prophecy, $call->getMethodName(), $call->getArguments()); + } + } + } + + private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, + array $arguments) + { + $classname = get_class($prophecy->reveal()); + $indentationLength = 8; // looks good + $argstring = implode( + ",\n", + $this->indentArguments( + array_map(array($this->util, 'stringify'), $arguments), + $indentationLength + ) + ); + + $expected = array(); + + foreach (array_merge(...array_values($prophecy->getMethodProphecies())) as $methodProphecy) { + $expected[] = sprintf( + " - %s(\n" . + "%s\n" . + " )", + $methodProphecy->getMethodName(), + implode( + ",\n", + $this->indentArguments( + array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), + $indentationLength + ) + ) + ); + } + + return new UnexpectedCallException( + sprintf( + "Unexpected method call on %s:\n". + " - %s(\n". + "%s\n". + " )\n". + "expected calls were:\n". + "%s", + + $classname, $methodName, $argstring, implode("\n", $expected) + ), + $prophecy, $methodName, $arguments + + ); + } + + private function indentArguments(array $arguments, $indentationLength) + { + return preg_replace_callback( + '/^/m', + function () use ($indentationLength) { + return str_repeat(' ', $indentationLength); + }, + $arguments + ); + } + + /** + * @param ObjectProphecy $prophecy + * @param string $methodName + * @param array $arguments + * + * @return array + */ + private function findMethodProphecies(ObjectProphecy $prophecy, $methodName, array $arguments) + { + $matches = array(); + foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { + if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { + $matches[] = array($score, $methodProphecy); + } + } + + return $matches; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.php b/vendor/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.php new file mode 100644 index 000000000..fabfbc61f --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.php @@ -0,0 +1,44 @@ +<?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\Comparator; + +use SebastianBergmann\Comparator\Comparator; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Closure comparator. + * + * @author Konstantin Kudryashov <[email protected]> + */ +final class ClosureComparator extends Comparator +{ + public function accepts($expected, $actual): bool + { + 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, array &$processed = array()): void + { + if ($expected !== $actual) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + 'all closures are different if not identical' + ); + } + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Comparator/Factory.php b/vendor/phpspec/prophecy/src/Prophecy/Comparator/Factory.php new file mode 100644 index 000000000..2070db142 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Comparator/Factory.php @@ -0,0 +1,47 @@ +<?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\Comparator; + +use SebastianBergmann\Comparator\Factory as BaseFactory; + +/** + * Prophecy comparator factory. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Comparator/ProphecyComparator.php b/vendor/phpspec/prophecy/src/Prophecy/Comparator/ProphecyComparator.php new file mode 100644 index 000000000..2736e410e --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Comparator/ProphecyComparator.php @@ -0,0 +1,31 @@ +<?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\Comparator; + +use Prophecy\Prophecy\ProphecyInterface; +use SebastianBergmann\Comparator\ObjectComparator; + +/** + * @final + */ +class ProphecyComparator extends ObjectComparator +{ + public function accepts($expected, $actual): bool + { + 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()): void + { + parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.php new file mode 100644 index 000000000..2b875211a --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.php @@ -0,0 +1,66 @@ +<?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; + +use ReflectionClass; + +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class CachedDoubler extends Doubler +{ + private static $classes = array(); + + /** + * {@inheritdoc} + */ + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $classId = $this->generateClassId($class, $interfaces); + if (isset(self::$classes[$classId])) { + return self::$classes[$classId]; + } + + return self::$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(); + } + foreach ($this->getClassPatches() as $patch) { + $parts[] = get_class($patch); + } + sort($parts); + + return md5(implode('', $parts)); + } + + public function resetCache() + { + self::$classes = array(); + } +} 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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.php new file mode 100644 index 000000000..699be3a2a --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.php @@ -0,0 +1,22 @@ +<?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; + +/** + * Core double interface. + * All doubled classes will implement this one. + * + * @author Konstantin Kudryashov <[email protected]> + */ +interface DoubleInterface +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php new file mode 100644 index 000000000..a378ae279 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php @@ -0,0 +1,146 @@ +<?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; + +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 <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php new file mode 100644 index 000000000..52e5e0455 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php @@ -0,0 +1,110 @@ +<?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\Generator; + +use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +use Prophecy\Doubler\Generator\Node\TypeNodeAbstract; + +/** + * Class code creator. + * Generates PHP code for specific class node tree. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class ClassCodeGenerator +{ + public function __construct(TypeHintReference $typeHintReference = null) + { + } + + /** + * 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())), + ($ret = $this->generateTypes($method->getReturnTypeNode())) ? ': '.$ret : '' + ); + $php .= $method->getCode()."\n"; + + return $php.'}'; + } + + private function generateTypes(TypeNodeAbstract $typeNode): string + { + if (!$typeNode->getTypes()) { + return ''; + } + + // When we require PHP 8 we can stop generating ?foo nullables and remove this first block + if ($typeNode->canUseNullShorthand()) { + return sprintf( '?%s', $typeNode->getNonNullTypes()[0]); + } else { + return join('|', $typeNode->getTypes()); + } + } + + private function generateArguments(array $arguments) + { + return array_map(function (Node\ArgumentNode $argument){ + + $php = $this->generateTypes($argument->getTypeNode()); + + $php .= ' '.($argument->isPassedByReference() ? '&' : ''); + + $php .= $argument->isVariadic() ? '...' : ''; + + $php .= '$'.$argument->getName(); + + if ($argument->isOptional() && !$argument->isVariadic()) { + $php .= ' = '.var_export($argument->getDefault(), true); + } + + return $php; + }, $arguments); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php new file mode 100644 index 000000000..882a4a4b7 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php @@ -0,0 +1,67 @@ +<?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\Generator; + +use Prophecy\Exception\Doubler\ClassCreatorException; + +/** + * Class creator. + * Creates specific class in current environment. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.php new file mode 100644 index 000000000..5d9cd2d20 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.php @@ -0,0 +1,254 @@ +<?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\Generator; + +use Prophecy\Doubler\Generator\Node\ArgumentTypeNode; +use Prophecy\Doubler\Generator\Node\ReturnTypeNode; +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Doubler\ClassMirrorException; +use ReflectionClass; +use ReflectionIntersectionType; +use ReflectionMethod; +use ReflectionNamedType; +use ReflectionParameter; +use ReflectionType; +use ReflectionUnionType; + +/** + * Class mirror. + * Core doubler class. Mirrors specific class and/or interfaces into class node tree. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class ClassMirror +{ + private static $reflectableMethods = array( + '__construct', + '__destruct', + '__sleep', + '__wakeup', + '__toString', + '__call', + '__invoke' + ); + + /** + * Reflects provided arguments into class node. + * + * @param ReflectionClass|null $class + * @param ReflectionClass[] $interfaces + * + * @return Node\ClassNode + * + */ + public function reflect(?ReflectionClass $class, 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 ($method->hasReturnType()) { + $returnTypes = $this->getTypeHints($method->getReturnType(), $method->getDeclaringClass(), $method->getReturnType()->allowsNull()); + $node->setReturnTypeNode(new ReturnTypeNode(...$returnTypes)); + } + elseif (method_exists($method, 'hasTentativeReturnType') && $method->hasTentativeReturnType()) { + $returnTypes = $this->getTypeHints($method->getTentativeReturnType(), $method->getDeclaringClass(), $method->getTentativeReturnType()->allowsNull()); + $node->setReturnTypeNode(new ReturnTypeNode(...$returnTypes)); + } + + 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); + + $typeHints = $this->getTypeHints($parameter->getType(), $parameter->getDeclaringClass(), $parameter->allowsNull()); + + $node->setTypeNode(new ArgumentTypeNode(...$typeHints)); + + if ($parameter->isVariadic()) { + $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 ($parameter->isVariadic()) { + return false; + } + + if ($parameter->isDefaultValueAvailable()) { + return true; + } + + return $parameter->isOptional() || ($parameter->allowsNull() && $parameter->getType() && \PHP_VERSION_ID < 80100); + } + + private function getDefaultValue(ReflectionParameter $parameter) + { + if (!$parameter->isDefaultValueAvailable()) { + return null; + } + + return $parameter->getDefaultValue(); + } + + private function getTypeHints(?ReflectionType $type, ?ReflectionClass $class, bool $allowsNull) : array + { + $types = []; + + if ($type instanceof ReflectionNamedType) { + $types = [$type->getName()]; + + } + elseif ($type instanceof ReflectionUnionType) { + $types = $type->getTypes(); + } + elseif ($type instanceof ReflectionIntersectionType) { + throw new ClassMirrorException('Doubling intersection types is not supported', $class); + } + elseif(is_object($type)) { + throw new ClassMirrorException('Unknown reflection type ' . get_class($type), $class); + } + + $types = array_map( + function(string $type) use ($class) { + if ($type === 'self') { + return $class->getName(); + } + if ($type === 'parent') { + return $class->getParentClass()->getName(); + } + + return $type; + }, + $types + ); + + if ($types && $types != ['mixed'] && $allowsNull) { + $types[] = 'null'; + } + + return $types; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.php new file mode 100644 index 000000000..da7fed4e1 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.php @@ -0,0 +1,133 @@ +<?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\Generator\Node; + +/** + * Argument node. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class ArgumentNode +{ + private $name; + private $default; + private $optional = false; + private $byReference = false; + private $isVariadic = false; + + /** @var ArgumentTypeNode */ + private $typeNode; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + $this->typeNode = new ArgumentTypeNode(); + } + + public function getName() + { + return $this->name; + } + + public function setTypeNode(ArgumentTypeNode $typeNode) + { + $this->typeNode = $typeNode; + } + + public function getTypeNode() : ArgumentTypeNode + { + return $this->typeNode; + } + + 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; + } + + /** + * @deprecated use getArgumentTypeNode instead + * @return string|null + */ + public function getTypeHint() + { + $type = $this->typeNode->getNonNullTypes() ? $this->typeNode->getNonNullTypes()[0] : null; + + return $type ? ltrim($type, '\\') : null; + } + + /** + * @deprecated use setArgumentTypeNode instead + * @param string|null $typeHint + */ + public function setTypeHint($typeHint = null) + { + $this->typeNode = ($typeHint === null) ? new ArgumentTypeNode() : new ArgumentTypeNode($typeHint); + } + + /** + * @deprecated use getArgumentTypeNode instead + * @return bool + */ + public function isNullable() + { + return $this->typeNode->canUseNullShorthand(); + } + + /** + * @deprecated use getArgumentTypeNode instead + * @param bool $isNullable + */ + public function setAsNullable($isNullable = true) + { + $nonNullTypes = $this->typeNode->getNonNullTypes(); + $this->typeNode = $isNullable ? new ArgumentTypeNode('null', ...$nonNullTypes) : new ArgumentTypeNode(...$nonNullTypes); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php new file mode 100644 index 000000000..0a18b91e1 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentTypeNode.php @@ -0,0 +1,10 @@ +<?php + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\Doubler\DoubleException; + +class ArgumentTypeNode extends TypeNodeAbstract +{ + +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.php new file mode 100644 index 000000000..f7bd2857a --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.php @@ -0,0 +1,169 @@ +<?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\Generator\Node; + +use Prophecy\Exception\Doubler\MethodNotExtendableException; +use Prophecy\Exception\InvalidArgumentException; + +/** + * Class node. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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, $force = false) + { + 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()); + } + + if ($force || !isset($this->methods[$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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php new file mode 100644 index 000000000..ece652f9f --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php @@ -0,0 +1,210 @@ +<?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\Generator\Node; + +use Prophecy\Doubler\Generator\TypeHintReference; +use Prophecy\Exception\InvalidArgumentException; + +/** + * Method node. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class MethodNode +{ + private $name; + private $code; + private $visibility = 'public'; + private $static = false; + private $returnsReference = false; + + /** @var ReturnTypeNode */ + private $returnTypeNode; + + /** + * @var ArgumentNode[] + */ + private $arguments = array(); + + /** + * @param string $name + * @param string $code + */ + public function __construct($name, $code = null, TypeHintReference $typeHintReference = null) + { + $this->name = $name; + $this->code = $code; + $this->returnTypeNode = new ReturnTypeNode(); + } + + 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; + } + + /** + * @deprecated use getReturnTypeNode instead + * @return bool + */ + public function hasReturnType() + { + return (bool) $this->returnTypeNode->getNonNullTypes(); + } + + public function setReturnTypeNode(ReturnTypeNode $returnTypeNode): void + { + $this->returnTypeNode = $returnTypeNode; + } + + /** + * @deprecated use setReturnTypeNode instead + * @param string $type + */ + public function setReturnType($type = null) + { + $this->returnTypeNode = ($type === '' || $type === null) ? new ReturnTypeNode() : new ReturnTypeNode($type); + } + + /** + * @deprecated use setReturnTypeNode instead + * @param bool $bool + */ + public function setNullableReturnType($bool = true) + { + if ($bool) { + $this->returnTypeNode = new ReturnTypeNode('null', ...$this->returnTypeNode->getTypes()); + } + else { + $this->returnTypeNode = new ReturnTypeNode(...$this->returnTypeNode->getNonNullTypes()); + } + } + + /** + * @deprecated use getReturnTypeNode instead + * @return string|null + */ + public function getReturnType() + { + if ($types = $this->returnTypeNode->getNonNullTypes()) + { + return $types[0]; + } + + return null; + } + + public function getReturnTypeNode() : ReturnTypeNode + { + return $this->returnTypeNode; + } + + /** + * @deprecated use getReturnTypeNode instead + * @return bool + */ + public function hasNullableReturnType() + { + return $this->returnTypeNode->canUseNullShorthand(); + } + + /** + * @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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php new file mode 100644 index 000000000..5b5824988 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php @@ -0,0 +1,45 @@ +<?php + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\Doubler\DoubleException; + +final class ReturnTypeNode extends TypeNodeAbstract +{ + protected function getRealType(string $type): string + { + switch ($type) { + case 'void': + case 'never': + return $type; + default: + return parent::getRealType($type); + } + } + + protected function guardIsValidType() + { + if (isset($this->types['void']) && count($this->types) !== 1) { + throw new DoubleException('void cannot be part of a union'); + } + if (isset($this->types['never']) && count($this->types) !== 1) { + throw new DoubleException('never cannot be part of a union'); + } + + parent::guardIsValidType(); + } + + /** + * @deprecated use hasReturnStatement + */ + public function isVoid() + { + return $this->types == ['void' => 'void']; + } + + public function hasReturnStatement(): bool + { + return $this->types !== ['void' => 'void'] + && $this->types !== ['never' => 'never']; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php new file mode 100644 index 000000000..97fc54978 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php @@ -0,0 +1,97 @@ +<?php + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\Doubler\DoubleException; + +abstract class TypeNodeAbstract +{ + /** @var string[] */ + protected $types = []; + + public function __construct(string ...$types) + { + foreach ($types as $type) { + $type = $this->getRealType($type); + $this->types[$type] = $type; + } + + $this->guardIsValidType(); + } + + public function canUseNullShorthand(): bool + { + return isset($this->types['null']) && count($this->types) <= 2; + } + + public function getTypes(): array + { + return array_values($this->types); + } + + public function getNonNullTypes(): array + { + $nonNullTypes = $this->types; + unset($nonNullTypes['null']); + + return array_values($nonNullTypes); + } + + protected function prefixWithNsSeparator(string $type): string + { + return '\\' . ltrim($type, '\\'); + } + + protected function getRealType(string $type): string + { + switch ($type) { + // type aliases + case 'double': + case 'real': + return 'float'; + case 'boolean': + return 'bool'; + case 'integer': + return 'int'; + + // built in types + case 'self': + case 'static': + case 'array': + case 'callable': + case 'bool': + case 'false': + case 'float': + case 'int': + case 'string': + case 'iterable': + case 'object': + case 'null': + return $type; + case 'mixed': + return \PHP_VERSION_ID < 80000 ? $this->prefixWithNsSeparator($type) : $type; + + default: + return $this->prefixWithNsSeparator($type); + } + } + + protected function guardIsValidType() + { + if ($this->types == ['null' => 'null']) { + throw new DoubleException('Type cannot be standalone null'); + } + + if ($this->types == ['false' => 'false']) { + throw new DoubleException('Type cannot be standalone false'); + } + + if ($this->types == ['false' => 'false', 'null' => 'null']) { + throw new DoubleException('Type cannot be nullable false'); + } + + if (\PHP_VERSION_ID >= 80000 && isset($this->types['mixed']) && count($this->types) !== 1) { + throw new DoubleException('mixed cannot be part of a union'); + } + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php new file mode 100644 index 000000000..d720b1515 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php @@ -0,0 +1,22 @@ +<?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\Generator; + +/** + * Reflection interface. + * All reflected classes implement this interface. + * + * @author Konstantin Kudryashov <[email protected]> + */ +interface ReflectionInterface +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/TypeHintReference.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/TypeHintReference.php new file mode 100644 index 000000000..5e8aa303d --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/TypeHintReference.php @@ -0,0 +1,43 @@ +<?php + +namespace Prophecy\Doubler\Generator; + +/** + * Tells whether a keyword refers to a class or to a built-in type for the + * current version of php + * + * @deprecated in favour of Node\TypeNodeAbstract + */ +final class TypeHintReference +{ + public function isBuiltInParamTypeHint($type) + { + switch ($type) { + case 'self': + case 'array': + case 'callable': + case 'bool': + case 'float': + case 'int': + case 'string': + case 'iterable': + case 'object': + return true; + + case 'mixed': + return PHP_VERSION_ID >= 80000; + + default: + return false; + } + } + + public function isBuiltInReturnTypeHint($type) + { + if ($type === 'void') { + return true; + } + + return $this->isBuiltInParamTypeHint($type); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php new file mode 100644 index 000000000..8a99c4ce8 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php @@ -0,0 +1,127 @@ +<?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; + +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 <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.php b/vendor/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.php new file mode 100644 index 000000000..d67ec6a4d --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.php @@ -0,0 +1,52 @@ +<?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; + +use ReflectionClass; + +/** + * Name generator. + * Generates classname for double. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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++); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.php new file mode 100644 index 000000000..48ed22542 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.php @@ -0,0 +1,40 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.php new file mode 100644 index 000000000..822918a29 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.php @@ -0,0 +1,31 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.php new file mode 100644 index 000000000..8fc53b8b5 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.php @@ -0,0 +1,31 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php new file mode 100644 index 000000000..5bc826d75 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php @@ -0,0 +1,33 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.php new file mode 100644 index 000000000..6642a58f2 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.php @@ -0,0 +1,18 @@ +<?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\Exception\Doubler; + +use RuntimeException; + +class DoubleException extends RuntimeException implements DoublerException +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.php new file mode 100644 index 000000000..9d6be1796 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.php @@ -0,0 +1,18 @@ +<?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\Exception\Doubler; + +use Prophecy\Exception\Exception; + +interface DoublerException extends Exception +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.php new file mode 100644 index 000000000..e344dead2 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.php @@ -0,0 +1,20 @@ +<?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\Exception\Doubler; + +class InterfaceNotFoundException extends ClassNotFoundException +{ + public function getInterfaceName() + { + return $this->getClassname(); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotExtendableException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotExtendableException.php new file mode 100644 index 000000000..56f47b110 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotExtendableException.php @@ -0,0 +1,41 @@ +<?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; + } + + } diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.php new file mode 100644 index 000000000..a53834948 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.php @@ -0,0 +1,60 @@ +<?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\Exception\Doubler; + +class MethodNotFoundException extends DoubleException +{ + /** + * @var string|object + */ + private $classname; + + /** + * @var string + */ + private $methodName; + + /** + * @var array + */ + private $arguments; + + /** + * @param string $message + * @param string|object $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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.php new file mode 100644 index 000000000..630304970 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.php @@ -0,0 +1,41 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Exception.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Exception.php new file mode 100644 index 000000000..416191284 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Exception.php @@ -0,0 +1,22 @@ +<?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\Exception; + +/** + * Core Prophecy exception interface. + * All Prophecy exceptions implement it. + * + * @author Konstantin Kudryashov <[email protected]> + */ +interface Exception extends \Throwable +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..bc91c690f --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.php @@ -0,0 +1,16 @@ +<?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\Exception; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.php new file mode 100644 index 000000000..a00dfb03c --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.php @@ -0,0 +1,51 @@ +<?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\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"; + $message = empty($this->exceptions) ? $message : "\n" . $message; + + $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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.php new file mode 100644 index 000000000..bbbbc3d97 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.php @@ -0,0 +1,24 @@ +<?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\Exception\Prediction; + +use RuntimeException; + +/** + * Basic failed prediction exception. + * Use it for custom prediction failures. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class FailedPredictionException extends RuntimeException implements PredictionException +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.php new file mode 100644 index 000000000..05ea4aad8 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.php @@ -0,0 +1,18 @@ +<?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\Exception\Prediction; + +use Prophecy\Exception\Prophecy\MethodProphecyException; + +class NoCallsException extends MethodProphecyException implements PredictionException +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.php new file mode 100644 index 000000000..2596b1ef1 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.php @@ -0,0 +1,18 @@ +<?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\Exception\Prediction; + +use Prophecy\Exception\Exception; + +interface PredictionException extends Exception +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php new file mode 100644 index 000000000..9d905431f --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php @@ -0,0 +1,31 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.php new file mode 100644 index 000000000..7a99c2d79 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.php @@ -0,0 +1,32 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.php new file mode 100644 index 000000000..1b03eaf47 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.php @@ -0,0 +1,34 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.php new file mode 100644 index 000000000..e345402e0 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.php @@ -0,0 +1,34 @@ +<?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\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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.php b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.php new file mode 100644 index 000000000..915733287 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.php @@ -0,0 +1,18 @@ +<?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\Exception\Prophecy; + +use Prophecy\Exception\Exception; + +interface ProphecyException extends Exception +{ +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php new file mode 100644 index 000000000..209821ce9 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php @@ -0,0 +1,69 @@ +<?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\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use phpDocumentor\Reflection\DocBlock\Tags\Method; + +/** + * @author Théo FIDRY <[email protected]> + * + * @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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassTagRetriever.php b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassTagRetriever.php new file mode 100644 index 000000000..9817a4479 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassTagRetriever.php @@ -0,0 +1,60 @@ +<?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\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tags\Method; +use phpDocumentor\Reflection\DocBlockFactory; +use phpDocumentor\Reflection\Types\ContextFactory; + +/** + * @author Théo FIDRY <[email protected]> + * + * @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) + ); + + $methods = array(); + + foreach ($phpdoc->getTagsByName('method') as $tag) { + if ($tag instanceof Method) { + $methods[] = $tag; + } + } + + return $methods; + } catch (\InvalidArgumentException $e) { + return array(); + } + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php new file mode 100644 index 000000000..c0dec3de8 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php @@ -0,0 +1,35 @@ +<?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\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; + +/** + * @author Théo FIDRY <[email protected]> + * + * @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'); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php new file mode 100644 index 000000000..d3989dad5 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php @@ -0,0 +1,30 @@ +<?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\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use phpDocumentor\Reflection\DocBlock\Tags\Method; + +/** + * @author Théo FIDRY <[email protected]> + * + * @internal + */ +interface MethodTagRetrieverInterface +{ + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass); +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.php b/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.php new file mode 100644 index 000000000..b47873669 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.php @@ -0,0 +1,86 @@ +<?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\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 <[email protected]> + */ +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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.php b/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.php new file mode 100644 index 000000000..31c6c575a --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.php @@ -0,0 +1,107 @@ +<?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\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 <[email protected]> + */ +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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.php b/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.php new file mode 100644 index 000000000..727931f3d --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.php @@ -0,0 +1,66 @@ +<?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\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; +use ReflectionFunction; + +/** + * Callback prediction. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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') && (new ReflectionFunction($callback))->getClosureThis() !== null) { + $callback = Closure::bind($callback, $object); + } + + call_user_func($callback, $calls, $object, $method); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.php b/vendor/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.php new file mode 100644 index 000000000..46ac5bfc0 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.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\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 <[email protected]> + */ +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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.php new file mode 100644 index 000000000..f7fb06a99 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.php @@ -0,0 +1,37 @@ +<?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\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 <[email protected]> + */ +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); +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php b/vendor/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php new file mode 100644 index 000000000..f766e6e67 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php @@ -0,0 +1,67 @@ +<?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\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; +use ReflectionFunction; + +/** + * Callback promise. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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') && (new ReflectionFunction($callback))->getClosureThis() !== null) { + $callback = Closure::bind($callback, $object); + } + + return call_user_func($callback, $args, $object, $method); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.php new file mode 100644 index 000000000..382537b47 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.php @@ -0,0 +1,35 @@ +<?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\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Promise interface. + * Promises are logical blocks, tied to `will...` keyword. + * + * @author Konstantin Kudryashov <[email protected]> + */ +interface PromiseInterface +{ + /** + * Evaluates promise. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.php b/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.php new file mode 100644 index 000000000..39bfeea07 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.php @@ -0,0 +1,61 @@ +<?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\Promise; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return argument promise. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.php b/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.php new file mode 100644 index 000000000..c7d5ac598 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.php @@ -0,0 +1,55 @@ +<?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\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return promise. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.php b/vendor/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.php new file mode 100644 index 000000000..26ec19edf --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.php @@ -0,0 +1,100 @@ +<?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\Promise; + +use Doctrine\Instantiator\Instantiator; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; + +/** + * Throw promise. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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) && !interface_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_a($exception, 'Throwable', true); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.php b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.php new file mode 100644 index 000000000..c45c281d7 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.php @@ -0,0 +1,573 @@ +<?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\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; +use ReflectionNamedType; +use ReflectionType; +use ReflectionUnionType; + +/** + * Method prophecy. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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); + } + + $hasTentativeReturnType = method_exists($reflectedMethod, 'hasTentativeReturnType') + && $reflectedMethod->hasTentativeReturnType(); + + if (true === $reflectedMethod->hasReturnType() || $hasTentativeReturnType) { + if ($hasTentativeReturnType) { + $reflectionType = $reflectedMethod->getTentativeReturnType(); + } + else { + $reflectionType = $reflectedMethod->getReturnType(); + } + + if ($reflectionType instanceof ReflectionNamedType) { + $types = [$reflectionType]; + } + elseif ($reflectionType instanceof ReflectionUnionType) { + $types = $reflectionType->getTypes(); + } + + $types = array_map( + function(ReflectionType $type) { return $type->getName(); }, + $types + ); + + usort( + $types, + static function(string $type1, string $type2) { + + // null is lowest priority + if ($type2 == 'null') { + return -1; + } + elseif ($type1 == 'null') { + return 1; + } + + // objects are higher priority than scalars + $isObject = static function($type) { + return class_exists($type) || interface_exists($type); + }; + + if($isObject($type1) && !$isObject($type2)) { + return -1; + } + elseif(!$isObject($type1) && $isObject($type2)) + { + return 1; + } + + // don't sort both-scalars or both-objects + return 0; + } + ); + + $defaultType = $types[0]; + + if ('void' === $defaultType) { + $this->voidReturnType = true; + } + + $this->will(function () use ($defaultType) { + switch ($defaultType) { + case 'void': return; + 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': + return (function () { yield; })(); + + default: + $prophet = new Prophet; + return $prophet->prophesize($defaultType)->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())); + } + + /** + * @param array $items + * @param mixed $return + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function willYield($items, $return = null) + { + if ($this->voidReturnType) { + throw new MethodProphecyException( + "The method \"$this->methodName\" has a void return type, and so cannot yield anything", + $this + ); + } + + if (!is_array($items)) { + throw new InvalidArgumentException(sprintf( + 'Expected array, but got %s.', + gettype($items) + )); + } + + $generator = function() use ($items, $return) { + yield from $items; + + return $return; + }; + + return $this->will($generator); + } + + /** + * 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)); + } + + /** + * Sets call times prediction to the prophecy. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @return $this + */ + public function shouldBeCalledOnce() + { + return $this->shouldBeCalledTimes(1); + } + + /** + * 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 call times prediction. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @return $this + */ + public function shouldHaveBeenCalledOnce() + { + return $this->shouldHaveBeenCalledTimes(1); + } + + /** + * 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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php new file mode 100644 index 000000000..11b87cf1b --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php @@ -0,0 +1,286 @@ +<?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\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 <[email protected]> + */ +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 = strtolower($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; + } + + $methodName = strtolower($methodName); + + 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 + * @throws \Prophecy\Exception\Call\UnexpectedCallException + */ + public function checkProphecyMethodsPredictions() + { + $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); + $exception->setObjectProphecy($this); + + $this->callCenter->checkUnexpectedCalls(); + + 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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.php new file mode 100644 index 000000000..462f15a90 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.php @@ -0,0 +1,27 @@ +<?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\Prophecy; + +/** + * Core Prophecy interface. + * + * @author Konstantin Kudryashov <[email protected]> + */ +interface ProphecyInterface +{ + /** + * Reveals prophecy object (double) . + * + * @return object + */ + public function reveal(); +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.php new file mode 100644 index 000000000..2d839585f --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.php @@ -0,0 +1,34 @@ +<?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\Prophecy; + +/** + * Controllable doubles interface. + * + * @author Konstantin Kudryashov <[email protected]> + */ +interface ProphecySubjectInterface +{ + /** + * Sets subject prophecy. + * + * @param ProphecyInterface $prophecy + */ + public function setProphecy(ProphecyInterface $prophecy); + + /** + * Returns subject prophecy. + * + * @return ProphecyInterface + */ + public function getProphecy(); +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.php b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.php new file mode 100644 index 000000000..60ecdac81 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.php @@ -0,0 +1,44 @@ +<?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\Prophecy; + +/** + * Basic prophecies revealer. + * + * @author Konstantin Kudryashov <[email protected]> + */ +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; + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.php b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.php new file mode 100644 index 000000000..ffc82bb6f --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.php @@ -0,0 +1,29 @@ +<?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\Prophecy; + +/** + * Prophecies revealer interface. + * + * @author Konstantin Kudryashov <[email protected]> + */ +interface RevealerInterface +{ + /** + * Unwraps value(s). + * + * @param mixed $value + * + * @return mixed + */ + public function reveal($value); +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Prophet.php b/vendor/phpspec/prophecy/src/Prophecy/Prophet.php new file mode 100644 index 000000000..d37c92a34 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Prophet.php @@ -0,0 +1,138 @@ +<?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; + +use Prophecy\Doubler\CachedDoubler; +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 <[email protected]> + */ +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 CachedDoubler(); + $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch); + $doubler->registerClassPatch(new ClassPatch\TraversablePatch); + $doubler->registerClassPatch(new ClassPatch\ThrowablePatch); + $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; + } + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Util/ExportUtil.php b/vendor/phpspec/prophecy/src/Prophecy/Util/ExportUtil.php new file mode 100644 index 000000000..1090a801e --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Util/ExportUtil.php @@ -0,0 +1,210 @@ +<?php + +namespace Prophecy\Util; + +use Prophecy\Prophecy\ProphecyInterface; +use SebastianBergmann\RecursionContext\Context; + +/* + * 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. + */ + +/** + * 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 ($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); + } +} diff --git a/vendor/phpspec/prophecy/src/Prophecy/Util/StringUtil.php b/vendor/phpspec/prophecy/src/Prophecy/Util/StringUtil.php new file mode 100644 index 000000000..ba4faff57 --- /dev/null +++ b/vendor/phpspec/prophecy/src/Prophecy/Util/StringUtil.php @@ -0,0 +1,99 @@ +<?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\Util; + +use Prophecy\Call\Call; + +/** + * String utility. + * + * @author Konstantin Kudryashov <[email protected]> + */ +class StringUtil +{ + private $verbose; + + /** + * @param bool $verbose + */ + public function __construct($verbose = true) + { + $this->verbose = $verbose; + } + + /** + * 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 (!$this->verbose && 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)); + } +} |