OriginalDrupalKernelHelper   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 69
Duplicated Lines 0 %

Test Coverage

Coverage 96.67%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 30
c 1
b 0
f 1
dl 0
loc 69
ccs 29
cts 30
cp 0.9667
rs 10
wmc 8

2 Methods

Rating   Name   Duplication   Size   Complexity  
A createOriginalDrupalKernelSubstitute() 0 29 4
A substitute() 0 27 4
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the ekino Drupal Debug project.
7
 *
8
 * (c) ekino
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Ekino\Drupal\Debug\Kernel\Helper;
15
16
use Composer\Autoload\ClassLoader;
17
use Drupal\Core\DrupalKernel;
18
use Ekino\Drupal\Debug\Exception\NotSupportedException;
19
use Ekino\Drupal\Debug\Kernel\DebugKernel;
20
use Ekino\Drupal\Debug\Resource\Model\ResourcesCollection;
21
use Ekino\Drupal\Debug\Resource\ResourcesFreshnessChecker;
22
use Symfony\Component\Config\Resource\FileExistenceResource;
23
use Symfony\Component\Config\Resource\FileResource;
24
use Symfony\Component\Filesystem\Filesystem;
25
26
/**
27
 * This helper substitutes entirely the original DrupalKernel with the
28
 * DebugKernel.
29
 *
30
 * The substitution is done with a class_alias.
31
 *
32
 * An original DrupalKernel substitute with a dedicated name
33
 * (OriginalDrupalKernel) must be created because the DebugKernel cannot
34
 * technically substitute the "\Drupal\Core\DrupalKernel" class and extends it
35
 * at the same time.
36
 *
37
 * The original DrupalKernel and the DebugKernel classes must not be loaded
38
 * before the alias declaration.
39
 */
40
class OriginalDrupalKernelHelper
41
{
42
    /**
43
     * @param ClassLoader $classLoader
44
     * @param string      $cacheDirectory
45
     */
46 7
    public static function substitute(ClassLoader $classLoader, string $cacheDirectory): void
47
    {
48 7
        $originalDrupalKernelFilePath = $classLoader->findFile('Drupal\Core\DrupalKernel');
49 7
        if (!\is_string($originalDrupalKernelFilePath)) {
50 1
            throw new \RuntimeException('The original DrupalKernel class file could not be found.');
51
        }
52
53 6
        $originalDrupalKernelSubstituteFilePath = \sprintf('%s/OriginalDrupalKernel.php', $cacheDirectory);
54
        // We watch the original DrupalKernel and the DebugKernel : if they did
55
        // not change, then the original DrupalKernel substitute does not need
56
        // to be recreated.
57 6
        $resourcesFreshnessChecker = new ResourcesFreshnessChecker(\sprintf('%s.meta', $originalDrupalKernelSubstituteFilePath), new ResourcesCollection(array(
58 6
            new FileExistenceResource($originalDrupalKernelSubstituteFilePath),
59 6
            new FileResource($originalDrupalKernelFilePath),
60 6
            new FileResource(\sprintf('%s/../DebugKernel.php', __DIR__)),
61
        )));
62
63 6
        if (!$resourcesFreshnessChecker->isFresh()) {
64 4
            self::createOriginalDrupalKernelSubstitute($originalDrupalKernelFilePath, $originalDrupalKernelSubstituteFilePath);
65
66 2
            $resourcesFreshnessChecker->commit();
67
        }
68
69 4
        require $originalDrupalKernelSubstituteFilePath;
70
71 4
        if (!@\class_alias(DebugKernel::class, DrupalKernel::class)) {
72 2
            throw new \RuntimeException('The DebugKernel class could not be aliased.');
73
        }
74 2
    }
75
76
    /**
77
     * @param string $originalDrupalKernelFilePath
78
     * @param string $originalDrupalKernelSubstituteFilePath
79
     */
80 4
    private static function createOriginalDrupalKernelSubstitute(string $originalDrupalKernelFilePath, string $originalDrupalKernelSubstituteFilePath): void
81
    {
82 4
        $content = @\file_get_contents($originalDrupalKernelFilePath);
83 4
        if (false === $content) {
84 1
            throw new \RuntimeException('The original DrupalKernel content could not be read.');
85
        }
86
87 3
        $content = \preg_replace(array(
88 3
            '/^class DrupalKernel/m',
89
            '/__DIR__/',
90
        ), array(
91 3
            'class OriginalDrupalKernel',
92 3
            \sprintf("'%s'", \dirname($originalDrupalKernelFilePath)),
93 3
        ), $content, -1, $replacementsDoneCount);
94 3
        if (null === $content) {
95
            throw new \RuntimeException('The original DrupalKernel specific values could not be replaced.');
96
        }
97
98
        // There should be only 2 replacements :
99
        //   * The class name
100
        //   * And the __DIR__ in the ::guessApplicationRoot() method
101
        //
102
        // If there is another replacement, we cannot support it naively. For
103
        // safety, it has to be reviewed.
104 3
        if (2 !== $replacementsDoneCount) {
105 1
            throw new NotSupportedException('There should be strictly 2 replacements done in the original DrupalKernel substitute.');
106
        }
107
108 2
        (new Filesystem())->dumpFile($originalDrupalKernelSubstituteFilePath, $content);
109 2
    }
110
}
111