NoOne4rever /
Axessors
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * This file is a part of "Axessors" library. |
||
| 4 | * |
||
| 5 | * @author <[email protected]> |
||
| 6 | * @license GPL |
||
| 7 | */ |
||
| 8 | |||
| 9 | namespace NoOne4rever\Axessors; |
||
| 10 | |||
| 11 | use NoOne4rever\Axessors\Exceptions\{ |
||
| 12 | AxessorsError, |
||
| 13 | InternalError, |
||
| 14 | OopError |
||
| 15 | }; |
||
| 16 | |||
| 17 | /** |
||
| 18 | * Class CallProcessor |
||
| 19 | * |
||
| 20 | * Handles method call. |
||
| 21 | * |
||
| 22 | * @package NoOne4rever\Axessors |
||
| 23 | */ |
||
| 24 | class CallProcessor |
||
| 25 | { |
||
| 26 | /** @var string method name */ |
||
| 27 | private $method; |
||
| 28 | /** @var mixed|null an object */ |
||
| 29 | private $object; |
||
| 30 | /** @var string class name */ |
||
| 31 | private $class; |
||
| 32 | /** @var int method call line number */ |
||
| 33 | private $line; |
||
| 34 | /** @var string method call file name */ |
||
| 35 | private $file; |
||
| 36 | /** @var string the name of calling class */ |
||
| 37 | private $callingClass; |
||
| 38 | /** @var \ReflectionClass class data */ |
||
| 39 | private $reflection; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * CallProcessor constructor. |
||
| 43 | * |
||
| 44 | * @param array $backtrace contains call stack |
||
| 45 | * @param mixed|null $object an object the method is called on (might not be given, if the method is static) |
||
| 46 | */ |
||
| 47 | 102 | public function __construct(array $backtrace, $object = null) |
|
| 48 | { |
||
| 49 | 102 | $this->object = $object; |
|
| 50 | 102 | $this->class = $backtrace[0]['class']; |
|
| 51 | 102 | for ($i = 1; ; ++$i) { |
|
| 52 | 102 | if (isset($backtrace[$i]['line'])) { |
|
| 53 | 102 | $this->line = $backtrace[$i]['line']; |
|
| 54 | 102 | $this->file = $backtrace[$i]['file']; |
|
| 55 | 102 | break; |
|
| 56 | } |
||
| 57 | } |
||
| 58 | 102 | $this->callingClass = $backtrace[1]['class']; |
|
| 59 | 102 | } |
|
| 60 | |||
| 61 | /** |
||
| 62 | * General function of calling the method. |
||
| 63 | * |
||
| 64 | * Processes method's name to extract property name and checks if the method is accessible. |
||
| 65 | * |
||
| 66 | * @param array $args the arguments of the method called |
||
| 67 | * @param string $method the name of the method called |
||
| 68 | * @return mixed return value of the method called |
||
| 69 | * @throws AxessorsError is thrown when Axessors method not found |
||
| 70 | * @throws OopError is thrown when the method is not accessible |
||
| 71 | */ |
||
| 72 | 102 | public function call(array $args, string $method) |
|
| 73 | { |
||
| 74 | 102 | $classData = Data::getClass($this->class); |
|
| 75 | 102 | $this->method = $method; |
|
| 76 | 102 | $this->reflection = $classData->reflection; |
|
| 77 | 102 | $propertyData = $this->searchMethod($classData); |
|
| 78 | 92 | $runner = new MethodRunner(0, $propertyData, $this->class, $this->method, $this->object); |
|
| 79 | 92 | return $runner->run($args, $this->file, $this->line); |
|
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * Searches called method in Axessors methods. |
||
| 84 | * |
||
| 85 | * @param ClassData $classData class data |
||
| 86 | * @return PropertyData property that has the called method |
||
| 87 | * @throws AxessorsError if the called method not found |
||
| 88 | * @throws OopError if the called method is not accessible |
||
| 89 | */ |
||
| 90 | 102 | private function searchMethod(ClassData $classData): PropertyData |
|
| 91 | { |
||
| 92 | 102 | while (!is_null($classData)) { |
|
| 93 | 102 | foreach ($classData->getAllProperties() as $propertyData) { |
|
| 94 | 102 | foreach ($propertyData->getMethods() as $modifier => $methods) { |
|
| 95 | 102 | foreach ($methods as $method) { |
|
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 96 | 102 | if ($this->method === $method) { |
|
| 97 | 96 | if ($this->isAccessible($modifier, $classData->reflection)) { |
|
| 98 | 92 | return $propertyData; |
|
| 99 | } else { |
||
| 100 | 80 | throw new OopError("method $this->class::$this->method() is not accessible there"); |
|
| 101 | } |
||
| 102 | } |
||
| 103 | } |
||
| 104 | } |
||
| 105 | } |
||
| 106 | 12 | $classData = $this->getParentClass($classData); |
|
| 107 | } |
||
| 108 | 6 | throw new AxessorsError("method {$this->class}::{$this->method}() not found"); |
|
| 109 | } |
||
| 110 | |||
| 111 | /** |
||
| 112 | * Returns parent class data. |
||
| 113 | * |
||
| 114 | * @param ClassData $classData class data |
||
| 115 | * @return ClassData|null parent class |
||
| 116 | */ |
||
| 117 | 12 | private function getParentClass(ClassData $classData): ?ClassData |
|
| 118 | { |
||
| 119 | 12 | $reflection = $classData->reflection->getParentClass(); |
|
| 120 | 12 | if ($reflection === false) { |
|
| 121 | 2 | return null; |
|
| 122 | } |
||
| 123 | try { |
||
| 124 | 10 | return Data::getClass($reflection->name); |
|
| 125 | 4 | } catch (InternalError $error) { |
|
| 126 | 4 | return null; |
|
| 127 | } |
||
| 128 | } |
||
| 129 | |||
| 130 | /** |
||
| 131 | * Checks if the method is accessible. |
||
| 132 | * |
||
| 133 | * @param string $accessModifier access modifier |
||
| 134 | * @param \ReflectionClass $reflection class data |
||
| 135 | * @return bool result of the checkout |
||
| 136 | * @throws InternalError if not a valid it's impossible to check accessibility. |
||
| 137 | */ |
||
| 138 | 96 | private function isAccessible(string $accessModifier, \ReflectionClass $reflection): bool |
|
| 139 | { |
||
| 140 | switch ($accessModifier) { |
||
| 141 | 96 | case 'public': |
|
| 142 | 88 | return true; |
|
| 143 | 8 | case 'protected': |
|
| 144 | 4 | return $this->isAccessibleProtected($reflection); |
|
| 145 | 4 | case 'private': |
|
| 146 | 4 | return $this->isAccessiblePrivate($reflection); |
|
| 147 | } |
||
| 148 | throw new InternalError('not a valid access modifier given'); |
||
| 149 | } |
||
| 150 | |||
| 151 | /** |
||
| 152 | * Checks if a private method is accessible. |
||
| 153 | * |
||
| 154 | * @param \ReflectionClass $reflection reflection |
||
| 155 | * @return bool the result of the checkout |
||
| 156 | */ |
||
| 157 | 6 | private function isAccessiblePrivate(\ReflectionClass $reflection): bool |
|
| 158 | { |
||
| 159 | 6 | $isCalledClass = $this->callingClass === $reflection->name; |
|
| 160 | 6 | $inCalledClass = $this->file === $reflection->getFileName() && $this->in($reflection); |
|
| 161 | 6 | return $isCalledClass && $inCalledClass; |
|
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * Checks if a protected method is accessible. |
||
| 166 | * |
||
| 167 | * @param \ReflectionClass $reflection reflection |
||
| 168 | * @return bool the result of the checkout |
||
| 169 | */ |
||
| 170 | 4 | private function isAccessibleProtected(\ReflectionClass $reflection): bool |
|
| 171 | { |
||
| 172 | 4 | $isSubclass = is_subclass_of($this->callingClass, $reflection->name); |
|
| 173 | 4 | $reflection = new \ReflectionClass($this->callingClass); |
|
| 174 | 4 | $inSubclass = $this->file === $reflection->getFileName() && $this->in($reflection); |
|
| 175 | 4 | return ($isSubclass && $inSubclass) || $this->isAccessiblePrivate($reflection); |
|
| 176 | } |
||
| 177 | |||
| 178 | /** |
||
| 179 | * Checks if the method is called in right place and it is accessible there. |
||
| 180 | * |
||
| 181 | * @param \ReflectionClass $reflection class data |
||
| 182 | * @return bool result of the checkout |
||
| 183 | */ |
||
| 184 | 4 | private function in(\ReflectionClass $reflection): bool |
|
| 185 | { |
||
| 186 | 4 | return $reflection->getStartLine() <= $this->line && $reflection->getEndLine() >= $this->line; |
|
| 187 | } |
||
| 188 | } |
||
| 189 |