1<?php 2 3/* 4 * This file is part of the Prophecy. 5 * (c) Konstantin Kudryashov <ever.zet@gmail.com> 6 * Marcello Duarte <marcello.duarte@gmail.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Prophecy\Doubler\ClassPatch; 13 14use Prophecy\Doubler\Generator\Node\ClassNode; 15use Prophecy\Doubler\Generator\Node\MethodNode; 16use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; 17use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; 18 19/** 20 * Discover Magical API using "@method" PHPDoc format. 21 * 22 * @author Thomas Tourlourat <thomas@tourlourat.com> 23 * @author Kévin Dunglas <dunglas@gmail.com> 24 * @author Théo FIDRY <theo.fidry@gmail.com> 25 */ 26class MagicCallPatch implements ClassPatchInterface 27{ 28 private $tagRetriever; 29 30 public function __construct(MethodTagRetrieverInterface $tagRetriever = null) 31 { 32 $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; 33 } 34 35 /** 36 * Support any class 37 * 38 * @param ClassNode $node 39 * 40 * @return boolean 41 */ 42 public function supports(ClassNode $node) 43 { 44 return true; 45 } 46 47 /** 48 * Discover Magical API 49 * 50 * @param ClassNode $node 51 */ 52 public function apply(ClassNode $node) 53 { 54 $types = array_filter($node->getInterfaces(), function ($interface) { 55 return 0 !== strpos($interface, 'Prophecy\\'); 56 }); 57 $types[] = $node->getParentClass(); 58 59 foreach ($types as $type) { 60 $reflectionClass = new \ReflectionClass($type); 61 62 while ($reflectionClass) { 63 $tagList = $this->tagRetriever->getTagList($reflectionClass); 64 65 foreach ($tagList as $tag) { 66 $methodName = $tag->getMethodName(); 67 68 if (empty($methodName)) { 69 continue; 70 } 71 72 if (!$reflectionClass->hasMethod($methodName)) { 73 $methodNode = new MethodNode($methodName); 74 $methodNode->setStatic($tag->isStatic()); 75 $node->addMethod($methodNode); 76 } 77 } 78 79 $reflectionClass = $reflectionClass->getParentClass(); 80 } 81 } 82 } 83 84 /** 85 * Returns patch priority, which determines when patch will be applied. 86 * 87 * @return integer Priority number (higher - earlier) 88 */ 89 public function getPriority() 90 { 91 return 50; 92 } 93} 94 95