Skip to content

Commit 7b39fcc

Browse files
committed
Release 3.5.1
1 parent 6c2307d commit 7b39fcc

6 files changed

Lines changed: 113 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ history, the old changelog, and committed file changes. Older Zemit-era entries
1515
are summarized where the commit history is too granular to be useful as
1616
release notes.
1717

18+
## 3.5.1 - 2026-06-19
19+
20+
### Fixed
21+
22+
- Fixed controller model inference so PHP built-in classes such as `Error` are
23+
not treated as loadable Phalcon models, and `loadModel()` now fails with a
24+
clear PhalconKit service exception before delegating invalid model names to
25+
Phalcon's models manager.
26+
1827
## 3.5.0 - 2026-06-18
1928

2029
### Changed

src/Mvc/Controller/Traits/Model.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public function getModelNameFromController(?array $namespaces = null, string $ne
132132
)
133133
);
134134

135-
if (class_exists($model)) {
135+
if (class_exists($model) && is_subclass_of($model, ModelInterface::class)) {
136136
return $model;
137137
}
138138

@@ -169,10 +169,21 @@ public function getControllerName(): string
169169
* @param string|null $modelName The name of the model to load. Default is null and will use $this->getModelName().
170170
*
171171
* @return ModelInterface The loaded model.
172+
*
173+
* @throws ServiceException When no model can be resolved or the resolved
174+
* class does not implement Phalcon's model contract.
172175
*/
173176
public function loadModel(?string $modelName = null): ModelInterface
174177
{
175-
$modelName ??= $this->getModelName() ?? '';
178+
$modelName ??= $this->getModelName();
179+
if (!$modelName || !is_a($modelName, ModelInterface::class, true)) {
180+
throw new ServiceException(sprintf(
181+
'Unable to load controller model "%s": expected a class implementing "%s".',
182+
$modelName ?: '(none)',
183+
ModelInterface::class
184+
));
185+
}
186+
176187
return $this->modelsManager->load($modelName);
177188
}
178189

src/Support/Version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ class Version extends \Phalcon\Support\Version
4040
#[\Override]
4141
protected function getVersion(): array
4242
{
43-
return [3, 5, 0, 4, 0];
43+
return [3, 5, 1, 4, 0];
4444
}
4545
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Phalcon Kit.
5+
*
6+
* (c) Phalcon Kit Team
7+
*
8+
* For the full copyright and license information, please view the LICENSE.txt
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace PhalconKit\Tests\Unit\Mvc\Controller\Traits\Fixtures\Models;
15+
16+
use Phalcon\Mvc\Model;
17+
18+
final class Error extends Model
19+
{
20+
}

tests/Unit/Mvc/Controller/Traits/ModelTraitTest.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,53 @@
1818
use PhalconKit\Exception\ServiceException;
1919
use PhalconKit\Mvc\Controller\Restful;
2020
use PhalconKit\Tests\Unit\AbstractUnit;
21+
use PhalconKit\Tests\Unit\Mvc\Controller\Traits\Fixtures\Models\Error as ErrorModel;
2122
use PhalconKit\Tests\Unit\Mvc\Controller\Traits\Fixtures\ModelColumnAlternateModel;
2223
use PhalconKit\Tests\Unit\Mvc\Controller\Traits\Fixtures\ModelColumnMappedModel;
2324
use PhalconKit\Tests\Unit\Mvc\Controller\Traits\Fixtures\ModelColumnMetadataModel;
2425
use PhalconKit\Tests\Unit\Mvc\Model\Fixtures\FakeMetaData;
2526

2627
class ModelTraitTest extends AbstractUnit
2728
{
29+
public function testModelNameInferenceIgnoresNonModelGlobalClasses(): void
30+
{
31+
$controller = $this->newControllerNamed('Error');
32+
$controller->setModelNamespaces([]);
33+
34+
$this->assertNull($controller->getModelNameFromController());
35+
}
36+
37+
public function testModelNameInferenceResolvesNamespacedModelAfterGlobalClassCollision(): void
38+
{
39+
$controller = $this->newControllerNamed('Error');
40+
$controller->setModelNamespaces([
41+
substr(ErrorModel::class, 0, -strlen('Error')) => __DIR__ . '/Fixtures/Models/',
42+
]);
43+
44+
$this->assertSame(ErrorModel::class, $controller->getModelNameFromController());
45+
}
46+
47+
public function testLoadModelRejectsMissingOrNonModelClassBeforeModelsManagerLoad(): void
48+
{
49+
$controller = $this->newControllerNamed('Error');
50+
$controller->setModelName('Error');
51+
52+
$this->expectException(ServiceException::class);
53+
$this->expectExceptionMessage(
54+
'Unable to load controller model "Error": expected a class implementing "Phalcon\Mvc\ModelInterface".'
55+
);
56+
57+
$controller->loadModel();
58+
}
59+
60+
public function testLoadModelAcceptsNamespacedModelAfterGlobalClassCollision(): void
61+
{
62+
$controller = $this->newControllerNamed('Error');
63+
$controller->setModelName(ErrorModel::class);
64+
65+
$this->assertInstanceOf(ErrorModel::class, $controller->loadModel());
66+
}
67+
2868
public function testGetModelNamespacesRejectsInvalidLoaderService(): void
2969
{
3070
$controller = new class extends Restful {
@@ -142,4 +182,33 @@ public function loadModel(?string $modelName = null): ModelInterface
142182

143183
return $controller;
144184
}
185+
186+
private function newControllerNamed(string $controllerName): Restful
187+
{
188+
$controller = new class extends Restful {
189+
public string $controllerName = '';
190+
191+
/**
192+
* Disable normal REST initialization for this trait-focused test.
193+
*/
194+
public function initialize(): void
195+
{
196+
}
197+
198+
public function getControllerName(): string
199+
{
200+
return $this->controllerName;
201+
}
202+
};
203+
204+
$di = $this->di;
205+
if ($di === null) {
206+
self::fail('DI container was not initialized.');
207+
}
208+
209+
$controller->setDI($di);
210+
$controller->controllerName = $controllerName;
211+
212+
return $controller;
213+
}
145214
}

tests/Unit/Support/VersionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function testVersion(): void
4040
$this->assertEquals($this->bootstrap->config->path('phalcon.version'), $phalconVersion->get());
4141

4242
// Test version->get()
43-
$this->assertSame('3.5.0', $version->get());
43+
$this->assertSame('3.5.1', $version->get());
4444
$this->assertNotEmpty($version->get());
4545
$this->assertIsString($version->get());
4646

0 commit comments

Comments
 (0)