Skip to content

Commit 3086d08

Browse files
author
trick
committed
Overhauled the code again;
- added a code loader for Facades to accurately determine their names - separated the registration of service providers and facades - default option when multiple files are found is now "None" - added --dry-run option, to simulate what will be done when the command is run - started writing unit tests (WIP)
1 parent 87cb9e2 commit 3086d08

File tree

8 files changed

+287
-30
lines changed

8 files changed

+287
-30
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
php_errors.log
22
Standard/
3-
3+
vendor/
4+
composer.lock
45

56
# Created by https://www.gitignore.io/api/laravel,eclipse,linux
67

phpunit.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit backupGlobals="false"
3+
backupStaticAttributes="false"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
processIsolation="false"
10+
stopOnFailure="false">
11+
<testsuites>
12+
<testsuite name="Unit Tests">
13+
<directory suffix="Test.php">./tests/Unit</directory>
14+
</testsuite>
15+
</testsuites>
16+
<filter>
17+
<whitelist processUncoveredFilesFromWhitelist="true">
18+
<directory suffix=".php">./src</directory>
19+
</whitelist>
20+
</filter>
21+
<php>
22+
<env name="APP_ENV" value="testing"/>
23+
<env name="CACHE_DRIVER" value="array"/>
24+
<env name="SESSION_DRIVER" value="array"/>
25+
<env name="QUEUE_DRIVER" value="sync"/>
26+
</php>
27+
</phpunit>

src/Console/Commands/RequireCommand.php

Lines changed: 97 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use LaravelRequire\Support\Rules\MatchFilenameRuleContract;
1818
use LaravelRequire\Support\RegisteredItemInformation;
1919
use LaravelRequire\Support\ClassInformationParser;
20+
use LaravelRequire\Support\FacadeClassLoader;
2021

2122

2223
class RequireCommand extends Command
@@ -26,7 +27,11 @@ class RequireCommand extends Command
2627
*
2728
* @var string
2829
*/
29-
protected $signature = 'require:package {package} {--register-only}';
30+
protected $signature = 'require:package {package} '.
31+
'{--d | --dry-run : Simulate installation and registration of a package } '.
32+
'{--r | --register-only : register package only, don\'t run `composer require`} '.
33+
'{--c | --no-class-loader : don\'t use the smart facade class loader}'.
34+
'';
3035

