1: <?php
2:
3: declare(strict_types=1);
4:
5: /**
6: * This file is part of the Nexus framework.
7: *
8: * (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9: *
10: * For the full copyright and license information, please view
11: * the LICENSE file that was distributed with this source code.
12: */
13:
14: namespace Nexus\PHPStan\Rules\Methods;
15:
16: use PhpParser\Node;
17: use PHPStan\Analyser\Scope;
18: use PHPStan\Node\InClassMethodNode;
19: use PHPStan\Rules\Rule;
20: use PHPStan\Rules\RuleErrorBuilder;
21:
22: /**
23: * @implements Rule<InClassMethodNode>
24: */
25: final class MethodNamingRule implements Rule
26: {
27: #[\Override]
28: public function getNodeType(): string
29: {
30: return InClassMethodNode::class;
31: }
32:
33: #[\Override]
34: public function processNode(Node $node, Scope $scope): array
35: {
36: $method = $node->getOriginalNode();
37: $methodName = $method->name->toString();
38: $methodReflection = $node->getMethodReflection();
39: $methodPrototype = $methodReflection->getPrototype();
40:
41: if (
42: $methodPrototype !== $methodReflection
43: && ! str_starts_with($methodPrototype->getDeclaringClass()->getDisplayName(), 'Nexus\\')
44: ) {
45: return [];
46: }
47:
48: if (str_starts_with($methodName, '__')) {
49: if ($method->isMagic()) {
50: return [];
51: }
52:
53: return [
54: RuleErrorBuilder::message(\sprintf(
55: 'Method %s::%s() should not start with double underscores.',
56: $node->getClassReflection()->getDisplayName(),
57: $methodName,
58: ))
59: ->identifier('nexus.methodDoubleUnderscore')
60: ->build(),
61: ];
62: }
63:
64: if (str_starts_with($methodName, '_')) {
65: return [
66: RuleErrorBuilder::message(\sprintf(
67: 'Method %s::%s() should not start with an underscore.',
68: $node->getClassReflection()->getDisplayName(),
69: $methodName,
70: ))
71: ->identifier('nexus.methodUnderscore')
72: ->build(),
73: ];
74: }
75:
76: if (preg_match('/^[a-z][a-zA-Z0-9]+$/', $methodName) !== 1) {
77: return [
78: RuleErrorBuilder::message(\sprintf(
79: 'Method %s::%s() should be written in camelCase format.',
80: $node->getClassReflection()->getDisplayName(),
81: $methodName,
82: ))
83: ->identifier('nexus.methodCasing')
84: ->build(),
85: ];
86: }
87:
88: $errors = [];
89:
90: foreach (array_values($method->params) as $index => $param) {
91: if (! $param->var instanceof Node\Expr\Variable) {
92: continue; // @codeCoverageIgnore
93: }
94:
95: if (! \is_string($param->var->name)) {
96: continue; // @codeCoverageIgnore
97: }
98:
99: if (preg_match('/^[a-z][a-zA-Z0-9]*$/', $param->var->name) !== 1) {
100: $errors[] = RuleErrorBuilder::message(\sprintf(
101: 'Parameter #%d $%s of %s::%s() should be in camelCase with no underscores.',
102: $index + 1,
103: $param->var->name,
104: $node->getClassReflection()->getDisplayName(),
105: $methodName,
106: ))
107: ->identifier('nexus.methodParamNaming')
108: ->line($param->getStartLine())
109: ->build()
110: ;
111: }
112: }
113:
114: return $errors;
115: }
116: }
117: