Source of file MethodClassReflectionExtension.php
Size: 5,466 Bytes - Last Modified: 2017-09-25T23:10:06+00:00
/var/www/docs.ssmods.com/process/src/src/MethodClassReflectionExtension.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 | <?php declare(strict_types = 1); namespace SilbinaryWolf\SilverstripePHPStan; use \ReflectionClass; use \ReflectionMethod; use PHPStan\Reflection\MethodsClassReflectionExtension; use PHPStan\Reflection\BrokerAwareClassReflectionExtension; use PHPStan\Broker\Broker; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\FileTypeMapper; use PHPStan\Type\ObjectType; use PHPStan\Reflection\Php\PhpMethodReflection; // Silverstripe use Object; use Config; use DataObject; use ContentController; class MethodClassReflectionExtension implements MethodsClassReflectionExtension, BrokerAwareClassReflectionExtension { /** @var MethodReflection[][] */ private $methods = []; /** @var Broker */ private $broker; public function hasMethod(ClassReflection $classReflection, string $methodName): bool { if (!isset($this->methods[$classReflection->getName()])) { $this->methods[$classReflection->getName()] = $this->createMethods($classReflection); } return isset($this->methods[$classReflection->getName()][strtolower($methodName)]); } public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection { // Fallback to has_one/has_many/many_many return $this->methods[$classReflection->getName()][strtolower($methodName)]; } public function setBroker(Broker $broker) { $this->broker = $broker; } /** * @param ClassReflection $classReflection * @return MethodReflection[] */ private function createMethods(ClassReflection $classReflection): array { if (!$classReflection->isSubclassOf(Object::class)) { return []; } $methods = []; $class = $classReflection->getName(); $isDataObjectOrContentController = $classReflection->getName() === DataObject::class || $classReflection->isSubclassOf(DataObject::class); // Add methods from extensions $extensionInstances = Config::inst()->get($class, 'extensions'); if ($extensionInstances) { foreach ($extensionInstances as $extensionClass) { // Ignore parameters // ie. Extract "Versioned" from "Versioned('Stage', 'Live')" $extensionClass = explode('(', $extensionClass, 2); $extensionClass = $extensionClass[0]; $extensionClassReflection = $this->broker->getClass($extensionClass); foreach (get_class_methods($extensionClass) as $methodName) { /** @var $methodReflection PhpMethodReflection */ $methodReflection = $extensionClassReflection->getMethod($methodName); $methods[strtolower($methodName)] = $methodReflection; } } } // Detect little-known Silverstripe '_' cache function // ie. Define: function _MyFunction() // Call: $this->MyFunction() will be cached. // foreach (get_class_methods($class) as $methodName) { if ($methodName && $methodName[0] === '_' && isset($methodName[1]) && $methodName[1] !== '_') { $uncachedMethodName = substr($methodName, 1); $methods[strtolower($uncachedMethodName)] = new CachedMethod($classReflection->getMethod($methodName)); } } // Handle Page_Controller where it has $failover // NOTE(Jake): This is not foolproof, but if people follow the general SS convention // it'll work. if (strpos($class, '_Controller') !== FALSE && $classReflection->isSubclassOf(ContentController::class)) { $class = str_replace('_Controller', '', $class); $isDataObjectOrContentController = true; $failoverClassReflection = $this->broker->getClass($class); foreach (get_class_methods($class) as $methodName) { /** @var $methodReflection PhpMethodReflection */ $methodReflection = $failoverClassReflection->getMethod($methodName); $methods[strtolower($methodName)] = $methodReflection; } } // todo(Jake): Figure out if an extension magic __call() has precedence over a has_one magic call if ($isDataObjectOrContentController) { $components = array( 'has_one' => ComponentHasOneMethod::class, 'belongs_to' => ComponentHasOneMethod::class, 'has_many' => ComponentHasManyMethod::class, 'many_many' => ComponentManyManyMethod::class, 'belongs_many_many' => ComponentManyManyMethod::class, ); foreach ($components as $componentType => $componentClass) { $componentNameValueMap = Config::inst()->get($class, $componentType); if (!$componentNameValueMap) { continue; } foreach ($componentNameValueMap as $methodName => $type) { // Ignore parameters $type = explode('(', $type, 2); $type = $type[0]; $methods[strtolower($methodName)] = new $componentClass($methodName, $classReflection, new ObjectType($type)); } } } return $methods; } } |