Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 75
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
AliasAutoloader
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 6
462
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 autoload
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
56
 load
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 classTemplate
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 interfaceTemplate
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 traitTemplate
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3// FunctionsAndConstants
4
5namespace BrianHenryIE\Strauss {
6
7    use BrianHenryIE\Strauss\Types\AutoloadAliasInterface;
8
9    /**
10     * @see AutoloadAliasInterface
11     *
12     * @phpstan-type ClassAliasArray array{'type':'class',isabstract:bool,classname:string,namespace?:string,extends:string,implements:array<string>}
13     * @phpstan-type InterfaceAliasArray array{'type':'interface',interfacename:string,namespace?:string,extends:array<string>}
14     * @phpstan-type TraitAliasArray array{'type':'trait',traitname:string,namespace?:string,use:array<string>}
15     * @phpstan-type EnumAliasArray array{'type':'enum',enumname:string,namespace?:string,concrete:string}
16     * @phpstan-type AutoloadAliasArray array<string,ClassAliasArray|InterfaceAliasArray|TraitAliasArray|EnumAliasArray>
17     */
18    class AliasAutoloader
19    {
20        private string $includeFilePath;
21
22        /**
23         * @var AutoloadAliasArray
24         */
25        private array $autoloadAliases = [];
26
27        public function __construct()
28        {
29            $this->includeFilePath = __DIR__ . '/autoload_alias.php';
30        }
31
32        /**
33         * @param string $class
34         */
35        public function autoload($class): void
36        {
37            if (!isset($this->autoloadAliases[$class])) {
38                return;
39            }
40            switch ($this->autoloadAliases[$class]['type']) {
41                case 'class':
42                        $this->load(
43                            $this->classTemplate(
44                                $this->autoloadAliases[$class]
45                            )
46                        );
47                    break;
48                case 'interface':
49                    $this->load(
50                        $this->interfaceTemplate(
51                            $this->autoloadAliases[$class]
52                        )
53                    );
54                    break;
55                case 'trait':
56                    $this->load(
57                        $this->traitTemplate(
58                            $this->autoloadAliases[$class]
59                        )
60                    );
61                    break;
62                case 'enum':
63                    // Enums are final so cannot be aliased with an `extends` shim like classes are; `class_alias()`
64                    // makes the original name a true alias of the renamed enum, preserving case identity (`===`),
65                    // `match` arms, `::cases()`, `::from()` and `instanceof` the enum itself. Unlike the class shim,
66                    // the alias does not implement the enum's original interface names, so `instanceof` against an
67                    // original interface name is false. Enums require PHP 8.1, but this only executes when an enum
68                    // is autoloaded by its original name, i.e. on a runtime already using enums.
69                    \class_alias(
70                        $this->autoloadAliases[$class]['concrete'],
71                        $class
72                    );
73                    break;
74                default:
75                    // Never.
76                    break;
77            }
78        }
79
80        private function load(string $includeFile): void
81        {
82            file_put_contents($this->includeFilePath, $includeFile);
83            include $this->includeFilePath;
84            file_exists($this->includeFilePath) && unlink($this->includeFilePath);
85        }
86
87        /**
88         * @param ClassAliasArray $class
89         */
90        private function classTemplate(array $class): string
91        {
92            $abstract = $class['isabstract'] ? 'abstract ' : '';
93            $classname = $class['classname'];
94            if (isset($class['namespace'])) {
95                $namespace = "namespace {$class['namespace']};";
96                $extends = '\\' . $class['extends'];
97                $implements = empty($class['implements']) ? ''
98                : ' implements \\' . implode(', \\', $class['implements']);
99            } else {
100                $namespace = '';
101                $extends = $class['extends'];
102                $implements = !empty($class['implements']) ? ''
103                : ' implements ' . implode(', ', $class['implements']);
104            }
105            return <<<EOD
106                <?php
107                $namespace
108                $abstract class $classname extends $extends $implements {}
109                EOD;
110        }
111
112        /**
113         * @param InterfaceAliasArray $interface
114         */
115        private function interfaceTemplate(array $interface): string
116        {
117            $interfacename = $interface['interfacename'];
118            $namespace = isset($interface['namespace'])
119            ? "namespace {$interface['namespace']};" : '';
120            $extends = isset($interface['namespace'])
121            ? '\\' . implode('\\ ,', $interface['extends'])
122            : implode(', ', $interface['extends']);
123            return <<<EOD
124                <?php
125                $namespace
126                interface $interfacename extends $extends {}
127                EOD;
128        }
129
130        /**
131         * @param TraitAliasArray $trait
132         */
133        private function traitTemplate(array $trait): string
134        {
135            $traitname = $trait['traitname'];
136            $namespace = isset($trait['namespace'])
137            ? "namespace {$trait['namespace']};" : '';
138            $uses = isset($trait['namespace'])
139            ? '\\' . implode(';' . PHP_EOL . '    use \\', $trait['use'])
140            : implode(';' . PHP_EOL . '    use ', $trait['use']);
141            return <<<EOD
142                <?php
143                $namespace
144                trait $traitname { 
145                    use $uses
146                }
147                EOD;
148        }
149    }
150
151    spl_autoload_register([ new AliasAutoloader(), 'autoload' ]);
152}