Update v1.0.6.5

This commit is contained in:
sujitprasad
2016-03-02 12:25:21 +05:30
parent 7011553462
commit c56ff86194
218 changed files with 17161 additions and 2358 deletions

View File

@@ -1,3 +1,21 @@
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
==================

View File

@@ -2,7 +2,7 @@ Contributing
------------
Prophecy is an open source, community-driven project. If you'd like to contribute,
feel free to do this, but remember to follow this few simple rules:
feel free to do this, but remember to follow these few simple rules:
- Make your feature addition or bug fix,
- Add either specs or examples for any changes you're making (bugfixes or additions)
@@ -16,6 +16,6 @@ Running tests
Make sure that you don't break anything with your changes by running:
```bash
$> composer install --dev --prefer-dist
$> composer install --prefer-dist
$> vendor/bin/phpspec run
```

View File

@@ -1,5 +1,6 @@
# 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

View File

@@ -17,9 +17,11 @@
}
],
"require": {
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "~2.0",
"sebastian/comparator": "~1.1",
"doctrine/instantiator": "^1.0.2"
"doctrine/instantiator": "^1.0.2",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
@@ -34,7 +36,7 @@
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
"dev-master": "1.5.x-dev"
}
}
}

View File

@@ -4,7 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "642fa332c5fa99e1d5086035641b5a74",
"hash": "0cb11a8d8108277277f9931f6e4fa947",
"content-hash": "d2c6dc828e1217dfa154bc2dbb431edb",
"packages": [
{
"name": "doctrine/instantiator",
@@ -382,16 +383,16 @@
},
{
"name": "phpspec/phpspec",
"version": "2.2.1",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/phpspec.git",
"reference": "e9a40577323e67f1de2e214abf32976a0352d8f8"
"reference": "36635a903bdeb54899d7407bc95610501fd98559"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/phpspec/zipball/e9a40577323e67f1de2e214abf32976a0352d8f8",
"reference": "e9a40577323e67f1de2e214abf32976a0352d8f8",
"url": "https://api.github.com/repos/phpspec/phpspec/zipball/36635a903bdeb54899d7407bc95610501fd98559",
"reference": "36635a903bdeb54899d7407bc95610501fd98559",
"shasum": ""
},
"require": {
@@ -403,15 +404,14 @@
"symfony/console": "~2.3",
"symfony/event-dispatcher": "~2.1",
"symfony/finder": "~2.1",
"symfony/process": "~2.1",
"symfony/process": "^2.6",
"symfony/yaml": "~2.1"
},
"require-dev": {
"behat/behat": "^3.0.11",
"bossa/phpspec2-expect": "~1.0",
"phpunit/phpunit": "~4.4",
"symfony/filesystem": "~2.1",
"symfony/process": "~2.1"
"symfony/filesystem": "~2.1"
},
"suggest": {
"phpspec/nyan-formatters": "~1.0 Adds Nyan formatters"
@@ -456,20 +456,20 @@
"testing",
"tests"
],
"time": "2015-05-30 15:21:40"
"time": "2015-09-07 07:07:37"
},
{
"name": "symfony/console",
"version": "v2.7.3",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "d6cf02fe73634c96677e428f840704bfbcaec29e"
"url": "https://github.com/symfony/console.git",
"reference": "5efd632294c8320ea52492db22292ff853a43766"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/d6cf02fe73634c96677e428f840704bfbcaec29e",
"reference": "d6cf02fe73634c96677e428f840704bfbcaec29e",
"url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766",
"reference": "5efd632294c8320ea52492db22292ff853a43766",
"shasum": ""
},
"require": {
@@ -478,7 +478,6 @@
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1",
"symfony/phpunit-bridge": "~2.7",
"symfony/process": "~2.1"
},
"suggest": {
@@ -513,20 +512,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2015-07-28 15:18:12"
"time": "2015-10-20 14:38:46"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.7.3",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3"
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/9310b5f9a87ec2ea75d20fec0b0017c77c66dac3",
"reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"shasum": ""
},
"require": {
@@ -537,7 +536,6 @@
"symfony/config": "~2.0,>=2.0.5",
"symfony/dependency-injection": "~2.6",
"symfony/expression-language": "~2.6",
"symfony/phpunit-bridge": "~2.7",
"symfony/stopwatch": "~2.3"
},
"suggest": {
@@ -571,28 +569,25 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2015-06-18 19:21:56"
"time": "2015-10-11 09:39:48"
},
{
"name": "symfony/finder",
"version": "v2.7.3",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/Finder.git",
"reference": "ae0f363277485094edc04c9f3cbe595b183b78e4"
"url": "https://github.com/symfony/finder.git",
"reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/ae0f363277485094edc04c9f3cbe595b183b78e4",
"reference": "ae0f363277485094edc04c9f3cbe595b183b78e4",
"url": "https://api.github.com/repos/symfony/finder/zipball/2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d",
"reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@@ -620,28 +615,25 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2015-07-09 16:07:40"
"time": "2015-10-11 09:39:48"
},
{
"name": "symfony/process",
"version": "v2.7.3",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "48aeb0e48600321c272955132d7606ab0a49adb3"
"url": "https://github.com/symfony/process.git",
"reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/48aeb0e48600321c272955132d7606ab0a49adb3",
"reference": "48aeb0e48600321c272955132d7606ab0a49adb3",
"url": "https://api.github.com/repos/symfony/process/zipball/4a959dd4e19c2c5d7512689413921e0a74386ec7",
"reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@@ -669,28 +661,25 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2015-07-01 11:25:50"
"time": "2015-10-23 14:47:27"
},
{
"name": "symfony/yaml",
"version": "v2.7.3",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "71340e996171474a53f3d29111d046be4ad8a0ff"
"url": "https://github.com/symfony/yaml.git",
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/71340e996171474a53f3d29111d046be4ad8a0ff",
"reference": "71340e996171474a53f3d29111d046be4ad8a0ff",
"url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d",
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@@ -718,7 +707,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2015-07-28 14:07:07"
"time": "2015-10-11 09:39:48"
}
],
"aliases": [],

View File

@@ -0,0 +1,55 @@
<?php
namespace spec\Prophecy\Argument\Token;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ApproximateValueTokenSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith(10.12345678, 4);
}
function it_is_initializable()
{
$this->shouldHaveType('Prophecy\Argument\Token\ApproximateValueToken');
}
function it_implements_TokenInterface()
{
$this->shouldBeAnInstanceOf('Prophecy\Argument\Token\TokenInterface');
}
function it_is_not_last()
{
$this->shouldNotBeLast();
}
function it_scores_10_if_rounded_argument_matches_rounded_value()
{
$this->scoreArgument(10.12345)->shouldReturn(10);
}
function it_does_not_score_if_rounded_argument_does_not_match_rounded_value()
{
$this->scoreArgument(10.1234)->shouldReturn(false);
}
function it_uses_a_default_precision_of_zero()
{
$this->beConstructedWith(10.7);
$this->scoreArgument(11.4)->shouldReturn(10);
}
function it_does_not_score_if_rounded_argument_is_not_numeric()
{
$this->scoreArgument('hello')->shouldReturn(false);
}
function it_has_simple_string_representation()
{
$this->__toString()->shouldBe('≅10.1235');
}
}

View File

@@ -98,4 +98,10 @@ class ArgumentSpec extends ObjectBehavior
$token = $this->containingString('string');
$token->shouldBeAnInstanceOf('Prophecy\Argument\Token\StringContainsToken');
}
function it_has_a_shortcut_for_approximate_token()
{
$token = $this->approximate(10);
$token->shouldBeAnInstanceOf('Prophecy\Argument\Token\ApproximateValueToken');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace spec\Prophecy\Comparator;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Prophet;
class ProphecyComparatorSpec extends ObjectBehavior
{
function it_is_a_comparator()
{
$this->shouldHaveType('SebastianBergmann\Comparator\ObjectComparator');
}
function it_accepts_only_prophecy_objects()
{
$this->accepts(123, 321)->shouldReturn(false);
$this->accepts('string', 'string')->shouldReturn(false);
$this->accepts(false, true)->shouldReturn(false);
$this->accepts(true, false)->shouldReturn(false);
$this->accepts((object)array(), (object)array())->shouldReturn(false);
$this->accepts(function(){}, (object)array())->shouldReturn(false);
$this->accepts(function(){}, function(){})->shouldReturn(false);
$prophet = new Prophet();
$prophecy = $prophet->prophesize('Prophecy\Prophecy\ObjectProphecy');
$this->accepts($prophecy, $prophecy)->shouldReturn(true);
}
function it_asserts_that_an_object_is_equal_to_its_revealed_prophecy()
{
$prophet = new Prophet();
$prophecy = $prophet->prophesize('Prophecy\Prophecy\ObjectProphecy');
$this->shouldNotThrow()->duringAssertEquals($prophecy->reveal(), $prophecy);
}
}

View File

@@ -46,6 +46,19 @@ class MagicCallPatchSpec extends ObjectBehavior
$this->apply($node);
}
/**
* @param \Prophecy\Doubler\Generator\Node\ClassNode $node
*/
function it_discovers_api_using_phpdoc_from_interface($node)
{
$node->getParentClass()->willReturn('spec\Prophecy\Doubler\ClassPatch\MagicalApiImplemented');
$node->addMethod(new MethodNode('implementedMethod'))->shouldBeCalled();
$this->apply($node);
}
function it_has_50_priority()
{
$this->getPriority()->shouldReturn(50);
@@ -73,4 +86,19 @@ class MagicalApi
class MagicalApiExtended extends MagicalApi
{
}
}
/**
*/
class MagicalApiImplemented implements MagicalApiInterface
{
}
/**
* @method void implementedMethod()
*/
interface MagicalApiInterface
{
}

View File

@@ -57,26 +57,32 @@ class ClassCodeGeneratorSpec extends ObjectBehavior
$argument11->isOptional()->willReturn(true);
$argument11->getDefault()->willReturn(null);
$argument11->isPassedByReference()->willReturn(false);
$argument11->isVariadic()->willReturn(false);
$argument12->getName()->willReturn('class');
$argument12->getTypeHint()->willReturn('ReflectionClass');
$argument12->isOptional()->willReturn(false);
$argument12->isPassedByReference()->willReturn(false);
$argument12->isVariadic()->willReturn(false);
$argument21->getName()->willReturn('default');
$argument21->getTypeHint()->willReturn(null);
$argument21->getTypeHint()->willReturn('string');
$argument21->isOptional()->willReturn(true);
$argument21->getDefault()->willReturn('ever.zet@gmail.com');
$argument21->isPassedByReference()->willReturn(false);
$argument21->isVariadic()->willReturn(false);
$argument31->getName()->willReturn('refValue');
$argument31->getTypeHint()->willReturn(null);
$argument31->isOptional()->willReturn(false);
$argument31->getDefault()->willReturn();
$argument31->isPassedByReference()->willReturn(false);
$argument31->isVariadic()->willReturn(false);
$code = $this->generate('CustomClass', $class);
$expected = <<<'PHP'
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$expected = <<<'PHP'
namespace {
class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generator\MirroredInterface, \ArrayAccess, \ArrayIterator {
public $name;
@@ -85,13 +91,138 @@ private $email;
public static function getName(array $fullname = NULL, \ReflectionClass $class): string {
return $this->name;
}
protected function getEmail( $default = 'ever.zet@gmail.com') {
protected function getEmail(string $default = 'ever.zet@gmail.com') {
return $this->email;
}
public function &getRefValue( $refValue) {
return $this->refValue;
}
}
}
PHP;
} else {
$expected = <<<'PHP'
namespace {
class CustomClass extends \RuntimeException implements \Prophecy\Doubler\Generator\MirroredInterface, \ArrayAccess, \ArrayIterator {
public $name;
private $email;
public static function getName(array $fullname = NULL, \ReflectionClass $class) {
return $this->name;
}
protected function getEmail(\string $default = 'ever.zet@gmail.com') {
return $this->email;
}
public function &getRefValue( $refValue) {
return $this->refValue;
}
}
}
PHP;
}
$expected = strtr($expected, array("\r\n" => "\n", "\r" => "\n"));
$code->shouldBe($expected);
}
/**
* @param \Prophecy\Doubler\Generator\Node\ClassNode $class
* @param \Prophecy\Doubler\Generator\Node\MethodNode $method1
* @param \Prophecy\Doubler\Generator\Node\MethodNode $method2
* @param \Prophecy\Doubler\Generator\Node\MethodNode $method3
* @param \Prophecy\Doubler\Generator\Node\MethodNode $method4
* @param \Prophecy\Doubler\Generator\Node\ArgumentNode $argument1
* @param \Prophecy\Doubler\Generator\Node\ArgumentNode $argument2
* @param \Prophecy\Doubler\Generator\Node\ArgumentNode $argument3
* @param \Prophecy\Doubler\Generator\Node\ArgumentNode $argument4
*/
function it_generates_proper_php_code_for_variadics(
$class, $method1, $method2, $method3, $method4, $argument1, $argument2,
$argument3, $argument4
)
{
$class->getParentClass()->willReturn('stdClass');
$class->getInterfaces()->willReturn(array('Prophecy\Doubler\Generator\MirroredInterface'));
$class->getProperties()->willReturn(array());
$class->getMethods()->willReturn(array(
$method1, $method2, $method3, $method4
));
$method1->getName()->willReturn('variadic');
$method1->getVisibility()->willReturn('public');
$method1->returnsReference()->willReturn(false);
$method1->isStatic()->willReturn(false);
$method1->getArguments()->willReturn(array($argument1));
$method1->hasReturnType()->willReturn(false);
$method1->getCode()->willReturn('');
$method2->getName()->willReturn('variadicByRef');
$method2->getVisibility()->willReturn('public');
$method2->returnsReference()->willReturn(false);
$method2->isStatic()->willReturn(false);
$method2->getArguments()->willReturn(array($argument2));
$method2->hasReturnType()->willReturn(false);
$method2->getCode()->willReturn('');
$method3->getName()->willReturn('variadicWithType');
$method3->getVisibility()->willReturn('public');
$method3->returnsReference()->willReturn(false);
$method3->isStatic()->willReturn(false);
$method3->getArguments()->willReturn(array($argument3));
$method3->hasReturnType()->willReturn(false);
$method3->getCode()->willReturn('');
$method4->getName()->willReturn('variadicWithTypeByRef');
$method4->getVisibility()->willReturn('public');
$method4->returnsReference()->willReturn(false);
$method4->isStatic()->willReturn(false);
$method4->getArguments()->willReturn(array($argument4));
$method4->hasReturnType()->willReturn(false);
$method4->getCode()->willReturn('');
$argument1->getName()->willReturn('args');
$argument1->getTypeHint()->willReturn(null);
$argument1->isOptional()->willReturn(false);
$argument1->isPassedByReference()->willReturn(false);
$argument1->isVariadic()->willReturn(true);
$argument2->getName()->willReturn('args');
$argument2->getTypeHint()->willReturn(null);
$argument2->isOptional()->willReturn(false);
$argument2->isPassedByReference()->willReturn(true);
$argument2->isVariadic()->willReturn(true);
$argument3->getName()->willReturn('args');
$argument3->getTypeHint()->willReturn('\ReflectionClass');
$argument3->isOptional()->willReturn(false);
$argument3->isPassedByReference()->willReturn(false);
$argument3->isVariadic()->willReturn(true);
$argument4->getName()->willReturn('args');
$argument4->getTypeHint()->willReturn('\ReflectionClass');
$argument4->isOptional()->willReturn(false);
$argument4->isPassedByReference()->willReturn(true);
$argument4->isVariadic()->willReturn(true);
$code = $this->generate('CustomClass', $class);
$expected = <<<'PHP'
namespace {
class CustomClass extends \stdClass implements \Prophecy\Doubler\Generator\MirroredInterface {
public function variadic( ...$args) {
}
public function variadicByRef( &...$args) {
}
public function variadicWithType(\\ReflectionClass ...$args) {
}
public function variadicWithTypeByRef(\\ReflectionClass &...$args) {
}
}
}
PHP;
@@ -126,6 +257,7 @@ PHP;
$argument->isOptional()->willReturn(true);
$argument->getDefault()->willReturn(null);
$argument->isPassedByReference()->willReturn(true);
$argument->isVariadic()->willReturn(false);
$code = $this->generate('CustomClass', $class);
$expected =<<<'PHP'

View File

@@ -2,9 +2,8 @@
namespace spec\Prophecy\Doubler\Generator;
use PhpSpec\ObjectBehavior;
use I\Simply;
use PhpSpec\ObjectBehavior;
use ReflectionClass;
use ReflectionMethod;
use ReflectionParameter;
@@ -13,15 +12,17 @@ class ClassMirrorSpec extends ObjectBehavior
{
/**
* @param ReflectionClass $class
* @param ReflectionClass $parent
* @param ReflectionMethod $method1
* @param ReflectionMethod $method2
* @param ReflectionMethod $method3
*/
function it_reflects_a_class_by_mirroring_all_its_public_methods(
$class, $method1, $method2, $method3
$class, $parent, $method1, $method2, $method3
)
{
$class->getName()->willReturn('Custom\ClassName');
$class->getParentClass()->willReturn($parent);
$class->isInterface()->willReturn(false);
$class->isFinal()->willReturn(false);
$class->getMethods(ReflectionMethod::IS_ABSTRACT)->willReturn(array());
@@ -29,9 +30,15 @@ class ClassMirrorSpec extends ObjectBehavior
$method1, $method2, $method3
));
$parent->getName()->willReturn('Custom\ParentClassName');
$method1->getDeclaringClass()->willReturn($class);
$method2->getDeclaringClass()->willReturn($class);
$method3->getDeclaringClass()->willReturn($class);
$method1->getName()->willReturn('getName');
$method2->getName()->willReturn('isPublic');
$method3->getName()->willReturn('isAbstract');
$method2->getName()->willReturn('getSelf');
$method3->getName()->willReturn('getParent');
$method1->isFinal()->willReturn(false);
$method2->isFinal()->willReturn(false);
@@ -54,9 +61,12 @@ class ClassMirrorSpec extends ObjectBehavior
$method3->getParameters()->willReturn(array());
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method1->hasReturnType()->willReturn(false);
$method2->hasReturnType()->willReturn(false);
$method3->hasReturnType()->willReturn(false);
$method1->hasReturnType()->willReturn(true);
$method1->getReturnType()->willReturn('string');
$method2->hasReturnType()->willReturn(true);
$method2->getReturnType()->willReturn('self');
$method3->hasReturnType()->willReturn(true);
$method3->getReturnType()->willReturn('parent');
}
$classNode = $this->reflect($class, array());
@@ -67,8 +77,14 @@ class ClassMirrorSpec extends ObjectBehavior
$methodNodes->shouldHaveCount(3);
$classNode->hasMethod('getName')->shouldReturn(true);
$classNode->hasMethod('isPublic')->shouldReturn(true);
$classNode->hasMethod('isAbstract')->shouldReturn(true);
$classNode->hasMethod('getSelf')->shouldReturn(true);
$classNode->hasMethod('getParent')->shouldReturn(true);
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$classNode->getMethod('getName')->getReturnType()->shouldReturn('string');
$classNode->getMethod('getSelf')->getReturnType()->shouldReturn('\Custom\ClassName');
$classNode->getMethod('getParent')->getReturnType()->shouldReturn('\Custom\ParentClassName');
}
}
/**
@@ -101,6 +117,9 @@ class ClassMirrorSpec extends ObjectBehavior
$parameter->getDefaultValue()->willReturn(null);
$parameter->isPassedByReference()->willReturn(false);
$parameter->getClass()->willReturn($class);
if (version_compare(PHP_VERSION, '5.6', '>=')) {
$parameter->isVariadic()->willReturn(false);
}
$classNode = $this->reflect($class, array());
@@ -177,6 +196,92 @@ class ClassMirrorSpec extends ObjectBehavior
$methodNodes['innerDetail']->isStatic()->shouldReturn(true);
}
/**
* @param ReflectionClass $class
* @param ReflectionMethod $constructMethod
* @param ReflectionMethod $destructMethod
* @param ReflectionMethod $sleepMethod
* @param ReflectionMethod $wakeupMethod
* @param ReflectionMethod $toStringMethod
* @param ReflectionMethod $callMethod
* @param ReflectionMethod $invokeMethod
*/
function it_reflects_allowed_magic_methods($class, $constructMethod, $destructMethod, $sleepMethod, $wakeupMethod, $toStringMethod, $callMethod, $invokeMethod)
{
$class->getName()->willReturn('Custom\ClassName');
$class->isInterface()->willReturn(false);
$class->isFinal()->willReturn(false);
$class->getMethods(ReflectionMethod::IS_ABSTRACT)->willReturn(array());
$class->getMethods(ReflectionMethod::IS_PUBLIC)->willReturn(array(
$constructMethod, $destructMethod, $sleepMethod, $wakeupMethod, $toStringMethod, $callMethod, $invokeMethod
));
$constructMethod->getName()->willReturn('__construct');
$destructMethod->getName()->willReturn('__destruct');
$sleepMethod->getName()->willReturn('__sleep');
$wakeupMethod->getName()->willReturn('__wakeup');
$toStringMethod->getName()->willReturn('__toString');
$callMethod->getName()->willReturn('__call');
$invokeMethod->getName()->willReturn('__invoke');
$constructMethod->isFinal()->willReturn(false);
$destructMethod->isFinal()->willReturn(false);
$sleepMethod->isFinal()->willReturn(false);
$wakeupMethod->isFinal()->willReturn(false);
$toStringMethod->isFinal()->willReturn(false);
$callMethod->isFinal()->willReturn(false);
$invokeMethod->isFinal()->willReturn(false);
$constructMethod->isProtected()->willReturn(false);
$destructMethod->isProtected()->willReturn(false);
$sleepMethod->isProtected()->willReturn(false);
$wakeupMethod->isProtected()->willReturn(false);
$toStringMethod->isProtected()->willReturn(false);
$callMethod->isProtected()->willReturn(false);
$invokeMethod->isProtected()->willReturn(false);
$constructMethod->isStatic()->willReturn(false);
$destructMethod->isStatic()->willReturn(false);
$sleepMethod->isStatic()->willReturn(false);
$wakeupMethod->isStatic()->willReturn(false);
$toStringMethod->isStatic()->willReturn(false);
$callMethod->isStatic()->willReturn(false);
$invokeMethod->isStatic()->willReturn(false);
$constructMethod->returnsReference()->willReturn(false);
$destructMethod->returnsReference()->willReturn(false);
$sleepMethod->returnsReference()->willReturn(false);
$wakeupMethod->returnsReference()->willReturn(false);
$toStringMethod->returnsReference()->willReturn(false);
$callMethod->returnsReference()->willReturn(false);
$invokeMethod->returnsReference()->willReturn(false);
$constructMethod->getParameters()->willReturn(array());
$destructMethod->getParameters()->willReturn(array());
$sleepMethod->getParameters()->willReturn(array());
$wakeupMethod->getParameters()->willReturn(array());
$toStringMethod->getParameters()->willReturn(array());
$callMethod->getParameters()->willReturn(array());
$invokeMethod->getParameters()->willReturn(array());
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$constructMethod->hasReturnType()->willReturn(false);
$destructMethod->hasReturnType()->willReturn(false);
$sleepMethod->hasReturnType()->willReturn(false);
$wakeupMethod->hasReturnType()->willReturn(false);
$toStringMethod->hasReturnType()->willReturn(false);
$callMethod->hasReturnType()->willReturn(false);
$invokeMethod->hasReturnType()->willReturn(false);
}
$classNode = $this->reflect($class, array());
$classNode->shouldBeAnInstanceOf('Prophecy\Doubler\Generator\Node\ClassNode');
$classNode->getParentClass()->shouldReturn('Custom\ClassName');
$methodNodes = $classNode->getMethods();
$methodNodes->shouldHaveCount(7);
}
/**
* @param ReflectionClass $class
* @param ReflectionMethod $method
@@ -184,9 +289,10 @@ class ClassMirrorSpec extends ObjectBehavior
* @param ReflectionParameter $param2
* @param ReflectionClass $typeHint
* @param ReflectionParameter $param3
* @param ReflectionParameter $param4
*/
function it_properly_reads_methods_arguments_with_types(
$class, $method, $param1, $param2, $typeHint, $param3
$class, $method, $param1, $param2, $typeHint, $param3, $param4
)
{
$class->getName()->willReturn('Custom\ClassName');
@@ -200,7 +306,7 @@ class ClassMirrorSpec extends ObjectBehavior
$method->isProtected()->willReturn(true);
$method->isStatic()->willReturn(false);
$method->returnsReference()->willReturn(false);
$method->getParameters()->willReturn(array($param1, $param2, $param3));
$method->getParameters()->willReturn(array($param1, $param2, $param3, $param4));
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method->hasReturnType()->willReturn(false);
@@ -234,6 +340,22 @@ class ClassMirrorSpec extends ObjectBehavior
$param3->isPassedByReference()->willReturn(false);
$param3->allowsNull()->willReturn(true);
$param4->getName()->willReturn('arg_4');
$param4->isArray()->willReturn(false);
$param4->getClass()->willReturn($typeHint);
$param4->isPassedByReference()->willReturn(false);
$param4->allowsNull()->willReturn(true);
if (version_compare(PHP_VERSION, '5.6', '>=')) {
$param1->isVariadic()->willReturn(false);
$param2->isVariadic()->willReturn(false);
$param3->isVariadic()->willReturn(false);
$param4->isVariadic()->willReturn(true);
} else {
$param4->isOptional()->willReturn(true);
$param4->isDefaultValueAvailable()->willReturn(false);
}
$classNode = $this->reflect($class, array());
$methodNodes = $classNode->getMethods();
$argNodes = $methodNodes['methodWithArgs']->getArguments();
@@ -255,6 +377,15 @@ class ClassMirrorSpec extends ObjectBehavior
} else {
$argNodes[2]->isOptional()->shouldReturn(false);
}
$argNodes[3]->getName()->shouldReturn('arg_4');
$argNodes[3]->getTypeHint()->shouldReturn('ArrayAccess');
if (version_compare(PHP_VERSION, '5.6', '>=')) {
$argNodes[3]->isVariadic()->shouldReturn(true);
} else {
$argNodes[3]->isOptional()->shouldReturn(true);
$argNodes[3]->getDefault()->shouldReturn(null);
}
}
/**
@@ -294,6 +425,9 @@ class ClassMirrorSpec extends ObjectBehavior
$param1->hasType()->willReturn(false);
}
if (version_compare(PHP_VERSION, '5.6', '>=')) {
$param1->isVariadic()->willReturn(false);
}
$param1->isDefaultValueAvailable()->willReturn(false);
$param1->isOptional()->willReturn(false);
$param1->isPassedByReference()->willReturn(false);
@@ -314,10 +448,11 @@ class ClassMirrorSpec extends ObjectBehavior
* @param ReflectionMethod $method
* @param ReflectionParameter $param1
* @param ReflectionParameter $param2
* @param ReflectionParameter $param3
* @param ReflectionClass $typeHint
*/
function it_marks_passed_by_reference_args_as_passed_by_reference(
$class, $method, $param1, $param2, $typeHint
$class, $method, $param1, $param2, $param3, $typeHint
)
{
$class->getName()->willReturn('Custom\ClassName');
@@ -331,7 +466,7 @@ class ClassMirrorSpec extends ObjectBehavior
$method->isProtected()->willReturn(false);
$method->isStatic()->willReturn(false);
$method->returnsReference()->willReturn(false);
$method->getParameters()->willReturn(array($param1, $param2));
$method->getParameters()->willReturn(array($param1, $param2, $param3));
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$method->hasReturnType()->willReturn(false);
@@ -343,6 +478,9 @@ class ClassMirrorSpec extends ObjectBehavior
$param1->isCallable()->willReturn(false);
}
$param1->getClass()->willReturn(null);
if (version_compare(PHP_VERSION, '5.6', '>=')) {
$param1->isVariadic()->willReturn(false);
}
$param1->isDefaultValueAvailable()->willReturn(false);
$param1->isOptional()->willReturn(true);
$param1->isPassedByReference()->willReturn(true);
@@ -359,6 +497,9 @@ class ClassMirrorSpec extends ObjectBehavior
$param2->getName()->willReturn('arg2');
$param2->isArray()->willReturn(false);
$param2->getClass()->willReturn($typeHint);
if (version_compare(PHP_VERSION, '5.6', '>=')) {
$param2->isVariadic()->willReturn(false);
}
$param2->isDefaultValueAvailable()->willReturn(false);
$param2->isOptional()->willReturn(false);
$param2->isPassedByReference()->willReturn(false);
@@ -370,12 +511,25 @@ class ClassMirrorSpec extends ObjectBehavior
$param2->allowsNull()->willReturn(false);
$typeHint->getName()->willReturn('ArrayAccess');
$param3->getName()->willReturn('arg2');
$param3->isArray()->willReturn(false);
$param3->getClass()->willReturn($typeHint);
if (version_compare(PHP_VERSION, '5.6', '>=')) {
$param3->isVariadic()->willReturn(true);
} else {
$param3->isOptional()->willReturn(true);
$param3->isDefaultValueAvailable()->willReturn(false);
}
$param3->isPassedByReference()->willReturn(true);
$param3->allowsNull()->willReturn(true);
$classNode = $this->reflect($class, array());
$methodNodes = $classNode->getMethods();
$argNodes = $methodNodes['methodWithArgs']->getArguments();
$argNodes[0]->isPassedByReference()->shouldReturn(true);
$argNodes[1]->isPassedByReference()->shouldReturn(false);
$argNodes[2]->isPassedByReference()->shouldReturn(true);
}
/**
@@ -410,6 +564,26 @@ class ClassMirrorSpec extends ObjectBehavior
$classNode->getMethods()->shouldHaveCount(0);
}
/**
* @param ReflectionClass $class
* @param ReflectionMethod $method
*/
function it_marks_final_methods_as_unextendable($class, $method)
{
$class->getName()->willReturn('Custom\ClassName');
$class->isInterface()->willReturn(false);
$class->isFinal()->willReturn(false);
$class->getMethods(ReflectionMethod::IS_ABSTRACT)->willReturn(array());
$class->getMethods(ReflectionMethod::IS_PUBLIC)->willReturn(array($method));
$method->isFinal()->willReturn(true);
$method->getName()->willReturn('finalImplementation');
$classNode = $this->reflect($class, array());
$classNode->getUnextendableMethods()->shouldHaveCount(1);
$classNode->isExtendable('finalImplementation')->shouldReturn(false);
}
/**
* @param ReflectionClass $interface
*/

View File

@@ -22,6 +22,36 @@ class ArgumentNodeSpec extends ObjectBehavior
$this->shouldBePassedByReference();
}
function it_is_not_variadic_by_default()
{
$this->shouldNotBeVariadic();
}
function it_is_variadic_if_marked()
{
$this->setAsVariadic();
$this->shouldBeVariadic();
}
function it_does_not_have_default_by_default()
{
$this->shouldNotHaveDefault();
}
function it_does_not_have_default_if_variadic()
{
$this->setDefault(null);
$this->setAsVariadic();
$this->shouldNotHaveDefault();
}
function it_does_have_default_if_not_variadic()
{
$this->setDefault(null);
$this->setAsVariadic(false);
$this->hasDefault()->shouldReturn(true);
}
function it_has_name_with_which_it_was_been_constructed()
{
$this->getName()->shouldReturn('name');

View File

@@ -3,6 +3,7 @@
namespace spec\Prophecy\Doubler\Generator\Node;
use PhpSpec\ObjectBehavior;
use Prophecy\Exception\Doubler\MethodNotExtendableException;
class ClassNodeSpec extends ObjectBehavior
{
@@ -151,4 +152,49 @@ class ClassNodeSpec extends ObjectBehavior
$this->addProperty('text', 'PRIVATE');
$this->getProperties()->shouldReturn(array('text' => 'private'));
}
function its_has_no_unextendable_methods_by_default()
{
$this->getUnextendableMethods()->shouldHaveCount(0);
}
function its_addUnextendableMethods_adds_an_unextendable_method()
{
$this->addUnextendableMethod('testMethod');
$this->getUnextendableMethods()->shouldHaveCount(1);
}
function its_methods_are_extendable_by_default()
{
$this->isExtendable('testMethod')->shouldReturn(true);
}
function its_unextendable_methods_are_not_extendable()
{
$this->addUnextendableMethod('testMethod');
$this->isExtendable('testMethod')->shouldReturn(false);
}
function its_addUnextendableMethods_doesnt_create_duplicates()
{
$this->addUnextendableMethod('testMethod');
$this->addUnextendableMethod('testMethod');
$this->getUnextendableMethods()->shouldHaveCount(1);
}
/**
* @param \Prophecy\Doubler\Generator\Node\MethodNode $method
*/
function it_throws_an_exception_when_adding_a_method_that_isnt_extendable($method)
{
$this->addUnextendableMethod('testMethod');
$method->getName()->willReturn('testMethod');
$expectedException = new MethodNotExtendableException(
"Method `testMethod` is not extendable, so can not be added.",
"stdClass",
"testMethod"
);
$this->shouldThrow($expectedException)->duringAddMethod($method);
}
}

View File

@@ -69,13 +69,16 @@ class MethodNodeSpec extends ObjectBehavior
$argument1->getName()->willReturn('objectName');
$argument2->getName()->willReturn('default');
$argument1->isVariadic()->willReturn(false);
$argument2->isVariadic()->willReturn(true);
$this->addArgument($argument1);
$this->addArgument($argument2);
$this->useParentCode();
$this->getCode()->shouldReturn(
'return parent::getTitle($objectName, $default);'
'return parent::getTitle($objectName, ...$default);'
);
}

View File

@@ -0,0 +1,29 @@
<?php
namespace spec\Prophecy\Exception\Doubler;
use PhpSpec\ObjectBehavior;
use spec\Prophecy\Exception\Prophecy;
class MethodNotExtendableExceptionSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith('', 'User', 'getName');
}
function it_is_DoubleException()
{
$this->shouldHaveType('Prophecy\Exception\Doubler\DoubleException');
}
function it_has_MethodName()
{
$this->getMethodName()->shouldReturn('getName');
}
function it_has_classname()
{
$this->getClassName()->shouldReturn('User');
}
}

View File

@@ -195,4 +195,18 @@ class Argument
{
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);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Argument\Token;
/**
* Approximate value token
*
* @author Daniel Leech <daniel@dantleech.com>
*/
class ApproximateValueToken implements TokenInterface
{
private $value;
private $precision;
public function __construct($value, $precision = 0)
{
$this->value = $value;
$this->precision = $precision;
}
/**
* {@inheritdoc}
*/
public function scoreArgument($argument)
{
return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false;
}
/**
* {@inheritdoc}
*/
public function isLast()
{
return false;
}
/**
* Returns string representation for token.
*
* @return string
*/
public function __toString()
{
return sprintf('≅%s', round($this->value, $this->precision));
}
}

View File

@@ -54,7 +54,17 @@ class CallCenter
*/
public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments)
{
$backtrace = debug_backtrace();
// For efficiency exclude 'args' from the generated backtrace
if (PHP_VERSION_ID >= 50400) {
// Limit backtrace to last 3 calls as we don't use the rest
// Limit argument was introduced in PHP 5.4.0
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
} elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) {
// DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
} else {
$backtrace = debug_backtrace();
}
$file = $line = null;
if (isset($backtrace[2]) && isset($backtrace[2]['file'])) {

View File

@@ -30,6 +30,7 @@ final class Factory extends BaseFactory
parent::__construct();
$this->register(new ClosureComparator());
$this->register(new ProphecyComparator());
}
/**

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Comparator;
use Prophecy\Prophecy\ProphecyInterface;
use SebastianBergmann\Comparator\ObjectComparator;
class ProphecyComparator extends ObjectComparator
{
public function accepts($expected, $actual)
{
return is_object($expected) && is_object($actual) && $actual instanceof ProphecyInterface;
}
public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array())
{
parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed);
}
}

View File

@@ -48,6 +48,12 @@ class MagicCallPatch implements ClassPatchInterface
$tagList = $phpdoc->getTagsByName('method');
$interfaces = $reflectionClass->getInterfaces();
foreach($interfaces as $interface) {
$phpdoc = new DocBlock($interface);
$tagList = array_merge($tagList, $phpdoc->getTagsByName('method'));
}
foreach($tagList as $tag) {
$methodName = $tag->getMethodName();

View File

@@ -60,7 +60,9 @@ class ClassCodeGenerator
$method->returnsReference() ? '&':'',
$method->getName(),
implode(', ', $this->generateArguments($method->getArguments())),
$method->hasReturnType() ? sprintf(': %s', $method->getReturnType()) : ''
version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()
? sprintf(': %s', $method->getReturnType())
: ''
);
$php .= $method->getCode()."\n";
@@ -73,16 +75,34 @@ class ClassCodeGenerator
$php = '';
if ($hint = $argument->getTypeHint()) {
if ('array' === $hint || 'callable' === $hint) {
$php .= $hint;
} else {
$php .= '\\'.$hint;
switch ($hint) {
case 'array':
case 'callable':
$php .= $hint;
break;
case 'string':
case 'int':
case 'float':
case 'bool':
if (version_compare(PHP_VERSION, '7.0', '>=')) {
$php .= $hint;
break;
}
// Fall-through to default case for PHP 5.x
default:
$php .= '\\'.$hint;
}
}
$php .= ' '.($argument->isPassedByReference() ? '&' : '').'$'.$argument->getName();
$php .= ' '.($argument->isPassedByReference() ? '&' : '');
if ($argument->isOptional()) {
$php .= $argument->isVariadic() ? '...' : '';
$php .= '$'.$argument->getName();
if ($argument->isOptional() && !$argument->isVariadic()) {
$php .= ' = '.var_export($argument->getDefault(), true);
}

View File

@@ -32,6 +32,7 @@ class ClassMirror
'__wakeup',
'__toString',
'__call',
'__invoke'
);
/**
@@ -109,6 +110,7 @@ class ClassMirror
}
if (true === $method->isFinal()) {
$node->addUnextendableMethod($method->getName());
continue;
}
@@ -142,7 +144,17 @@ class ClassMirror
}
if (version_compare(PHP_VERSION, '7.0', '>=') && true === $method->hasReturnType()) {
$node->setReturnType((string) $method->getReturnType());
$returnType = (string) $method->getReturnType();
$returnTypeLower = strtolower($returnType);
if ('self' === $returnTypeLower) {
$returnType = $method->getDeclaringClass()->getName();
}
if ('parent' === $returnTypeLower) {
$returnType = $method->getDeclaringClass()->getParentClass()->getName();
}
$node->setReturnType($returnType);
}
if (is_array($params = $method->getParameters()) && count($params)) {
@@ -159,23 +171,45 @@ class ClassMirror
$name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName();
$node = new Node\ArgumentNode($name);
$typeHint = $this->getTypeHint($parameter);
$node->setTypeHint($typeHint);
$node->setTypeHint($this->getTypeHint($parameter));
if (true === $parameter->isDefaultValueAvailable()) {
$node->setDefault($parameter->getDefaultValue());
} elseif (true === $parameter->isOptional()
|| (true === $parameter->allowsNull() && $typeHint)) {
$node->setDefault(null);
if ($this->isVariadic($parameter)) {
$node->setAsVariadic();
}
if (true === $parameter->isPassedByReference()) {
if ($this->hasDefaultValue($parameter)) {
$node->setDefault($this->getDefaultValue($parameter));
}
if ($parameter->isPassedByReference()) {
$node->setAsPassedByReference();
}
$methodNode->addArgument($node);
}
private function hasDefaultValue(ReflectionParameter $parameter)
{
if ($this->isVariadic($parameter)) {
return false;
}
if ($parameter->isDefaultValueAvailable()) {
return true;
}
return $parameter->isOptional() || $this->isNullable($parameter);
}
private function getDefaultValue(ReflectionParameter $parameter)
{
if (!$parameter->isDefaultValueAvailable()) {
return null;
}
return $parameter->getDefaultValue();
}
private function getTypeHint(ReflectionParameter $parameter)
{
if (null !== $className = $this->getParameterClassName($parameter)) {
@@ -197,6 +231,16 @@ class ClassMirror
return null;
}
private function isVariadic(ReflectionParameter $parameter)
{
return PHP_VERSION_ID >= 50600 && $parameter->isVariadic();
}
private function isNullable(ReflectionParameter $parameter)
{
return $parameter->allowsNull() && null !== $this->getTypeHint($parameter);
}
private function getParameterClassName(ReflectionParameter $parameter)
{
try {

View File

@@ -23,6 +23,7 @@ class ArgumentNode
private $default;
private $optional = false;
private $byReference = false;
private $isVariadic = false;
/**
* @param string $name
@@ -47,6 +48,11 @@ class ArgumentNode
$this->typeHint = $typeHint;
}
public function hasDefault()
{
return $this->isOptional() && !$this->isVariadic();
}
public function getDefault()
{
return $this->default;
@@ -72,4 +78,14 @@ class ArgumentNode
{
return $this->byReference;
}
public function setAsVariadic($isVariadic = true)
{
$this->isVariadic = $isVariadic;
}
public function isVariadic()
{
return $this->isVariadic;
}
}

View File

@@ -11,6 +11,7 @@
namespace Prophecy\Doubler\Generator\Node;
use Prophecy\Exception\Doubler\MethodNotExtendableException;
use Prophecy\Exception\InvalidArgumentException;
/**
@@ -23,6 +24,7 @@ class ClassNode
private $parentClass = 'stdClass';
private $interfaces = array();
private $properties = array();
private $unextendableMethods = array();
/**
* @var MethodNode[]
@@ -100,6 +102,12 @@ class ClassNode
public function addMethod(MethodNode $method)
{
if (!$this->isExtendable($method->getName())){
$message = sprintf(
'Method `%s` is not extendable, so can not be added.', $method->getName()
);
throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName());
}
$this->methods[$method->getName()] = $method;
}
@@ -127,4 +135,32 @@ class ClassNode
{
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);
}
}

View File

@@ -170,8 +170,19 @@ class MethodNode
{
$this->code = sprintf(
'return parent::%s(%s);', $this->getName(), implode(', ',
array_map(function (ArgumentNode $arg) { return '$'.$arg->getName(); }, $this->arguments)
array_map(array($this, 'generateArgument'), $this->arguments)
)
);
}
private function generateArgument(ArgumentNode $arg)
{
$argument = '$'.$arg->getName();
if ($arg->isVariadic()) {
$argument = '...'.$argument;
}
return $argument;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Created by PhpStorm.
* User: jeff
* Date: 25/08/2015
* Time: 19:14
*/
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;
}
}

