Skip to content

Commit e173734

Browse files
committed
Merge branch '3.x' into 3.next
2 parents 8d03e28 + 31952e1 commit e173734

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+464
-99
lines changed

.editorconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ indent_size = 4
1111
insert_final_newline = true
1212
trim_trailing_whitespace = true
1313

14+
[*.neon]
15+
indent_style = tab
16+
17+
[*.neon.dist]
18+
indent_style = tab
19+
1420
[*.yml]
1521
indent_size = 2
1622

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
vendor/
55
/.idea/
66
/.phpunit.cache
7+
/.phpunit.result.cache
8+
.phpcs.cache
79
tests/test_app/App/Model/Table/CommentsTable.php
810
tests/test_app/App/Controller/ProductVersionsController.php
911
tests/test_app/App/Controller/ProductsController.php
1012
tests/test_app/Plugin/TestBake/
1113
tests/test_app/tests/
1214
clover.xml
13-
/.phpunit.result.cache

.phive/phars.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<phive xmlns="https://phar.io/phive">
3-
<phar name="phpstan" version="2.1.33" installed="2.1.33" location="./tools/phpstan" copy="false"/>
3+
<phar name="phpstan" version="2.1.39" installed="2.1.39" location="./tools/phpstan" copy="false"/>
44
</phive>

composer.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
},
2424
"require": {
2525
"php": ">=8.1",
26-
"brick/varexporter": "^0.6.0",
26+
"brick/varexporter": "^0.6.0 || ^0.7.0",
2727
"cakephp/cakephp": "^5.1",
2828
"cakephp/twig-view": "^2.0.2",
2929
"nikic/php-parser": "^5.0.0"
@@ -40,10 +40,17 @@
4040
},
4141
"autoload-dev": {
4242
"psr-4": {
43+
"Authentication\\": "tests/test_app/Plugin/Authentication/src/",
44+
"Authorization\\": "tests/test_app/Plugin/Authorization/src/",
4345
"BakeTest\\": "tests/test_app/Plugin/BakeTest/src/",
4446
"Bake\\Test\\": "tests/",
4547
"Bake\\Test\\App\\": "tests/test_app/App/",
4648
"Company\\Pastry\\": "tests/test_app/Plugin/Company/Pastry/src/",
49+
"FixtureTest\\": "tests/test_app/App/Plugin/FixtureTest/src/",
50+
"TestBake\\": "tests/test_app/Plugin/TestBake/src/",
51+
"TestBakeTheme\\": "tests/test_app/Plugin/TestBakeTheme/src/",
52+
"TestTemplate\\": "tests/test_app/App/Plugin/TestTemplate/src/",
53+
"TestTest\\": "tests/test_app/App/Plugin/TestTest/src/",
4754
"WithBakeSubFolder\\": "tests/test_app/Plugin/WithBakeSubFolder/src/"
4855
}
4956
},
@@ -58,8 +65,8 @@
5865
"@test",
5966
"@cs-check"
6067
],
61-
"cs-check": "phpcs --parallel=16 -p src/ tests/",
62-
"cs-fix": "phpcbf --parallel=16 -p src/ tests/",
68+
"cs-check": "phpcs",
69+
"cs-fix": "phpcbf",
6370
"phpstan": "tools/phpstan analyse",
6471
"stan": "@phpstan",
6572
"stan-baseline": "tools/phpstan --generate-baseline",

docs/en/usage.rst

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,55 @@ For non-conventional relations, you can use references in the constraints / fore
6464
->addForeignKey('shipping_country_id', 'countries', 'cid')
6565

6666

