mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-28 23:10:51 +00:00
Updated Rector to commit 25821f6212
25821f6212
[TASK] Add documentation about FileProcessors (#121)
This commit is contained in:
parent
ab3b6b2c9e
commit
d39c210590
|
@ -39,6 +39,7 @@ By [buying a book](https://leanpub.com/rector-the-power-of-automated-refactoring
|
|||
- [How to Ignore Rule or Paths](/docs/how_to_ignore_rule_or_paths.md)
|
||||
- [Static Reflection and Autoload](/docs/static_reflection_and_autoload.md)
|
||||
- [How to Configure Rule](/docs/how_to_configure_rules.md)
|
||||
- [Beyond PHP - Entering the realm of FileProcessors](/docs/beyond_php_file_processors.md)
|
||||
|
||||
### For Rule Developers and Contributors
|
||||
|
||||
|
@ -47,7 +48,7 @@ By [buying a book](https://leanpub.com/rector-the-power-of-automated-refactoring
|
|||
- [How to Work with Doc Block and Comments](/docs/how_to_work_with_doc_block_and_comments.md)
|
||||
- [How to Generate New Rector Rule](/docs/create_own_rule.md)
|
||||
- [How to add Test for Rector Rule](/docs/how_to_add_test_for_rector_rule.md)
|
||||
|
||||
- [How to create a custom FileProcessor](/docs/create_custom_fileprocessor.md)
|
||||
<br>
|
||||
|
||||
## Install
|
||||
|
|
101
docs/beyond_php_file_processors.md
Normal file
101
docs/beyond_php_file_processors.md
Normal file
|
@ -0,0 +1,101 @@
|
|||
# Beyond PHP - FileProcessors
|
||||
|
||||
You think Rector is all about PHP? You might be wrong.
|
||||
Sure, the vast majority of the rules included in Rector is for PHP-Code. That´s true.
|
||||
|
||||
But since version 0.11.x Rector introduced the concept of so called FileProcessors.
|
||||
When you are running Rector with the process command all collected files from your configured paths
|
||||
are iterated through all registered FileProcessors.
|
||||
|
||||
Each FileProcessor must implement the [FileProcessorInterface](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Processor/FileProcessorInterface.php) and must decide if it is able to handle a given file by
|
||||
the [supports](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Processor/FileProcessorInterface.php#L11) method or not.
|
||||
|
||||
Rector itself already ships with three FileProcessors. Whereas the most important one, you guessed it, is the PhpFileProcessor.
|
||||
|
||||
But another nice one is the ComposerFileProcessor. The ComposerFileProcessor lets you manipulate composer.json files in your project.
|
||||
Let´s say you want to define a custom configuration where you want to update the version constraint of some packages.
|
||||
All you have to do is using the ChangePackageVersionComposerRector:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// rector.php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Composer\Rector\ChangePackageVersionComposerRector;
|
||||
use Rector\Composer\ValueObject\PackageAndVersion;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(ChangePackageVersionComposerRector::class)
|
||||
->call('configure', [[
|
||||
// we use constant for keys to save you from typos
|
||||
ChangePackageVersionComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
|
||||
new PackageAndVersion('symfony/yaml', '^5.0'),
|
||||
]),
|
||||
]]);
|
||||
};
|
||||
```
|
||||
|
||||
There are some more rules related to manipulate your composer.json files. Let´s see them in action:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// rector.php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Composer\Rector\AddPackageToRequireComposerRector;
|
||||
use Rector\Composer\Rector\AddPackageToRequireDevComposerRector;
|
||||
use Rector\Composer\Rector\RemovePackageComposerRector;
|
||||
use Rector\Composer\Rector\ReplacePackageAndVersionComposerRector;
|
||||
use Rector\Composer\ValueObject\PackageAndVersion;
|
||||
use Rector\Composer\ValueObject\ReplacePackageAndVersion;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
// Add a package to the require section of your composer.json
|
||||
$services->set(AddPackageToRequireComposerRector::class)
|
||||
->call('configure', [[
|
||||
// we use constant for keys to save you from typos
|
||||
AddPackageToRequireComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
|
||||
new PackageAndVersion('symfony/yaml', '^5.0'),
|
||||
]),
|
||||
]]);
|
||||
|
||||
// Add a package to the require dev section of your composer.json
|
||||
$services->set(AddPackageToRequireDevComposerRector::class)
|
||||
->call('configure', [[
|
||||
// we use constant for keys to save you from typos
|
||||
AddPackageToRequireDevComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
|
||||
new PackageAndVersion('phpunit/phpunit', '^9.0'),
|
||||
]),
|
||||
]]);
|
||||
|
||||
// Remove a package from composer.json
|
||||
$services->set(RemovePackageComposerRector::class)
|
||||
->call('configure', [[
|
||||
// we use constant for keys to save you from typos
|
||||
RemovePackageComposerRector::PACKAGE_NAMES => ['symfony/console']
|
||||
]]);
|
||||
|
||||
// Replace a package in the composer.json
|
||||
$services->set(ReplacePackageAndVersionComposerRector::class)
|
||||
->call('configure', [[
|
||||
// we use constant for keys to save you from typos
|
||||
ReplacePackageAndVersionComposerRector::REPLACE_PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
|
||||
new ReplacePackageAndVersion('vendor1/package2', 'vendor2/package1', '^3.0'),
|
||||
]),
|
||||
]]);
|
||||
};
|
||||
```
|
||||
|
||||
Behind every FileProcessor are one or multiple rules which are in turn implementing a dedicated Interface extending the [RectorInterface](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Rector/RectorInterface.php).
|
||||
In the case of the ComposerFileProcessor all rules are implementing the [ComposerRectorInterface](https://github.com/rectorphp/rector-src/blob/main/rules/Composer/Contract/Rector/ComposerRectorInterface.php)
|
||||
|
||||
Are you eager to create your own one? Dive in and have a look at [How to create a custom FileProcessor](how_to_create_custom_fileprocessor.md)
|
||||
|
||||
|
206
docs/how_to_create_custom_fileprocessor.md
Normal file
206
docs/how_to_create_custom_fileprocessor.md
Normal file
|
@ -0,0 +1,206 @@
|
|||
# Create your own custom FileProcessor
|
||||
|
||||
This section is all about creating your custom specific FileProcessor.
|
||||
If you don´t know the concept of FileProcessors in the context of Rector, have a look at [Beyond PHP - Entering the realm of FileProcessors](beyond_php_file_processors.md)
|
||||
|
||||
Most of the examples starting with a rather contrived example, let´s do it the same.
|
||||
|
||||
Imagine you would like to replace the sentence "Make america great again" to "Make the whole world a better place to be" in every file named bold_statement.txt.
|
||||
|
||||
In order to do so, we create the BoldStatementFileProcessor like that:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace MyVendor\MyPackage\FileProcessor;
|
||||
|
||||
use Rector\Core\Contract\Processor\FileProcessorInterface;
|
||||
use Rector\Core\ValueObject\Application\File;
|
||||
|
||||
final class BoldStatementFileProcessor implements FileProcessorInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const OLD_STATEMENT = 'Make america great again';
|
||||
|
||||
public function supports(File $file): bool
|
||||
{
|
||||
$smartFileInfo = $file->getSmartFileInfo();
|
||||
return 'bold_statement.txt' === $smartFileInfo->getBasename();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File[] $files
|
||||
*/
|
||||
public function process(array $files): void
|
||||
{
|
||||
foreach ($files as $file) {
|
||||
$this->processFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
private function processFile(File $file): void
|
||||
{
|
||||
$oldContent = $file->getFileContent();
|
||||
|
||||
if(false === strpos($oldContent, self::OLD_STATEMENT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newFileContent = str_replace(self::OLD_STATEMENT, 'Make the whole world a better place to be', $oldContent);
|
||||
$file->changeFileContent($newFileContent);
|
||||
}
|
||||
|
||||
public function getSupportedFileExtensions(): array
|
||||
{
|
||||
return ['txt'];
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Now register your FileProcessor in your configuration (actually in the container):
|
||||
|
||||
```php
|
||||
<?php
|
||||
// rector.php
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use MyVendor\MyPackage\FileProcessor\BoldStatementFileProcessor;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
// [...]
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(BoldStatementFileProcessor::class);
|
||||
};
|
||||
```
|
||||
|
||||
Run rector again and see what happens. Yes, we made the world better.
|
||||
|
||||
The astute reader has noticed, that the BoldStatementFileProcessor is not really reusable and easily extendable.
|
||||
So it would be much better to separate the processing from the actual rule(s).
|
||||
This is also the best practice in all Rector internal FileProcessors. So, let´s just do that.
|
||||
|
||||
Create a new dedicated Interface for our rules used by the BoldStatementFileProcessor. Just call it BoldStatementRectorInterface.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace MyVendor\MyPackage\FileProcessor\Rector;
|
||||
|
||||
use Rector\Core\Contract\Rector\RectorInterface;
|
||||
|
||||
interface BoldStatementRectorInterface extends RectorInterface
|
||||
{
|
||||
public function transform(string $content): string;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Now, separate the modification from the processing:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace MyVendor\MyPackage\FileProcessor\Rector;
|
||||
use Rector\Core\ValueObject\Application\File;
|
||||
|
||||
final class BoldStatementMakeAmericaGreatAgainRector implements BoldStatementRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const OLD_STATEMENT = 'Make america great again';
|
||||
|
||||
public function transform(string $content): string
|
||||
{
|
||||
if(false === strpos($content, self::OLD_STATEMENT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return str_replace(self::OLD_STATEMENT, 'Make the whole world a better place to be', $content);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
And change our BoldStatementFileProcessor so it is using one or multiple classes implementing the BoldStatementRectorInterface:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace MyVendor\MyPackage\FileProcessor;
|
||||
|
||||
use Rector\Core\Contract\Processor\FileProcessorInterface;
|
||||
use Rector\Core\ValueObject\Application\File;
|
||||
use MyVendor\MyPackage\FileProcessor\Rector\BoldStatementRectorInterface;
|
||||
|
||||
final class BoldStatementFileProcessor implements FileProcessorInterface
|
||||
{
|
||||
/**
|
||||
* @var BoldStatementRectorInterface[]
|
||||
*/
|
||||
private $boldStatementRectors;
|
||||
|
||||
/**
|
||||
* @param BoldStatementRectorInterface[] $boldStatementRectors
|
||||
*/
|
||||
public function __construct(array $boldStatementRectors)
|
||||
{
|
||||
$this->boldStatementRectors = $boldStatementRectors;
|
||||
}
|
||||
|
||||
public function supports(File $file): bool
|
||||
{
|
||||
$smartFileInfo = $file->getSmartFileInfo();
|
||||
return 'bold_statement.txt' === $smartFileInfo->getBasename();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File[] $files
|
||||
*/
|
||||
public function process(array $files): void
|
||||
{
|
||||
foreach ($files as $file) {
|
||||
$this->processFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
private function processFile(File $file): void
|
||||
{
|
||||
foreach ($this->boldStatementRectors as $boldStatementRector) {
|
||||
$changeFileContent = $boldStatementRector->transform($file->getFileContent());
|
||||
$file->changeFileContent($changeFileContent);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSupportedFileExtensions(): array
|
||||
{
|
||||
return ['txt'];
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Notice the annotation BoldStatementRectorInterface[]. This is important to inject all active classes implementing the BoldStatementRectorInterface into the BoldStatementFileProcessor.
|
||||
Yes, we said active. So last but not least we must register our new rule in the container, so it is applied:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// rector.php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use MyVendor\MyPackage\FileProcessor\BoldStatementFileProcessor;
|
||||
use MyVendor\MyPackage\FileProcessor\Rector\BoldStatementMakeAmericaGreatAgainRector;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
// [...]
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(BoldStatementFileProcessor::class);
|
||||
$services->set(BoldStatementMakeAmericaGreatAgainRector::class);
|
||||
};
|
||||
```
|
||||
|
||||
Run rector again and yes, we made the world a better place again.
|
||||
|
||||
Puh. This was a long ride. But we are done and have our new shiny BoldStatementFileProcessor in place.
|
||||
Now, it´s up to you, to create something useful. But always keep in mind: Try to make the world a better place to be.
|
|
@ -16,11 +16,11 @@ final class VersionResolver
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const PACKAGE_VERSION = '651562dca23b2637184260a754f4dcdd7c41c520';
|
||||
public const PACKAGE_VERSION = '25821f62122d0b1f42080389045a542dfe2343d3';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const RELEASE_DATE = '2021-06-13 11:47:20';
|
||||
public const RELEASE_DATE = '2021-06-13 13:19:16';
|
||||
public static function resolvePackageVersion() : string
|
||||
{
|
||||
$process = new \RectorPrefix20210613\Symfony\Component\Process\Process(['git', 'log', '--pretty="%H"', '-n1', 'HEAD'], __DIR__);
|
||||
|
|
2
vendor/autoload.php
vendored
2
vendor/autoload.php
vendored
|
@ -4,4 +4,4 @@
|
|||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969::getLoader();
|
||||
return ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc::getLoader();
|
||||
|
|
14
vendor/composer/autoload_real.php
vendored
14
vendor/composer/autoload_real.php
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969
|
||||
class ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
|
@ -22,15 +22,15 @@ class ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969
|
|||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::getInitializer($loader));
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::getInitializer($loader));
|
||||
} else {
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
|
@ -42,19 +42,19 @@ class ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969
|
|||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$files;
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequirea07e25d44a859f4e8c5e186c56ba3969($fileIdentifier, $file);
|
||||
composerRequireebaae27a38f113f52c90b5eba352fcbc($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequirea07e25d44a859f4e8c5e186c56ba3969($fileIdentifier, $file)
|
||||
function composerRequireebaae27a38f113f52c90b5eba352fcbc($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
|
8
vendor/composer/autoload_static.php
vendored
8
vendor/composer/autoload_static.php
vendored
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969
|
||||
class ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc
|
||||
{
|
||||
public static $files = array (
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
|
@ -3863,9 +3863,9 @@ class ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969
|
|||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$classMap;
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
|
|
10
vendor/scoper-autoload.php
vendored
10
vendor/scoper-autoload.php
vendored
|
@ -21,8 +21,8 @@ if (!class_exists('SomeTestCase', false) && !interface_exists('SomeTestCase', fa
|
|||
if (!class_exists('CheckoutEntityFactory', false) && !interface_exists('CheckoutEntityFactory', false) && !trait_exists('CheckoutEntityFactory', false)) {
|
||||
spl_autoload_call('RectorPrefix20210613\CheckoutEntityFactory');
|
||||
}
|
||||
if (!class_exists('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', false) && !interface_exists('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', false) && !trait_exists('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', false)) {
|
||||
spl_autoload_call('RectorPrefix20210613\ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969');
|
||||
if (!class_exists('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', false) && !interface_exists('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', false) && !trait_exists('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', false)) {
|
||||
spl_autoload_call('RectorPrefix20210613\ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc');
|
||||
}
|
||||
if (!class_exists('Doctrine\Inflector\Inflector', false) && !interface_exists('Doctrine\Inflector\Inflector', false) && !trait_exists('Doctrine\Inflector\Inflector', false)) {
|
||||
spl_autoload_call('RectorPrefix20210613\Doctrine\Inflector\Inflector');
|
||||
|
@ -3323,9 +3323,9 @@ if (!function_exists('print_node')) {
|
|||
return \RectorPrefix20210613\print_node(...func_get_args());
|
||||
}
|
||||
}
|
||||
if (!function_exists('composerRequirea07e25d44a859f4e8c5e186c56ba3969')) {
|
||||
function composerRequirea07e25d44a859f4e8c5e186c56ba3969() {
|
||||
return \RectorPrefix20210613\composerRequirea07e25d44a859f4e8c5e186c56ba3969(...func_get_args());
|
||||
if (!function_exists('composerRequireebaae27a38f113f52c90b5eba352fcbc')) {
|
||||
function composerRequireebaae27a38f113f52c90b5eba352fcbc() {
|
||||
return \RectorPrefix20210613\composerRequireebaae27a38f113f52c90b5eba352fcbc(...func_get_args());
|
||||
}
|
||||
}
|
||||
if (!function_exists('parseArgs')) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user