最近在迁徙一个上古我的项目到 laravel 中。我这边的做法是先用 rector 做一个整体初步的语法降级与 laravel 写法的替换,而后次要就是手动重写数据操作的局部。到目前为止除了利用到 rector 自带的规定外,还写了一些自定义的规定,其中有一个规范化命名格调的规定(RenameToPsrNameRector)实用于所有的 PHP 我的项目,所以在此分享进去。该规定次要是针对常量、变量、函数、类、属性、办法等命名进行对立的标准。其中,常量名遵循大写蛇形命名格调,函数名遵循小写蛇形命名格调,类名遵循大驼峰命名格调,变量名、属性名、办法名遵循小驼峰命名格调。成果 <?php // lower snake-function functionName(){}-functionName();-call_user_func('functionName');-call_user_func_array('functionName');-function_exists('functionName');+function function_name(){}+\function_name();+call_user_func('function_name');+call_user_func_array('function_name');+function_exists('function_name'); // ucfirst camel-class class_name{}-enum enum_name{}-enum Enum{case case_name;}-interface interface_name{}-trait trait_name{}-class Foo extends class_name implements interface_name{}-class_name::$property;-class_name::CONST;-class_name::method();-enum Enum implements interface_name{}-use class_name;-use trait_name;-class_alias('class_name', 'alias_class_name');-class_exists('class_name');-class_implements('class_name');-class_parents('class_name');-class_uses('class_name');-enum_exists('enum_name');-get_class_methods('class_name');-get_class_vars('class_name');-get_parent_class('class_name');-interface_exists('interface_name');-is_subclass_of('class_name', 'parent_class_name');-trait_exists('trait_name', true);+class ClassName{}+enum EnumName{}+enum Enum{case CaseName;}+interface InterfaceName{}+trait TraitName{}+class Foo extends \ClassName implements \InterfaceName{}+\ClassName::$property;+\ClassName::CONST;+\ClassName::method();+enum Enum implements \InterfaceName{}+use ClassName;+use TraitName;+class_alias('ClassName', 'AliasClassName');+class_exists('ClassName');+class_implements('ClassName');+class_parents('ClassName');+class_uses('ClassName');+enum_exists('EnumName');+get_class_methods('ClassName');+get_class_vars('ClassName');+get_parent_class('ClassName');+interface_exists('InterfaceName');+is_subclass_of('ClassName', 'ParentClassName');+trait_exists('TraitName', true); // upper snake-class Foo{public const constName = 'const';}-Foo::constName;-define('constName', 'const');-defined('constName');-constant('constName');+class Foo{public const CONST_NAME = 'const';}+Foo::CONST_NAME;+define('CONST_NAME', 'const');+defined('CONST_NAME');+constant('CONST_NAME'); constant('Foo::constName');-constName;+\CONST_NAME; // lcfirst camel-$var_name;-$object->method_name();-$object->property_name;-call_user_method('method_name', $object);-call_user_method_array('method_name', $object);-class Foo{public $property_name;}-class Foo{public function method_name(){}}-class Foo{public int $property_name;}-Foo::$property_name;-Foo::method_name();-method_exists($object, 'method_name');-property_exists($object, 'property_name');+$varName;+$object->methodName();+$object->propertyName;+call_user_method('methodName', $object);+call_user_method_array('methodName', $object);+class Foo{public $propertyName;}+class Foo{public function methodName(){}}+class Foo{public int $propertyName;}+Foo::$propertyName;+Foo::methodName();+method_exists($object, 'methodName');+property_exists($object, 'propertyName');规定(RenameToPsrNameRector)<?phpnamespace App\Support\Rectors;use Illuminate\Support\Str;use PhpParser\Node;use PhpParser\Node\Expr\FuncCall;use PhpParser\Node\Name;use Rector\Core\Contract\Rector\ConfigurableRectorInterface;use Rector\Core\Rector\AbstractRector;use RectorPrefix202305\Webmozart\Assert\Assert;use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;class RenameToPsrNameRector extends AbstractRector implements ConfigurableRectorInterface{ /** * @var array<string> */ protected $except = [ '*::*', 'class', 'false', 'null', 'stdClass', 'true', ]; public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( 'Rename to psr name', [ new CodeSample( <<<'CODE_SAMPLE'// lower snakefunction functionName(){}functionName();call_user_func('functionName');call_user_func_array('functionName');function_exists('functionName');// ucfirst camelclass class_name{}enum enum_name{}enum Enum{case case_name;}interface interface_name{}trait trait_name{}class Foo extends class_name implements interface_name{}class_name::$property;class_name::CONST;class_name::method();enum Enum implements interface_name{}use class_name;use trait_name;class_alias('class_name', 'alias_class_name');class_exists('class_name');class_implements('class_name');class_parents('class_name');class_uses('class_name');enum_exists('enum_name');get_class_methods('class_name');get_class_vars('class_name');get_parent_class('class_name');interface_exists('interface_name');is_subclass_of('class_name', 'parent_class_name');trait_exists('trait_name', true);// upper snakeclass Foo{public const constName = 'const';}Foo::constName;define('constName', 'const');defined('constName');constant('constName');constant('Foo::constName');constName;// lcfirst camel$var_name;$object->method_name();$object->property_name;call_user_method('method_name', $object);call_user_method_array('method_name', $object);class Foo{public $property_name;}class Foo{public function method_name(){}}class Foo{public int $property_name;}Foo::$property_name;Foo::method_name();method_exists($object, 'method_name');property_exists($object, 'property_name');CODE_SAMPLE , <<<'CODE_SAMPLE'// lower snakefunction function_name(){}function_name();call_user_func('function_name');call_user_func_array('function_name');function_exists('function_name');// ucfirst camelclass ClassName{}enum EnumName{}enum Enum{case CaseName;}interface InterfaceName{}trait TraitName{}class Foo extends ClassName implements InterfaceName{}ClassName::$property;ClassName::CONST;ClassName::method();enum Enum implements InterfaceName{}use ClassName;use TraitName;class_alias('ClassName', 'AliasClassName');class_exists('ClassName');class_implements('ClassName');class_parents('ClassName');class_uses('ClassName');enum_exists('EnumName');get_class_methods('ClassName');get_class_vars('ClassName');get_parent_class('ClassName');interface_exists('InterfaceName');is_subclass_of('ClassName', 'ParentClassName');trait_exists('TraitName', true);// upper snakeclass Foo{public const CONST_NAME = 'const';}Foo::CONST_NAME;define('CONST_NAME', 'const');defined('CONST_NAME');constant('CONST_NAME');constant('Foo::CONST_NAME');CONST_NAME;// lcfirst camel$varName$object->methodName();$object->propertyName;class Foo{public $propertyName;}class Foo{public function methodName(){}}class Foo{public int $propertyName;}Foo::$propertyName;Foo::methodName();call_user_method('methodName', $object);call_user_method_array('methodName', $object);method_exists($object, 'methodName');property_exists($object, 'propertyName');CODE_SAMPLE ), ]); } /** * {@inheritDoc} */ public function getNodeTypes(): array { return [ \PhpParser\Node\Name::class, \PhpParser\Node\Expr\FuncCall::class, \PhpParser\Node\Expr\Variable::class, \PhpParser\Node\Identifier::class, ]; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ public function refactor(Node $node) { try { if ($this->shouldLowerSnakeName($node)) { return $this->rename($node, static fn (string $name): string => Str::lower(Str::snake($name))); } if ($this->shouldUcfirstCamelName($node)) { return $this->rename($node, static fn (string $name): string => Str::ucfirst(Str::camel($name))); } if ($this->shouldUpperSnakeName($node)) { return $this->rename($node, static fn (string $name): string => Str::upper(Str::snake($name))); } if ($this->shouldLcfirstCamelName($node)) { return $this->rename($node, static fn (string $name): string => Str::lcfirst(Str::camel($name))); } } catch (\RuntimeException $e) { // skip } return null; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function rename(Node $node, callable $renamer): Node { $preprocessor = function (string $value): string { if ($this->is($this->except, $value)) { throw new \RuntimeException("The name[$value] is skipped."); } if (ctype_upper(preg_replace('/[^a-zA-Z]/', '', $value))) { return mb_strtolower($value, 'UTF-8'); } return $value; }; if ($node instanceof Name) { $node->parts[count($node->parts) - 1] = $renamer($preprocessor($node->parts[count($node->parts) - 1])); return $node; } if ( $this->isSubclasses($node, [ Node\Expr\Variable::class, Node\Identifier::class, ]) ) { $node->name = $renamer($preprocessor($node->name)); return $node; } if ($node instanceof FuncCall) { if ( $this->isNames($node, [ 'call_user_func', 'call_user_func_array', 'call_user_method', 'call_user_method_array', 'class_alias', 'class_exists', 'class_implements', 'class_parents', 'class_uses', 'constant', 'define', 'defined', 'enum_exists', 'function_exists', 'get_class_methods', 'get_class_vars', 'get_parent_class', 'interface_exists', 'is_subclass_of', 'trait_exists', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { $node->args[0]->value->value = $renamer($preprocessor($node->args[0]->value->value)); } if ( $this->isNames($node, [ 'class_alias', 'is_subclass_of', 'method_exists', 'property_exists', ]) && $this->hasFuncCallIndexStringArg($node, 1) ) { $node->args[1]->value->value = $renamer($preprocessor($node->args[1]->value->value)); } } return $node; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldLowerSnakeName(Node $node): bool { $parent = $node->getAttribute('parent'); // function function_name(){} if ($node instanceof Node\Identifier && $parent instanceof Node\Stmt\Function_) { return true; } // function_name(); if ($node instanceof Node\Name && $parent instanceof FuncCall) { return true; } if ( $node instanceof FuncCall && $this->isNames($node, [ // function_exists('function_name'); 'function_exists', // call_user_func('function_name'); 'call_user_func', // call_user_func_array('function_name'); 'call_user_func_array', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } return false; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldUcfirstCamelName(Node $node): bool { $parent = $node->getAttribute('parent'); if ( $node instanceof Node\Identifier && $this->isSubclasses($parent, [ // interface InterfaceName{} Node\Stmt\Interface_::class, // class ClassName{} Node\Stmt\Class_::class, // trait TraitName{} Node\Stmt\Trait_::class, // enum EnumName{} Node\Stmt\Enum_::class, // enum Enum{case CaseName;} Node\Stmt\EnumCase::class, ]) ) { return true; } if ( $node instanceof Node\Name && ! $this->isName($node, 'stdClass') && $this->isSubclasses($parent, [ // ClassName::CONST; Node\Expr\ClassConstFetch::class, // ClassName::$property; Node\Expr\StaticPropertyFetch::class, // ClassName::method(); Node\Expr\StaticCall::class, // class Foo extends ClassName implements InterfaceName{} Node\Stmt\Class_::class, // enum Enum implements InterfaceName{} Node\Stmt\Enum_::class, // use ClassName; Node\Stmt\UseUse::class, // use TraitName; Node\Stmt\TraitUse::class, ]) ) { return true; } if ($node instanceof FuncCall) { if ( $this->isNames($node, [ // class_alias('ClassName', 'AliasClassName'); 'class_alias', // class_exists('ClassName'); 'class_exists', // class_implements('ClassName'); 'class_implements', // class_parents('ClassName'); 'class_parents', // class_uses('ClassName'); 'class_uses', // enum_exists('EnumName'); 'enum_exists', // get_class_methods('ClassName'); 'get_class_methods', // get_class_vars('ClassName'); 'get_class_vars', // get_parent_class('ClassName'); 'get_parent_class', // interface_exists('InterfaceName'); 'interface_exists', // is_subclass_of('ClassName', 'ParentClassName'); 'is_subclass_of', // trait_exists('TraitName', true); 'trait_exists', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } if ( $this->isNames($node, [ // class_alias('ClassName', 'AliasClassName'); 'class_alias', // is_subclass_of('ClassName', 'ParentClassName'); 'is_subclass_of', ]) && $this->hasFuncCallIndexStringArg($node, 1) ) { return true; } } return false; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldUpperSnakeName(Node $node): bool { $parent = $node->getAttribute('parent'); if ( $node instanceof Node\Identifier && ! $this->isName($node, 'class') && $this->isSubclasses($parent, [ // class Foo{public const CONST_NAME = 'const';} Node\Const_::class, // Foo::CONST_NAME; Node\Expr\ClassConstFetch::class, ]) ) { return true; } if ( $node instanceof FuncCall && $this->isNames($node, [ // define('CONST_NAME', 'const'); 'define', // defined('CONST_NAME'); 'defined', // constant('Foo::CONST_NAME'); 'constant', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } // CONST_NAME; if ( $node instanceof Name && ! $this->isNames($node, ['null', 'true', 'false']) && $parent instanceof Node\Expr\ConstFetch ) { return true; } return false; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldLcfirstCamelName(Node $node): bool { // $varName; if ($node instanceof Node\Expr\Variable) { return true; } if ( $node instanceof Node\Identifier && $this->isSubclasses($node->getAttribute('parent'), [ // class Foo{public $propertyName;} Node\Stmt\Property::class, // class Foo{public int $propertyName;} Node\Stmt\PropertyProperty::class, // class Foo{public function methodName(){}} Node\Stmt\ClassMethod::class, // $object->propertyName; Node\Expr\PropertyFetch::class, // Foo::$propertyName; Node\Expr\StaticPropertyFetch::class, // $object->methodName(); Node\Expr\MethodCall::class, // Foo::methodName(); Node\Expr\StaticCall::class, ]) ) { return true; } if ($node instanceof FuncCall) { if ( $this->isNames($node, [ // call_user_method('methodName', $object); 'call_user_method', // call_user_method_array('methodName', $object); 'call_user_method_array', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } if ( $this->isNames($node, [ // method_exists($object, 'methodName'); 'method_exists', // property_exists($object, 'propertyName'); 'property_exists', ]) && $this->hasFuncCallIndexStringArg($node, 1) ) { return true; } } return false; } protected function isSubclasses($object, array $classes): bool { if (! is_object($object)) { return false; } foreach ($classes as $class) { if ($object instanceof $class) { return true; } } return false; } /** * @param string|iterable<string> $patterns * @param string $value */ public function is($patterns, $value): bool { $value = (string) $value; if (! is_iterable($patterns)) { $patterns = [$patterns]; } foreach ($patterns as $pattern) { $pattern = (string) $pattern; if ($pattern === $value) { return true; } $pattern = preg_quote($pattern, '#'); $pattern = str_replace('\*', '.*', $pattern); if (preg_match('#^'.$pattern.'\z#u', $value) === 1) { return true; } } return false; } protected function hasFuncCallIndexStringArg(FuncCall $funcCall, int $index): bool { return isset($funcCall->args[$index]) && $funcCall->args[$index]->name === null && $funcCall->args[$index]->value instanceof Node\Scalar\String_; } protected function hasFuncCallNameStringArg(FuncCall $funcCall, string $name): bool { foreach ($funcCall->args as $arg) { if ( $arg->name instanceof Node\Identifier && $arg->name->name === $name && $arg->value instanceof Node\Scalar\String_) { return true; } } return false; } public function configure(array $configuration): void { Assert::allStringNotEmpty($configuration); $this->except = [...$this->except, ...$configuration]; }}应用rector 配置文件中配置该规定即可
...