67+
Bake Enums
68+
==========
69+
70+
You can use bake to generate `backed enums <https://www.php.net/manual/en/language.enumerations.backed.php>`_
71+
for use in your models. Enums are placed in **src/Model/Enum/** and implement
72+
``EnumLabelInterface`` which provides a ``label()`` method for human-readable display.
73+
74+
To bake a string-backed enum::
75+
76+
bin/cake bake enum ArticleStatus draft,published,archived
77+
78+
This generates **src/Model/Enum/ArticleStatus.php**::
79+
80+
namespace App\Model\Enum;
81+
82+
use Cake\Database\Type\EnumLabelInterface;
83+
use Cake\Utility\Inflector;
84+
85+
enum ArticleStatus: string implements EnumLabelInterface
86+
{
87+
case Draft = 'draft';
88+
case Published = 'published';
89+
case Archived = 'archived';
90+
91+
public function label(): string
92+
{
93+
return Inflector::humanize(Inflector::underscore($this->name));
94+
}
95+
}
96+
97+
For int-backed enums, use the ``-i`` option and provide values with colons::
98+
99+
bin/cake bake enum Priority low:1,medium:2,high:3 -i
100+
101+
This generates an int-backed enum::
102+
103+
enum Priority: int implements EnumLabelInterface
104+
{
105+
case Low = 1;
106+
case Medium = 2;
107+
case High = 3;
108+
109+
// ...
110+
}
111+
112+
You can also bake enums into plugins::
113+
114+
bin/cake bake enum MyPlugin.OrderStatus pending,processing,shipped
115+
67116
Bake Themes
68117
===========
69118

phpcs.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
<file>tests/</file>
77

88
<rule ref="CakePHP" />
9+
<arg value="nps"/>
10+
<arg name="colors"/>
11+
<arg name="parallel" value="4"/>
12+
<arg name="cache" value=".phpcs.cache"/>
913

1014
<exclude-pattern>*/comparisons/*</exclude-pattern>
1115
<exclude-pattern>tests/test_app/*</exclude-pattern>

phpstan.neon

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ includes:
22
- phpstan-baseline.neon
33

44
parameters:
5-
level: 6
6-
paths:
7-
- src/
8-
bootstrapFiles:
9-
- tests/bootstrap.php
10-
ignoreErrors:
11-
- identifier: missingType.iterableValue
5+
level: 8
6+
paths:
7+
- src/
8+
bootstrapFiles:
9+
- tests/bootstrap.php
10+
ignoreErrors:
11+
- identifier: missingType.iterableValue

src/BakePlugin.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
use Cake\Http\BaseApplication;
2727
use DirectoryIterator;
2828
use ReflectionClass;
29-
use ReflectionException;
3029

3130
/**
3231
* Plugin class for bake
@@ -140,11 +139,11 @@ protected function findInPath(string $namespace, string $path): array
140139
$class = $namespace . $item->getBasename('.php');
141140

142141
if (!$hasSubfolder) {
143-
try {
144-
$reflection = new ReflectionClass($class);
145-
} catch (ReflectionException) {
142+
if (!class_exists($class)) {
146143
continue;
147144
}
145+
146+
$reflection = new ReflectionClass($class);
148147
if (!$reflection->isInstantiable() || !$reflection->isSubclassOf(BakeCommand::class)) {
149148
continue;
150149
}

src/CodeGen/CodeParser.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ public function parseFile(string $code): ?ParsedFile
8181
{
8282
$this->fileText = $code;
8383
try {
84-
$this->traverser->traverse($this->parser->parse($code));
84+
$ast = $this->parser->parse($code);
85+
if ($ast === null) {
86+
return null;
87+
}
88+
$this->traverser->traverse($ast);
8589
} catch (Error $e) {
8690
throw new ParseException($e->getMessage(), null, $e);
8791
}
@@ -172,7 +176,11 @@ public function enterNode(Node $node)
172176
throw new ParseException('Multiple constants per line are not supported, update your file');
173177
}
174178

175-
$name = (string)current($constant->consts)->name;
179+
$const = current($constant->consts);
180+
if ($const === false) {
181+
continue;
182+
}
183+
$name = (string)$const->name;
176184
$constants[$name] = $this->getNodeCode($constant);
177185
}
178186

@@ -182,7 +190,11 @@ public function enterNode(Node $node)
182190
throw new ParseException('Multiple properties per line are not supported, update your file');
183191
}
184192

185-
$name = (string)current($property->props)->name;
193+
$prop = current($property->props);
194+
if ($prop === false) {
195+
continue;
196+
}
197+
$name = (string)$prop->name;
186198
$properties[$name] = $this->getNodeCode($property);
187199
}
188200

src/CodeGen/ColumnTypeExtractor.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public function extract(string $code): array
7373
// Wrap code in a dummy class if needed for parsing
7474
$wrappedCode = "<?php\nclass Dummy {\n" . $code . "\n}";
7575
$ast = $this->parser->parse($wrappedCode);
76+
if ($ast === null) {
77+
return [];
78+
}
7679

7780
$traverser = new NodeTraverser();
7881
$traverser->addVisitor($this);
@@ -144,8 +147,13 @@ protected function processMethodCall(MethodCall $methodCall): void
144147
) {
145148
// Extract the column name and type expression
146149
if (count($methodCall->args) >= 2) {
147-
$columnArg = $methodCall->args[0]->value;
148-
$typeArg = $methodCall->args[1]->value;
150+
$columnArgNode = $methodCall->args[0];
151+
$typeArgNode = $methodCall->args[1];
152+
if (!$columnArgNode instanceof Node\Arg || !$typeArgNode instanceof Node\Arg) {
153+
return;
154+
}
155+
$columnArg = $columnArgNode->value;
156+
$typeArg = $typeArgNode->value;
149157

150158
// Get column name
151159
$columnName = $this->getStringValue($columnArg);
@@ -199,7 +207,11 @@ protected function getTypeExpression(Node $node): ?string
199207
if ($className === 'EnumType' || str_ends_with($className, '\\EnumType')) {
200208
if ($methodName === 'from' && count($node->args) > 0) {
201209
// Extract the enum class name
202-
$arg = $node->args[0]->value;
210+
$argNode = $node->args[0];
211+
if (!$argNode instanceof Node\Arg) {
212+
return null;
213+
}
214+
$arg = $argNode->value;
203215
if ($arg instanceof Node\Expr\ClassConstFetch) {
204216
if (
205217
$arg->class instanceof Node\Name &&

0 commit comments

Comments
 (0)