1<?php 2 3namespace Prophecy\Doubler\ClassPatch; 4 5use Prophecy\Doubler\Generator\Node\ClassNode; 6use Prophecy\Exception\Doubler\ClassCreatorException; 7 8class ThrowablePatch implements ClassPatchInterface 9{ 10 /** 11 * Checks if patch supports specific class node. 12 * 13 * @param ClassNode $node 14 * @return bool 15 */ 16 public function supports(ClassNode $node) 17 { 18 return $this->implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); 19 } 20 21 /** 22 * @param ClassNode $node 23 * @return bool 24 */ 25 private function implementsAThrowableInterface(ClassNode $node) 26 { 27 foreach ($node->getInterfaces() as $type) { 28 if (is_a($type, 'Throwable', true)) { 29 return true; 30 } 31 } 32 33 return false; 34 } 35 36 /** 37 * @param ClassNode $node 38 * @return bool 39 */ 40 private function doesNotExtendAThrowableClass(ClassNode $node) 41 { 42 return !is_a($node->getParentClass(), 'Throwable', true); 43 } 44 45 /** 46 * Applies patch to the specific class node. 47 * 48 * @param ClassNode $node 49 * 50 * @return void 51 */ 52 public function apply(ClassNode $node) 53 { 54 $this->checkItCanBeDoubled($node); 55 $this->setParentClassToException($node); 56 } 57 58 private function checkItCanBeDoubled(ClassNode $node) 59 { 60 $className = $node->getParentClass(); 61 if ($className !== 'stdClass') { 62 throw new ClassCreatorException( 63 sprintf( 64 'Cannot double concrete class %s as well as implement Traversable', 65 $className 66 ), 67 $node 68 ); 69 } 70 } 71 72 private function setParentClassToException(ClassNode $node) 73 { 74 $node->setParentClass('Exception'); 75 76 $node->removeMethod('getMessage'); 77 $node->removeMethod('getCode'); 78 $node->removeMethod('getFile'); 79 $node->removeMethod('getLine'); 80 $node->removeMethod('getTrace'); 81 $node->removeMethod('getPrevious'); 82 $node->removeMethod('getNext'); 83 $node->removeMethod('getTraceAsString'); 84 } 85 86 /** 87 * Returns patch priority, which determines when patch will be applied. 88 * 89 * @return int Priority number (higher - earlier) 90 */ 91 public function getPriority() 92 { 93 return 100; 94 } 95} 96