/** * Resolve GodObjects * * 8 public methods or more (excluding getters and setters) * lack of cohesion of methods * instanciate 6 or more different classes * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new GodObject($class->getFullname()); // we don't look private methods $collection = (new Collection($class->getMethods()))->where('method => method.getVisibility() == "public"'); // at least 8 public methods if (sizeof($collection) < 8) { return; } // lack of cohesion of methods $lcom = new LackOfCohesionOfMethods(); $result = $lcom->calculate($class); if ($result->getLcom() < 3) { return; } // know everything (instanciate more than 8 different classes) $nb = 0; foreach ($collection as $method) { $instancied = array_diff($method->getInstanciedClasses(), array($class->getFullname())); $nb += sizeof($instancied); } if ($nb < 6) { return; } $resolved->pushPattern($pattern); }
/** * Resolve AbstractFactory * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new AbstractFactory($class->getFullname()); $resolver = new TypeResolver(); // we don't look private and non static methods $collection = (new Collection($class->getMethods()))->where(sprintf('method => method.getVisibility() == "public" and method.getState() == %s', ReflectedMethod::STATE_STATIC)); if (empty($collection)) { return; } /** @var ReflectedMethod $method */ foreach ($collection as $method) { // method instanciates at least one object $instanciated = array_unique($method->getInstanciedClasses()); if (empty($instanciated)) { continue; } foreach ($method->getReturns() as $return) { // returns an object if ($resolver->isNative($return->getType())) { continue; } // doesn't return itself (avoid singleton) if (in_array($return->getType(), array('\\self', '\\static', $class->getFullname()))) { continue; } $pattern->setCreated($return->getType()); $resolved->pushPattern($pattern); return; } } }
/** * Resolve Decorators * * extends one class, * has one public method * receive one argument and call this argument * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new Decorator($class->getFullname()); // extends one class if (!$class->getParent()) { return; } // exclude magical methods $collection = (new Collection($class->getMethods()))->where('method => !isMagicMethod(method)'); // class should have only one public method $collection->where('method => method.getVisibility() == "public"'); if (sizeof($collection) != 1) { return; } // receive one argument $collection->where('count(method.getArguments()) == 1'); if (sizeof($collection) != 1) { return; } // call received argument $method = $collection->first(); foreach ($method->getArguments() as $argument) { if (in_array($argument->getName(), $method->getExternalCalls())) { $pattern->setDecorated($argument->getType()); $resolved->pushPattern($pattern); return; } } }
/** * Resolve facades * * Call lot of non instancied external classes * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new Facade($class->getFullname()); foreach ($class->getMethods() as $method) { $instancied = array_unique($method->getInstanciedClasses()); // $externalCalls = $method->getExternalCalls(); // today PhpMetrics is not able to get external calls when they are made on internal propery // ex: $this->foo->bar() // we will count T_OBJECT_CALL if (preg_match_all('!\\->\\w+\\(!', $method->getTokens()->asString(), $matches)) { $nbExternalCalls = sizeof($matches[0]); } if (sizeof($instancied) == 0 && $nbExternalCalls >= 2) { $resolved->pushPattern($pattern); return; } } }
/** * Resolve Structures * * extends nothing * has only getter and setters * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new Structure($class->getFullname()); // extends one class if ($class->getParent()) { return; } // we don't look private methods $collection1 = (new Collection($class->getMethods()))->where('method => method.getVisibility() == "public"'); // at least 2 public methods if (sizeof($collection1) < 2) { return; } // list getters and setters $collection2 = (new Collection($class->getMethods()))->where('method => (method.isGetter() or method.isSetter()) and method.getVisibility() == "public"'); if (sizeof($collection1) === sizeof($collection2)) { $resolved->pushPattern($pattern); } }
/** * Resolve bridges * * Bridge is identified by its Abstraction class, which * manages a number of Implementors and has some realization as RefinedAbstraction. Each * Implementor has some realization. * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new Bridge($class->getFullname()); // uses more than 2 classes ? $deps = $class->getDependencies(); if (sizeof($deps) < 1) { return; } // preparing data // only for algorithm $map = []; foreach ($deps as $index => $dep) { $dep = $this->searchClassNamed($dep, $this->classes); if (!$dep || $dep->getFullname() === $class->getFullname()) { continue; } $map[$dep->getFullname()] = $dep; } // uses a class (abstraction) that itslef uses another that main class uses (implementation) ? foreach ($map as $dep) { // get dependencies of this dependency $relatedDeps = $dep->getDependencies(); foreach ($relatedDeps as $relatedDep) { $relatedDep = $this->searchClassNamed($relatedDep, $this->classes); if (!$relatedDep || $relatedDep->getFullname() === $class->getFullname()) { continue; } // does this related dependencies is also used by main dependency ? if (isset($map[$relatedDep->getFullname()]) && $dep->getFullname() !== $relatedDep->getFullname()) { $pattern->setAbstractness($dep->getFullname()); $pattern->setImplemenation($relatedDep->getFullname()); $resolved->pushPattern($pattern); return; } } } }
/** * Resolve Singleton * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new Singleton($class->getFullname()); $methods = $class->getMethods(); // private construct ? if (isset($methods['__construct']) && $methods['__construct']->getVisibility() !== ReflectedMethod::VISIBILITY_PUBLIC) { // static method ? foreach ($methods as $method) { if ($method->getState() === ReflectedMethod::STATE_STATIC) { // call itself ? $deps = $method->getReturns(); foreach ($deps as $dep) { if (in_array($dep->getType(), array('\\self', '\\static', $class->getFullname()))) { // yes, class is a singleton $resolved->pushPattern($pattern); return; } } } } } }
/** * Resolve proxies * * A class implements an interface or extends * an (abstract) class, and owns a * reference to a class that implements the * same interface or extends the same (abstract) * class. * * @param ResolvedClass $resolved */ public function resolve(ResolvedClass $resolved) { $class = $resolved->getClass(); $pattern = new Proxy($class->getFullname()); if ($class->getParent() || $class->getInterfaces()) { $deps = $class->getDependencies(); foreach ($deps as $dep) { // does call any class ? $dep = $this->searchClassNamed($dep, $this->classes); if (!$dep || $dep->getFullname() === $class->getFullname()) { continue; } // same parent ? $sameKind = null !== $dep->getParent() && $dep->getParent() === $class->getParent(); // same interface ? $sameKind = $sameKind || sizeof(array_intersect($class->getInterfaces(), $dep->getInterfaces())) > 0; if ($sameKind) { $pattern->setProxified($dep->getFullname()); $resolved->pushPattern($pattern); return; } } } }