Skip to content

Commit 46bf8d4

Browse files
committed
First release
0 parents  commit 46bf8d4

File tree

5 files changed

+197
-0
lines changed

5 files changed

+197
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.php_cs.cache
2+
composer.lock
3+
.phpunit.result.cache
4+
/phpunit.xml
5+
vendor/*

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2022 EasyCorp
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
EasyAdmin No Final Composer Plugin
2+
==================================
3+
4+
EasyAdmin is [designed to not allow class inheritance][1] and that's why all
5+
its classes use the `final` PHP keyword. This is troublesome for those who want
6+
to extend EasyAdmin classes to add new features.
7+
8+
This project is a Composer plugin that removes the `final` PHP keyword from all
9+
EasyAdmin classes, so you can extend all of them. It only modifies EasyAdmin
10+
files located in the `vendor/` directory of your project. It doesn't change any
11+
other file in your application.
12+
13+
Run the following command to install this Composer plugin in your projects:
14+
15+
```
16+
$ composer require easycorp/easyadmin-no-final-plugin
17+
```
18+
19+
When does this plugin update EasyAdmin classes?
20+
21+
* Just after installing this Composer plugin;
22+
* Just after installing or updating any EasyAdmin version.
23+
24+
[1]: https://easycorp.github.io/blog/posts/the-road-to-easyadmin-3-no-more-inheritance

composer.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "easycorp/easyadmin-no-final-plugin",
3+
"type": "composer-plugin",
4+
"description": "It removes the 'final' PHP keyword from all EasyAdmin classes so you can extend those classes in your projects.",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Project Contributors",
9+
"homepage": "https://github.com/EasyCorp/EasyAdminBundle/graphs/contributors"
10+
}
11+
],
12+
"require": {
13+
"php": ">=8.0.2",
14+
"composer-plugin-api": "^2.0"
15+
},
16+
"require-dev": {
17+
"composer/composer": "^2.0"
18+
},
19+
"autoload": {
20+
"psr-4": {
21+
"EasyCorp\\Bundle\\EasyAdminBundle\\": "src"
22+
}
23+
},
24+
"config": {
25+
"platform": { "php": "8.0.2" },
26+
"preferred-install": { "*": "dist" },
27+
"sort-packages": true
28+
},
29+
"extra": {
30+
"class": "EasyCorp\\Bundle\\EasyAdminBundle\\NoFinalClassPlugin"
31+
}
32+
}
33+

src/NoFinalClassPlugin.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle;
4+
5+
use Composer\Composer;
6+
use Composer\DependencyResolver\Operation\InstallOperation;
7+
use Composer\DependencyResolver\Operation\UpdateOperation;
8+
use Composer\EventDispatcher\EventSubscriberInterface;
9+
use Composer\Factory;
10+
use Composer\Installer\PackageEvent;
11+
use Composer\Installer\PackageEvents;
12+
use Composer\IO\IOInterface;
13+
use Composer\Package\PackageInterface;
14+
use Composer\Plugin\PluginInterface;
15+
16+
final class NoFinalClassPlugin implements PluginInterface, EventSubscriberInterface
17+
{
18+
private IOInterface $io;
19+
20+
public function activate(Composer $composer, IOInterface $io)
21+
{
22+
$this->io = $io;
23+
}
24+
25+
public static function getSubscribedEvents()
26+
{
27+
return [
28+
PackageEvents::POST_PACKAGE_INSTALL => 'onPackageInstall',
29+
PackageEvents::POST_PACKAGE_UPDATE => 'onPackageUpdate',
30+
];
31+
}
32+
33+
public function deactivate(Composer $composer, IOInterface $io)
34+
{
35+
}
36+
37+
public function uninstall(Composer $composer, IOInterface $io)
38+
{
39+
}
40+
41+
public function onPackageInstall(PackageEvent $event)
42+
{
43+
if (!$this->isComposerWorkingOn('easycorp/easyadmin-bundle', $event) && !$this->isComposerWorkingOn('easycorp/easyadmin-no-final-plugin', $event)) {
44+
return;
45+
}
46+
47+
$this->removeFinalFromAllEasyAdminClasses();
48+
}
49+
50+
public function onPackageUpdate(PackageEvent $event)
51+
{
52+
if (!$this->isComposerWorkingOn('easycorp/easyadmin-bundle', $event)) {
53+
return;
54+
}
55+
56+
$this->removeFinalFromAllEasyAdminClasses();
57+
}
58+
59+
public function removeFinalFromAllEasyAdminClasses()
60+
{
61+
$vendorDirPath = $this->getVendorDirPath();
62+
$easyAdminDirPath = $vendorDirPath.'/easycorp/easyadmin-bundle';
63+
foreach ($this->getFilePathsOfAllEasyAdminClasses($easyAdminDirPath) as $filePath) {
64+
file_put_contents(
65+
$filePath,
66+
str_replace('final class ', 'class ', file_get_contents($filePath)),
67+
flags: \LOCK_EX
68+
);
69+
}
70+
71+
$this->io->write('Updated all EasyAdmin PHP files to make classes non-final');
72+
}
73+
74+
private function isComposerWorkingOn(string $packageName, PackageEvent $event): bool
75+
{
76+
/** @var PackageInterface|null $package */
77+
$package = null;
78+
79+
foreach ($event->getOperations() as $operation) {
80+
if ('install' === $operation->getOperationType()) {
81+
/** @var InstallOperation $operation */
82+
$package = $operation->getPackage();
83+
} elseif ('update' === $operation->getOperationType()) {
84+
/** @var UpdateOperation $operation */
85+
$package = $operation->getInitialPackage();
86+
}
87+
}
88+
89+
return $packageName === $package?->getName();
90+
}
91+
92+
private function getVendorDirPath(): string
93+
{
94+
$composerJsonFilePath = Factory::getComposerFile();
95+
$composerJsonContents = json_decode(file_get_contents($composerJsonFilePath), associative: true, flags: JSON_THROW_ON_ERROR);
96+
$projectDir = dirname(realpath($composerJsonFilePath));
97+
98+
return $composerJsonContents['config']['vendor-dir'] ?? $projectDir.'/vendor';
99+
}
100+
101+
/**
102+
* @return iterable Returns the file paths of all PHP files that contain EasyAdmin classes
103+
*/
104+
private function getFilePathsOfAllEasyAdminClasses(string $easyAdminDirPath): iterable
105+
{
106+
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($easyAdminDirPath, \FilesystemIterator::SKIP_DOTS)) as $filePath) {
107+
if (is_dir($filePath) || !str_ends_with($filePath, '.php')) {
108+
continue;
109+
}
110+
111+
yield $filePath;
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)