Bootstrap::writeComposerAutoloaderPatch()   B
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 15
cts 15
cp 1
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 16
nc 5
nop 0
crap 4
1
<?php
2
3
namespace MagentoHackathon\Composer\Magento\Patcher;
4
5
use Composer\IO\NullIO;
6
use Composer\IO\IOInterface;
7
use MagentoHackathon\Composer\Magento\ProjectConfig;
8
9
class Bootstrap
10
{
11
    /**
12
     * String inserted as a PHP comment, before and after the patch code.
13
     */
14
    const PATCH_MARK = 'AUTOLOADER PATCH';
15
16
    /**
17
     * @var ProjectConfig
18
     */
19
    private $config;
20
21
    /**
22
     * @var IOInterface
23
     */
24
    private $io;
25
26
    /**
27
     * @var string
28
     */
29
    private $mageClassFilePath;
30
31
    /**
32
     * @param string $mageClassFilePath Path to the Mage.php file which the patch will be applied on.
33
     * @param ProjectConfig $config
34
     */
35 8
    private function __construct($mageClassFilePath, ProjectConfig $config)
36
    {
37 8
        $this->setMageClassFilePath($mageClassFilePath);
38 8
        $this->config = $config;
39 8
    }
40
41
    /**
42
     * @param ProjectConfig $config
43
     * @return $this
44
     */
45 8
    public static function fromConfig(ProjectConfig $config)
46
    {
47 8
        return new self($config->getMagentoRootDir() . '/app/Mage.php', $config);
48
    }
49
50
    /**
51
     * @return ProjectConfig
52
     */
53 8
    private function getConfig()
54
    {
55 8
        return $this->config;
56
    }
57
58
    /**
59
     * @return string
60
     * @throws \DomainException
61
     */
62 6
    private function getMageClassFilePath()
63
    {
64 6
        $mageFileCheck = true;
65
66 6
        if (!is_file($this->mageClassFilePath)) {
67 4
            $message = "{$this->mageClassFilePath} is not a file.";
68 4
            $mageFileCheck = false;
69 2
        } elseif (!is_readable($this->mageClassFilePath)) {
70
            $message = "{$this->mageClassFilePath} is not readable.";
71
            $mageFileCheck = false;
72 2
        } elseif (!is_writable($this->mageClassFilePath)) {
73
            $message = "{$this->mageClassFilePath} is not writable.";
74
            $mageFileCheck = false;
75
        }
76
77 6
        if (!$mageFileCheck) {
78 4
            throw new \DomainException($message);
79
        }
80
81 2
        return $this->mageClassFilePath;
82
    }
83
84
    /**
85
     * Path to the Mage.php file which the patch will be applied on.
86
     *
87
     * @param string $mageClassFilePath
88
     */
89 8
    private function setMageClassFilePath($mageClassFilePath)
90
    {
91 8
        $this->mageClassFilePath = $mageClassFilePath;
92 8
    }
93
94
    /**
95
     * @return bool
96
     */
97 6
    private function isPatchAlreadyApplied()
98
    {
99 6
        return strpos(file_get_contents($this->getMageClassFilePath()), self::PATCH_MARK) !== false;
100
    }
101
102
    /**
103
     * @return bool
104
     */
105 8
    public function canApplyPatch()
106
    {
107
        // check the config first
108 8
        if (!$this->getConfig()->mustApplyBootstrapPatch()) {
109 2
            $message = "<comment>Magento autoloader patching skipped because of configuration flag</comment>";
110 2
            $result = false;
111 6
        } elseif ($this->isPatchAlreadyApplied()) {
112 2
            $message = "<comment>{$this->getMageClassFilePath()} was already patched</comment>";
113 2
            $result = false;
114
        } else {
115 2
            $result = true;
116 2
            $message = "<info>Autoloader patch to {$this->getMageClassFilePath()} was applied successfully</info>";
117
        }
118
119 4
        $this->getIo()->write($message);
120
121 4
        return $result;
122
    }
123
124
    /**
125
     * @return bool
126
     */
127 8
    public function patch()
128
    {
129 8
        return $this->canApplyPatch() ? $this->writeComposerAutoloaderPatch() : false;
130
    }
131
132
    /**
133
     * @return string
134
     */
135
    protected function getAppPath()
136
    {
137
        return $this->getConfig()->getMagentoRootDir() . '/app';
138
    }
139
140
    /**
141
     * @return bool
142
     */
143 2
    protected function writeComposerAutoloaderPatch()
144
    {
145 2
        $mageFileContent = file($this->getMageClassFilePath());
146
147 2
        $mageFileBootstrapPart = '';
148 2
        $mageFileClassDeclarationPart = '';
149 2
        $isBootstrapPart = true;
150
151 2
        foreach ($mageFileContent as $row) {
152 2
            if ($isBootstrapPart) {
153 2
                $mageFileBootstrapPart .= $row;
154
            } else {
155 2
                $mageFileClassDeclarationPart .= $row;
156
            }
157 2
            if (strpos($row, 'Varien_Autoload') === 0) {
158 2
                $isBootstrapPart = false;
159
            }
160
        }
161
162 2
        $mageFileReplacement = $mageFileBootstrapPart . PHP_EOL
163 2
                             . $this->getAutoloaderPatchString() . PHP_EOL
164 2
                             . $mageFileClassDeclarationPart;
165
166 2
        return file_put_contents($this->getMageClassFilePath(), $mageFileReplacement) !== false;
167
    }
168
169
    /**
170
     * @param IOInterface $io
171
     */
172 3
    public function setIo(IOInterface $io)
173
    {
174 3
        $this->io = $io;
175 3
    }
176
177
    /**
178
     * @return IOInterface
179
     */
180 4
    public function getIo()
181
    {
182 4
        if (!$this->io) {
183 4
            $this->io = new NullIO;
184
        }
185 4
        return $this->io;
186
    }
187
188
    /**
189
     * @return string
190
     */
191 2
    private function getAutoloaderPatchString()
192
    {
193 2
        $patchMark = self::PATCH_MARK;
194
195
        // get the vendor folder name from Config, in case it's changed
196 2
        $vendorFolderName = basename($this->getConfig()->getVendorDir());
197
198 2
        $autoloadPhp = $vendorFolderName . '/autoload.php';
199
200
        return <<<PATCH
201 2
/** $patchMark **/
202 2
if (file_exists(\$autoloaderPath = BP . DS . '{$autoloadPhp}') ||
203 2
    file_exists(\$autoloaderPath = BP . DS . '../{$autoloadPhp}')
204
) {
205
    require \$autoloaderPath;
206
}
207 2
/** $patchMark **/
208
PATCH;
209
    }
210
}
211