3136
/**
3237
* The console command description.
@@ -54,6 +59,7 @@ public function handle()
5459
{
5560
$packageName = $this->argument('package');
5661
$registerOnly = $this->option('register-only');
62+
$dryRun = $this->option('dry-run');
5763

5864
try {
5965
$this->validatePackageName($packageName);
@@ -62,7 +68,11 @@ public function handle()
6268
return;
6369
}
6470

65-
if (!$registerOnly) {
71+
if ($dryRun) {
72+
$this->info('[dry-run] run composer require '.$packageName);
73+
}
74+
75+
if (!$registerOnly && !$dryRun) {
6676
$composerRequireCommand = $this->findComposerBinary() . " require $packageName";
6777
$process = new Process($composerRequireCommand, base_path(), null, null, null);
6878

@@ -77,54 +87,93 @@ public function handle()
7787
});
7888
}
7989

80-
$splist = (array)$this->findPackageFilesToRegister($packageName);
81-
$providers = [];
90+
$splist = (array)$this->findPackageFilesToRegister($packageName);
91+
$providers = ['None'];
92+
$facades = ['None'];
93+
$items = [];
94+
95+
foreach($splist as $info) {
96+
switch ($info->type) {
97+
case RegisteredItemInformation::SERVICE_PROVIDER_TYPE:
98+
$providers[] = $info;
99+
break;
100+
case RegisteredItemInformation::FACADE_TYPE:
101+
$facades[] = $info;
102+
break;
103+
default:
104+
//nothing to do
105+
}
106+
}
82107

83108
//found multiple service providers
84-
if (count($splist) > 1) {
85-
109+
if (count($providers) == 1) {
110+
$items[] = $providers[0];
111+
} else {
86112
$selected = $this->choice(
87-
"Multiple Service Provider and/or Facade classes were located.\n".
113+
" Service Provider classes were located.\n".
88114
" Please enter a comma-separated list of item numbers to register. Default:",
89-
$splist, 0, null, true
115+
$providers, 0, null, (count($providers) >= 1)
90116
);
91117

92118
foreach($selected as $class) {
93-
foreach($splist as $info) {
94-
if ($info->qualifiedName() == $class) {
95-
$providers[] = $info;
119+
if (strtolower($class) == 'none')
120+
break;
121+
foreach($providers as $provider) {
122+
if ($provider->qualifiedName() == $class) {
123+
$items[] = $provider;
96124
}
97125
}
98126
}
127+
}
99128

129+
if (count($facades) == 1) {
130+
$items[] = $facades[0];
100131
} else {
101-
$providers = $splist; //Didn't find more than one service provider/facade
132+
$selected = $this->choice(
133+
" Facade classes were located.\n".
134+
" Please enter a comma-separated list of item numbers to register. Default:",
135+
$facades, 0, null, (count($facades) > 1)
136+
);
137+
foreach($selected as $class) {
138+
if (strtolower($class) == 'none')
139+
break;
140+
foreach($facades as $facade) {
141+
if ($facade->qualifiedName() == $class) {
142+
$items[] = $facade;
143+
}
144+
}
145+
}
102146
}
103147

104-
foreach($providers as $provider) {
148+
foreach($items as $item) {
105149

106-
if (!$provider->filename) {
150+
if (!$item->filename) {
107151
$this->comment(
108-
"The service provider file for ".$provider->qualifiedName()." could not be found. ".
152+
"The service provider file for ".$item->qualifiedName()." could not be found. ".
109153
"This package must be registered manually."
110154
);
111155
continue;
112156
}
113157

114158
try {
115-
$this->info("Registering ".$provider->displayName().': '.$provider->qualifiedName()."...");
116-
117-
if ($this->registerPackageItem($provider)) {
118-
$this->info('...registered successfully.');
119-
} else {
120-
$this->comment('The package and/or service provider did not register or install correctly.');
159+
$this->info("Registering ".$item->displayName().': '.$item->qualifiedName()."...");
160+
161+
if ($dryRun) {
162+
$this->info('[dry-run] registerPackageItem');
163+
} elseif (!$dryRun) {
164+
if ($this->registerPackageItem($item)) {
165+
$this->info('...registered successfully.');
166+
} else {
167+
$this->comment('The package and/or service provider did not register or install correctly.');
168+
}
121169
}
122170
} catch (ServiceProvidersVariableMissingException $e) {
123171
$this->comment($e->getMessage());
124172
} catch (ServiceProviderAlreadyRegisteredException $e) {
125173
$this->comment($e->getMessage());
126174
}
127175
} //end foreach(providers)
176+
$this->info('Finished.');
128177
}
129178

130179
/**
@@ -155,7 +204,7 @@ protected function findPackageFilesToRegister($packageName)
155204

156205
foreach ($files as $file) {
157206
$spInfo = $scanner->scanFile($file->getPathname(), new ServiceProviderRule);
158-
$fInfo = $scanner->scanFile($file->getPathname(), new FacadeRule);
207+
$fInfo = $scanner->scanFile($file->getPathname(), new FacadeRule(new FacadeClassLoader));
159208
if ($fInfo !== false)
160209
$result[] = $fInfo->type(RegisteredItemInformation::FACADE_TYPE);
161210
if ($spInfo !== false)
@@ -213,6 +262,7 @@ protected function registerPackageItem(RegisteredItemInformation $item)
213262
}
214263
}
215264

265+
216266
if ($item->type == RegisteredItemInformation::FACADE_TYPE) {
217267
$searchLine = "'aliases' => [";
218268
//some Facades provided by packages are named 'Facade.php', so we will try
@@ -240,4 +290,29 @@ protected function registerPackageItem(RegisteredItemInformation $item)
240290

241291
return false;
242292
}
293+
294+
/**
295+
* Get the console command arguments.
296+
*
297+
* @return array
298+
*/
299+
protected function getArguments()
300+
{
301+
return [
302+
['package', InputArgument::REQUIRED, 'The name the package to install and register.'],
303+
];
304+
}
305+
306+
/**
307+
* Get the console command options.
308+
*
309+
* @return array
310+
*/
311+
protected function getOptions()
312+
{
313+
return [
314+
['register-only', 'r', InputOption::VALUE_NONE, 'The terminal command that should be assigned.', 'command:name'],
315+
];
316+
}
317+
243318
}

src/Support/FacadeClassLoader.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
namespace LaravelRequire\Support;
4+
5+
class FacadeClassLoader
6+
{
7+
protected $tempFiles = [];
8+
9+
public function __construct()
10+
{
11+
//
12+
}
13+
14+
public function load($filename, $isContent = false)
15+
{
16+
if ($isContent) {
17+
$contents = $filename;
18+
} else {
19+
if (!file_exists($filename)) {
20+
return false;
21+
}
22+
$contents = file_get_contents($filename);
23+
}
24+
25+
26+
$loaderClassname = $this->getLoaderClassname();
27+
$loaderfn = $this->getLoaderFilename($loaderClassname);
28+
$contents = $this->processFacadeFileContents($loaderClassname, $contents);
29+
30+
$this->writeDataToTempFile($loaderfn, $contents);
31+
32+
try {
33+
include_once($loaderfn);
34+
$facadeName = $loaderClassname::getFacadeName();
35+
} catch(\Exception $e) {
36+
return false;
37+
} finally {
38+
$this->cleanup();
39+
}
40+
41+
return ucfirst($facadeName);
42+
}
43+
44+
protected function writeDataToTempFile($filename, $data)
45+
{
46+
if (!in_array($filename, $this->tempFiles))
47+
$this->tempFiles[] = $filename;
48+
file_put_contents($filename, $data);
49+
50+
return $this;
51+
}
52+
53+
protected function processFacadeFileContents($loaderClassname, $contents)
54+
{
55+
$temp = $contents;
56+
$temp = str_replace('namespace ', '//namespace ', $temp);
57+
$temp = preg_replace('/use /', '//use ', $temp);
58+
$temp = preg_replace('/class\s+([a-zA-Z0-9_]+)\s+extends\s+[\\\A-Za-z0-9_]*Facade/', "class $loaderClassname ", $temp);
59+
$temp = preg_replace('/(protected|private)/', 'public', $temp);
60+
$temp = preg_replace('/getFacadeAccessor/', 'getFacadeName', $temp);
61+
//$temp = preg_replace('/protected\s+static\s+function\s+getFacadeAccessor\b/', 'public static function getFacadeName', $temp);
62+
63+
return $temp;
64+
}
65+
66+
protected function getLoaderClassname($refresh = false)
67+
{
68+
static $classname = '';
69+
if ($classname == '' || $refresh === true)
70+
$classname = (new \ReflectionClass($this))->getShortName().sha1(mt_rand(1, 999999999));
71+
72+
return $classname;
73+
}
74+
75+
protected function getLoaderFilename($classname)
76+
{
77+
return "${classname}.laravel-require.facade-loader.php";
78+
}
79+
80+
protected function cleanup()
81+
{
82+
foreach($this->tempFiles as $file) {
83+
if (file_exists($file))
84+
unlink($file);
85+
}
86+
$this->tempFiles = [];
87+
return $this;
88+
}
89+
90+
}

src/Support/PackageFileScanner.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,24 @@ public function scanFile($filename, $rule)
2525
$namespace = $parser->getNamespaceFromSource($contents);
2626
$info->filename($filename)
2727
->classname($classname)
28-
->namespace($namespace);
28+
->namespace($namespace)
29+
->name($classname);
30+
2931
return $info;
3032
}
3133

3234
$codeMatch = $rule->sourceCodeMatch($contents);
3335
if ($codeMatch!==false && !$info) {
36+
$codeClassname = $codeMatch['class'];
37+
$codeExtends = $codeMatch['extends'];
38+
$codeName = $codeMatch['name'];
39+
$codeType = $codeMatch['type'];
3440

3541
$info = new RegisteredItemInformation();
36-
$info->classname($codeMatch[1])
42+
$info->classname($codeClassname)
3743
->filename($filename)
38-
->extends($codeMatch[2]);
44+
->extends($codeExtends)
45+
->name($codeName);
3946

4047
$namespace = $parser->getNamespaceFromSource($contents);
4148
$info->namespace($namespace);

0 commit comments

Comments
 (0)