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\Functions;
15:
16: use PhpParser\Node;
17: use PHPStan\Analyser\Scope;
18: use PHPStan\Rules\Rule;
19: use PHPStan\Rules\RuleErrorBuilder;
20: use PHPStan\ShouldNotHappenException;
21:
22: /**
23: * @implements Rule<Node\Stmt\Function_>
24: */
25: final class FunctionNamingRule implements Rule
26: {
27: #[\Override]
28: public function getNodeType(): string
29: {
30: return Node\Stmt\Function_::class;
31: }
32:
33: #[\Override]
34: public function processNode(Node $node, Scope $scope): array
35: {
36: if (null === $node->namespacedName) {
37: throw new ShouldNotHappenException(); // @codeCoverageIgnore
38: }
39:
40: $functionName = $node->namespacedName->toString();
41:
42: if (! str_starts_with($functionName, 'Nexus\\')) {
43: return [
44: RuleErrorBuilder::message(\sprintf(
45: 'Function %s() should be namespaced using the "Nexus\\" namespace.',
46: $functionName,
47: ))
48: ->identifier('nexus.functionNamespace')
49: ->build(),
50: ];
51: }
52:
53: $basename = basename(str_replace('\\', '/', $functionName));
54:
55: if (preg_match('/^[a-z][a-z0-9_]+$/', $basename) !== 1) {
56: return [
57: RuleErrorBuilder::message(\sprintf(
58: 'Function %s() should be in lower snake case format.',
59: $functionName,
60: ))
61: ->identifier('nexus.functionCasing')
62: ->build(),
63: ];
64: }
65:
66: $errors = [];
67:
68: foreach (array_values($node->params) as $index => $param) {
69: if (! $param->var instanceof Node\Expr\Variable) {
70: continue; // @codeCoverageIgnore
71: }
72:
73: if (! \is_string($param->var->name)) {
74: continue; // @codeCoverageIgnore
75: }
76:
77: if (preg_match('/^[a-z][a-zA-Z0-9]*$/', $param->var->name) !== 1) {
78: $errors[] = RuleErrorBuilder::message(\sprintf(
79: 'Parameter #%d $%s of function %s() should be in camelCase format.',
80: $index + 1,
81: $param->var->name,
82: $functionName,
83: ))
84: ->identifier('nexus.functionParamCasing')
85: ->line($param->getStartLine())
86: ->build()
87: ;
88: }
89: }
90:
91: return $errors;
92: }
93: }
94: