Fixed twitter link

This commit is contained in:
Glitch (t404null)
2022-06-25 11:33:59 +00:00
parent 654925cdbf
commit 25e7853992
1488 changed files with 124494 additions and 20 deletions

View File

@@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
/**
* Class patch interface.
* Class patches extend doubles functionality or help
* Prophecy to avoid some internal PHP bugs.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
interface ClassPatchInterface
{
/**
* Checks if patch supports specific class node.
*
* @param ClassNode $node
*
* @return bool
*/
public function supports(ClassNode $node);
/**
* Applies patch to the specific class node.
*
* @param ClassNode $node
* @return void
*/
public function apply(ClassNode $node);
/**
* Returns patch priority, which determines when patch will be applied.
*
* @return int Priority number (higher - earlier)
*/
public function getPriority();
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
/**
* Disable constructor.
* Makes all constructor arguments optional.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class DisableConstructorPatch implements ClassPatchInterface
{
/**
* Checks if class has `__construct` method.
*
* @param ClassNode $node
*
* @return bool
*/
public function supports(ClassNode $node)
{
return true;
}
/**
* Makes all class constructor arguments optional.
*
* @param ClassNode $node
*/
public function apply(ClassNode $node)
{
if (!$node->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;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
/**
* Exception patch for HHVM to remove the stubs from special methods
*
* @author Christophe Coevoet <stof@notk.org>
*/
class HhvmExceptionPatch implements ClassPatchInterface
{
/**
* Supports exceptions on HHVM.
*
* @param ClassNode $node
*
* @return bool
*/
public function supports(ClassNode $node)
{
if (!defined('HHVM_VERSION')) {
return false;
}
return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception');
}
/**
* Removes special exception static methods from the doubled methods.
*
* @param ClassNode $node
*
* @return void
*/
public function apply(ClassNode $node)
{
if ($node->hasMethod('setTraceOptions')) {
$node->getMethod('setTraceOptions')->useParentCode();
}
if ($node->hasMethod('getTraceOptions')) {
$node->getMethod('getTraceOptions')->useParentCode();
}
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return -50;
}
}

View File

@@ -0,0 +1,140 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
/**
* Remove method functionality from the double which will clash with php keywords.
*
* @author Milan Magudia <milan@magudia.com>
*/
class KeywordPatch implements ClassPatchInterface
{
/**
* Support any class
*
* @param ClassNode $node
*
* @return boolean
*/
public function supports(ClassNode $node)
{
return true;
}
/**
* Remove methods that clash with php keywords
*
* @param ClassNode $node
*/
public function apply(ClassNode $node)
{
$methodNames = array_keys($node->getMethods());
$methodsToRemove = array_intersect($methodNames, $this->getKeywords());
foreach ($methodsToRemove as $methodName) {
$node->removeMethod($methodName);
}
}
/**
* Returns patch priority, which determines when patch will be applied.
*
* @return int Priority number (higher - earlier)
*/
public function getPriority()
{
return 49;
}
/**
* Returns array of php keywords.
*
* @return array
*/
private function getKeywords()
{
if (\PHP_VERSION_ID >= 70000) {
return array('__halt_compiler');
}
return array(
'__halt_compiler',
'abstract',
'and',
'array',
'as',
'break',
'callable',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'die',
'do',
'echo',
'else',
'elseif',
'empty',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'eval',
'exit',
'extends',
'final',
'finally',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'interface',
'isset',
'list',
'namespace',
'new',
'or',
'print',
'private',
'protected',
'public',
'require',
'require_once',
'return',
'static',
'switch',
'throw',
'trait',
'try',
'unset',
'use',
'var',
'while',
'xor',
'yield',
);
}
}

View File

@@ -0,0 +1,94 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever;
use Prophecy\PhpDocumentor\MethodTagRetrieverInterface;
/**
* Discover Magical API using "@method" PHPDoc format.
*
* @author Thomas Tourlourat <thomas@tourlourat.com>
* @author Kévin Dunglas <dunglas@gmail.com>
* @author Théo FIDRY <theo.fidry@gmail.com>
*/
class MagicCallPatch implements ClassPatchInterface
{
private $tagRetriever;
public function __construct(MethodTagRetrieverInterface $tagRetriever = null)
{
$this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever;
}
/**
* Support any class
*
* @param ClassNode $node
*
* @return boolean
*/
public function supports(ClassNode $node)
{
return true;
}
/**
* Discover Magical API
*
* @param ClassNode $node
*/
public function apply(ClassNode $node)
{
$types = array_filter($node->getInterfaces(), function ($interface) {
return 0 !== strpos($interface, 'Prophecy\\');
});
$types[] = $node->getParentClass();
foreach ($types as $type) {
$reflectionClass = new \ReflectionClass($type);
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);
$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;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
use Prophecy\Doubler\Generator\Node\ArgumentNode;
/**
* Add Prophecy functionality to the double.
* This is a core class patch for Prophecy.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class ProphecySubjectPatch implements ClassPatchInterface
{
/**
* Always returns true.
*
* @param ClassNode $node
*
* @return bool
*/
public function supports(ClassNode $node)
{
return true;
}
/**
* Apply Prophecy functionality to class node.
*
* @param ClassNode $node
*/
public function apply(ClassNode $node)
{
$node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface');
$node->addProperty('objectProphecyClosure', 'private');
foreach ($node->getMethods() as $name => $method) {
if ('__construct' === strtolower($name)) {
continue;
}
if ($method->getReturnType() === 'void') {
$method->setCode(
'$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'
);
} else {
$method->setCode(
'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());'
);
}
}
$prophecySetter = new MethodNode('setProphecy');
$prophecyArgument = new ArgumentNode('prophecy');
$prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface');
$prophecySetter->addArgument($prophecyArgument);
$prophecySetter->setCode('$this->objectProphecyClosure = function () use ($prophecy) { return $prophecy; };');
$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)),
\$this->getProphecy(), 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;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
/**
* ReflectionClass::newInstance patch.
* Makes first argument of newInstance optional, since it works but signature is misleading
*
* @author Florian Klein <florian.klein@free.fr>
*/
class ReflectionClassNewInstancePatch implements ClassPatchInterface
{
/**
* Supports ReflectionClass
*
* @param ClassNode $node
*
* @return bool
*/
public function supports(ClassNode $node)
{
return 'ReflectionClass' === $node->getParentClass();
}
/**
* Updates newInstance's first argument to make it optional
*
* @param ClassNode $node
*/
public function apply(ClassNode $node)
{
foreach ($node->getMethod('newInstance')->getArguments() as $argument) {
$argument->setDefault(null);
}
}
/**
* Returns patch priority, which determines when patch will be applied.
*
* @return int Priority number (higher = earlier)
*/
public function getPriority()
{
return 50;
}
}

View File

@@ -0,0 +1,123 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
/**
* SplFileInfo patch.
* Makes SplFileInfo and derivative classes usable with Prophecy.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class SplFileInfoPatch implements ClassPatchInterface
{
/**
* Supports everything that extends SplFileInfo.
*
* @param ClassNode $node
*
* @return bool
*/
public function supports(ClassNode $node)
{
if (null === $node->getParentClass()) {
return false;
}
return 'SplFileInfo' === $node->getParentClass()
|| is_subclass_of($node->getParentClass(), 'SplFileInfo')
;
}
/**
* Updated constructor code to call parent one with dummy file argument.
*
* @param ClassNode $node
*/
public function apply(ClassNode $node)
{
if ($node->hasMethod('__construct')) {
$constructor = $node->getMethod('__construct');
} else {
$constructor = new MethodNode('__construct');
$node->addMethod($constructor);
}
if ($this->nodeIsDirectoryIterator($node)) {
$constructor->setCode('return parent::__construct("' . __DIR__ . '");');
return;
}
if ($this->nodeIsSplFileObject($node)) {
$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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,83 @@
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\ClassPatch;
use Prophecy\Doubler\Generator\Node\ClassNode;
use Prophecy\Doubler\Generator\Node\MethodNode;
/**
* Traversable interface patch.
* Forces classes that implement interfaces, that extend Traversable to also implement Iterator.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class TraversablePatch implements ClassPatchInterface
{
/**
* Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate.
*
* @param ClassNode $node
*
* @return bool
*/
public function supports(ClassNode $node)
{
if (in_array('Iterator', $node->getInterfaces())) {
return false;
}
if (in_array('IteratorAggregate', $node->getInterfaces())) {
return false;
}
foreach ($node->getInterfaces() as $interface) {
if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) {
continue;
}
if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) {
continue;
}
if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) {
continue;
}
return true;
}
return false;
}
/**
* Forces class to implement Iterator interface.
*
* @param ClassNode $node
*/
public function apply(ClassNode $node)
{
$node->addInterface('Iterator');
$node->addMethod(new MethodNode('current'));
$node->addMethod(new MethodNode('key'));
$node->addMethod(new MethodNode('next'));
$node->addMethod(new MethodNode('rewind'));
$node->addMethod(new MethodNode('valid'));
}
/**
* Returns patch priority, which determines when patch will be applied.
*
* @return int Priority number (higher - earlier)
*/
public function getPriority()
{
return 100;
}
}