View File

@@ -37,10 +37,10 @@ class ReturnArgumentPromise implements PromiseInterface
public function __construct($index = 0)
{
if (!is_int($index) || $index < 0) {
throw new InvalidArgumentException(
throw new InvalidArgumentException(sprintf(
'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.',
$index
);
));
}
$this->index = $index;
}

View File

@@ -46,13 +46,13 @@ class ThrowPromise implements PromiseInterface
&& !is_subclass_of($exception, 'Exception')) {
throw new InvalidArgumentException(sprintf(
'Exception class or instance expected as argument to ThrowPromise, but got %s.',
gettype($exception)
$exception
));
}
} elseif (!$exception instanceof \Exception) {
throw new InvalidArgumentException(sprintf(
'Exception class or instance expected as argument to ThrowPromise, but got %s.',
gettype($exception)
is_object($exception) ? get_class($exception) : gettype($exception)
));
}

View File

@@ -260,6 +260,8 @@ class ObjectProphecy implements ProphecyInterface
* Tries to get property value from double.
*
* @param string $name
*
* @return mixed
*/
public function __get($name)
{
@@ -270,7 +272,7 @@ class ObjectProphecy implements ProphecyInterface
* Tries to set property value to double.
*
* @param string $name
* @param string $value
* @param mixed $value
*/
public function __set($name, $value)
{

View File

@@ -3,7 +3,7 @@
namespace Prophecy\Util;
use Prophecy\Prophecy\ProphecyInterface;
use SplObjectStorage;
use SebastianBergmann\RecursionContext\Context;
/*
* This file is part of the Prophecy.
@@ -15,73 +15,85 @@ use SplObjectStorage;
*/
/**
* Exporting utility.
*
* This class is derived from the PHPUnit testing framework.
*
* @author Sebastiaan Stok <s.stok@rollerscapes.net
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License>
* This class is a modification from sebastianbergmann/exporter
* @see https://github.com/sebastianbergmann/exporter
*/
class ExportUtil
{
/**
* Exports a value into a string.
* 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")
* - 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 The value to export
* @param integer $indentation The indentation level of the 2nd+ line
*
* @param mixed $value
* @param int $indentation The indentation level of the 2nd+ line
* @return string
*/
public static function export($value, $indentation = 0)
{
return static::recursiveExport($value, $indentation);
return self::recursiveExport($value, $indentation);
}
/**
* Converts an object to an array containing all of its private, protected
* and public properties.
*
* @param object $object
*
* @param mixed $value
* @return array
*/
public static function toArray($object)
public static function toArray($value)
{
if (!is_object($value)) {
return (array) $value;
}
$array = array();
foreach ((array) $object as $key => $value) {
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];
}
$array[$key] = $value;
// 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
// above (fast) mechanism nor with reflection in Zend.
// Format the output similarly to print_r() in this case
if ($object instanceof SplObjectStorage) {
foreach ($object as $key => $value) {
$array[spl_object_hash($value)] = array(
'obj' => $value,
'inf' => $object->getInfo(),
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(),
);
}
}
@@ -90,16 +102,15 @@ class ExportUtil
}
/**
* Recursive implementation of export.
*
* @param mixed $value The value to export
* @param integer $indentation The indentation level of the 2nd+ line
* @param array $processedObjects Contains all objects that were already
* rendered
* 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, &$processedObjects = array())
protected static function recursiveExport(&$value, $indentation, $processed = null)
{
if ($value === null) {
return 'null';
@@ -113,73 +124,88 @@ class ExportUtil
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) . "'";
return "'" .
str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) .
"'";
}
$origValue = $value;
$whitespace = str_repeat(' ', 4 * $indentation);
if (is_object($value)) {
if ($value instanceof ProphecyInterface) {
return sprintf('%s Object (*Prophecy*)', get_class($value));
} elseif (in_array($value, $processedObjects, true)) {
return sprintf('%s Object (*RECURSION*)', get_class($value));
}
$processedObjects[] = $value;
// Convert object to array
$value = self::toArray($value);
if (!$processed) {
$processed = new Context;
}
if (is_array($value)) {
$whitespace = str_repeat(' ', $indentation);
// There seems to be no other way to check arrays for recursion
// http://www.php.net/manual/en/language.types.array.php#73936
preg_match_all('/\n \[(\w+)\] => Array\s+\*RECURSION\*/', print_r($value, true), $matches);
$recursiveKeys = array_unique($matches[1]);
// Convert to valid array keys
// Numeric integer strings are automatically converted to integers
// by PHP
foreach ($recursiveKeys as $key => $recursiveKey) {
if ((string) (integer) $recursiveKey === $recursiveKey) {
$recursiveKeys[$key] = (integer) $recursiveKey;
}
if (($key = $processed->contains($value)) !== false) {
return 'Array &' . $key;
}
$content = '';
$key = $processed->add($value);
$values = '';
foreach ($value as $key => $val) {
if (in_array($key, $recursiveKeys, true)) {
$val = 'Array (*RECURSION*)';
} else {
$val = self::recursiveExport($val, $indentation + 1, $processedObjects);
if (count($value) > 0) {
foreach ($value as $k => $v) {
$values .= sprintf(
'%s %s => %s' . "\n",
$whitespace,
self::recursiveExport($k, $indentation),
self::recursiveExport($value[$k], $indentation + 1, $processed)
);
}
$content .= $whitespace . ' ' . self::export($key) . ' => ' . $val . "\n";
$values = "\n" . $values . $whitespace;
}
if (strlen($content) > 0) {
$content = "\n" . $content . $whitespace;
return sprintf('Array &%s (%s)', $key, $values);
}
if (is_object($value)) {
$class = get_class($value);
if ($value instanceof ProphecyInterface) {
return sprintf('%s Object (*Prophecy*)', $class);
} elseif ($hash = $processed->contains($value)) {
return sprintf('%s:%s Object', $class, $hash);
}
return sprintf(
"%s (%s)",
is_object($origValue) ? sprintf('%s:%s', get_class($origValue), spl_object_hash($origValue)) . ' Object' : 'Array', $content
);
$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);
}
if (is_double($value) && (double)(integer) $value === $value) {
return $value . '.0';
}
return (string) $value;
return var_export($value, true);
}
}