/**
  * 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;
             }
         }
     }
 }