Completed
Push — develop ( fae898...646b61 )
by Tom
04:48
created

ClassExistsChecker   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 12
c 0
b 0
f 0
lcom 1
cbo 3
dl 0
loc 126
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 4 1
A __construct() 0 4 1
A existsExtendsSafe() 0 12 2
A startContext() 0 10 1
A exceptionContext() 0 13 2
A endContext() 0 9 2
A autoloadTerminator() 0 21 3
1
<?php
2
/**
3
 * this file is part of magerun
4
 *
5
 * @author Tom Klingenberg <https://github.com/ktomk>
6
 */
7
namespace N98\Magento\Command\Developer\Module\Rewrite;
8
9
use BadMethodCallException;
10
use Exception;
11
use N98\Util\AutoloadHandler;
12
use stdClass;
13
14
/**
15
 * More robust (against fatal errors in the inheritance chain) class_exists checker
16
 *
17
 * @package N98\Magento\Command\Developer\Module\Rewrite
18
 */
19
final class ClassExistsChecker
20
{
21
    /**
22
     * @var string
23
     */
24
    private $className;
25
26
    /**
27
     * @var stdClass
28
     */
29
    private $context;
30
31
    /**
32
     * @param string $className
33
     *
34
     * @return ClassExistsChecker
35
     */
36
    public static function create($className)
37
    {
38
        return new self($className);
39
    }
40
41
    /**
42
     * ClassExistsChecker constructor.
43
     *
44
     * @param string $className
45
     */
46
    public function __construct($className)
47
    {
48
        $this->className = $className;
49
    }
50
51
    /**
52
     * Check for class-existence while handling conditional definition of classes that extend from non-existent classes
53
     * as it can happen with Magento Varien_Autoload that is using include to execute files for class definitions.
54
     *
55
     * @return bool
56
     */
57
    public function existsExtendsSafe()
58
    {
59
        $context = $this->startContext();
60
        try {
61
            $exists = class_exists($this->className);
62
        } catch (Exception $ex) {
63
            return $this->exceptionContext($context, $ex);
64
        }
65
        $this->endContext($context);
66
67
        return $exists;
68
    }
69
70
    /**
71
     * @return stdClass
72
     */
73
    private function startContext()
74
    {
75
        $context = new stdClass();
76
        $context->lastException = null;
77
        $context->stack = array();
78
        $context->terminator = AutoloadHandler::create(array($this, 'autoloadTerminator'));
79
        $context->className = $this->className;
80
81
        return $this->context = $context;
82
    }
83
84
    /**
85
     * @param $context
86
     * @param Exception $ex
87
     * @return bool
88
     */
89
    private function exceptionContext($context, Exception $ex)
90
    {
91
        /** @var $terminator AutoloadHandler */
92
        $terminator = $context->terminator;
93
        $terminator->reset();
94
95
        if ($ex !== $context->lastException) {
96
            $message = sprintf('Exception when checking for class %s existence', $context->className);
97
            throw new ClassExistsThrownException($message, 0, $ex);
98
        }
99
100
        return false;
101
    }
102
103
    /**
104
     * @param $context
105
     */
106
    private function endContext($context)
107
    {
108
        if (isset($context->terminator)) {
109
            /** @var $terminator AutoloadHandler */
110
            $terminator = $context->terminator;
111
            $terminator->reset();
112
        }
113
        $this->context = null;
114
    }
115
116
    /**
117
     * Method is called as last auto-loader (if all others have failed), so the class does not exists (is not
118
     * resolve-able)
119
     *
120
     * @param $notFoundClass
121
     * @throws CanNotAutoloadCollaboratorClassException
122
     */
123
    public function autoloadTerminator($notFoundClass)
124
    {
125
        $className = $this->className;
126
        if (null === $context = $this->context) {
127
            //@codeCoverageIgnoreStart
128
            // sanity check, should never come here
129
            throw new BadMethodCallException('No autoloading in place');
130
            // @codeCoverageIgnoreStop
131
        }
132
133
        if ($notFoundClass === $className) {
134
            return;
135
        }
136
137
        $context->stack[] = array($notFoundClass, $className);
138
139
        $context->lastException = new CanNotAutoloadCollaboratorClassException(
140
            sprintf('%s for %s', $notFoundClass, $className)
141
        );
142
        throw $context->lastException;
143
    }
144
}
145