Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Having this logic out of the a dispatcher filter is necessary for PSR7 migration where the controller factory acts as part of the ActionDispatcher.
- Loading branch information
Showing
6 changed files
with
404 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice. | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since 3.3.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\Http; | ||
|
||
use Cake\Core\App; | ||
use Cake\Network\Request; | ||
use Cake\Network\Response; | ||
use Cake\Routing\Exception\MissingControllerException; | ||
use Cake\Utility\Inflector; | ||
use ReflectionClass; | ||
|
||
/** | ||
* Factory method for building controllers from request/response pairs. | ||
*/ | ||
class ControllerFactory | ||
{ | ||
/** | ||
* Create a controller for a given request/response | ||
* | ||
* @param \Cake\Network\Request $request The request to build a controller for. | ||
* @param \Cake\Network\Response $response The response to use. | ||
* @return \Cake\Controller\Controller | ||
*/ | ||
public function create(Request $request, Response $response) | ||
{ | ||
$pluginPath = $controller = null; | ||
$namespace = 'Controller'; | ||
if (isset($request->params['plugin'])) { | ||
$pluginPath = $request->params['plugin'] . '.'; | ||
} | ||
if (isset($request->params['controller'])) { | ||
$controller = $request->params['controller']; | ||
} | ||
if (isset($request->params['prefix'])) { | ||
if (strpos($request->params['prefix'], '/') === false) { | ||
$namespace .= '/' . Inflector::camelize($request->params['prefix']); | ||
} else { | ||
$prefixes = array_map( | ||
'Cake\Utility\Inflector::camelize', | ||
explode('/', $request->params['prefix']) | ||
); | ||
$namespace .= '/' . implode('/', $prefixes); | ||
} | ||
} | ||
$firstChar = substr($controller, 0, 1); | ||
if (strpos($controller, '\\') !== false || | ||
strpos($controller, '.') !== false || | ||
$firstChar === strtolower($firstChar) | ||
) { | ||
return $this->missingController($request); | ||
} | ||
$className = false; | ||
if ($pluginPath . $controller) { | ||
$className = App::classname($pluginPath . $controller, $namespace, 'Controller'); | ||
} | ||
if (!$className) { | ||
return $this->missingController($request); | ||
} | ||
$reflection = new ReflectionClass($className); | ||
if ($reflection->isAbstract() || $reflection->isInterface()) { | ||
return $this->missingController($request); | ||
} | ||
return $reflection->newInstance($request, $response, $controller); | ||
} | ||
|
||
/** | ||
* Throws an exception when a controller is missing. | ||
* | ||
* @param \Cake\Network\Request $request The request. | ||
* @throws \Cake\Routing\Exception\MissingControllerException | ||
* @return void | ||
*/ | ||
protected function missingController($request) | ||
{ | ||
throw new MissingControllerException([ | ||
'class' => $request->param('controller'), | ||
'plugin' => $request->param('plugin'), | ||
'prefix' => $request->param('prefix'), | ||
'_ext' => $request->param('_ext') | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice. | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since 3.3.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\Test\TestCase\Http; | ||
|
||
use Cake\Core\Configure; | ||
use Cake\Http\ControllerFactory; | ||
use Cake\Network\Request; | ||
use Cake\Network\Response; | ||
use Cake\TestSuite\TestCase; | ||
|
||
/** | ||
* Test case for ControllerFactory. | ||
*/ | ||
class ControllerFactoryTest extends TestCase | ||
{ | ||
/** | ||
* Setup | ||
* | ||
* @return void | ||
*/ | ||
public function setUp() | ||
{ | ||
parent::setUp(); | ||
Configure::write('App.namespace', 'TestApp'); | ||
$this->factory = new ControllerFactory(); | ||
$this->response = $this->getMock('Cake\Network\Response'); | ||
} | ||
|
||
/** | ||
* Test building an application controller | ||
* | ||
* @return void | ||
*/ | ||
public function testApplicationController() | ||
{ | ||
$request = new Request([ | ||
'url' => 'cakes/index', | ||
'params' => [ | ||
'controller' => 'Cakes', | ||
'action' => 'index', | ||
] | ||
]); | ||
$result = $this->factory->create($request, $this->response); | ||
$this->assertInstanceOf('TestApp\Controller\CakesController', $result); | ||
$this->assertSame($request, $result->request); | ||
$this->assertSame($this->response, $result->response); | ||
} | ||
|
||
/** | ||
* Test building a prefixed app controller. | ||
* | ||
* @return void | ||
*/ | ||
public function testPrefixedAppController() | ||
{ | ||
$request = new Request([ | ||
'url' => 'admin/posts/index', | ||
'params' => [ | ||
'prefix' => 'admin', | ||
'controller' => 'Posts', | ||
'action' => 'index', | ||
] | ||
]); | ||
$result = $this->factory->create($request, $this->response); | ||
$this->assertInstanceOf( | ||
'TestApp\Controller\Admin\PostsController', | ||
$result | ||
); | ||
$this->assertSame($request, $result->request); | ||
$this->assertSame($this->response, $result->response); | ||
} | ||
|
||
/** | ||
* Test building a nested prefix app controller | ||
* | ||
* @return void | ||
*/ | ||
public function testNestedPrefixedAppController() | ||
{ | ||
$request = new Request([ | ||
'url' => 'admin/sub/posts/index', | ||
'params' => [ | ||
'prefix' => 'admin/sub', | ||
'controller' => 'Posts', | ||
'action' => 'index', | ||
] | ||
]); | ||
$result = $this->factory->create($request, $this->response); | ||
$this->assertInstanceOf( | ||
'TestApp\Controller\Admin\Sub\PostsController', | ||
$result | ||
); | ||
$this->assertSame($request, $result->request); | ||
$this->assertSame($this->response, $result->response); | ||
} | ||
|
||
/** | ||
* Test building a plugin controller | ||
* | ||
* @return void | ||
*/ | ||
public function testPluginController() | ||
{ | ||
$request = new Request([ | ||
'url' => 'test_plugin/test_plugin/index', | ||
'params' => [ | ||
'plugin' => 'TestPlugin', | ||
'controller' => 'TestPlugin', | ||
'action' => 'index', | ||
] | ||
]); | ||
$result = $this->factory->create($request, $this->response); | ||
$this->assertInstanceOf( | ||
'TestPlugin\Controller\TestPluginController', | ||
$result | ||
); | ||
$this->assertSame($request, $result->request); | ||
$this->assertSame($this->response, $result->response); | ||
} | ||
|
||
/** | ||
* Test building a vendored plugin controller. | ||
* | ||
* @return void | ||
*/ | ||
public function testVendorPluginController() | ||
{ | ||
$request = new Request([ | ||
'url' => 'test_plugin_three/ovens/index', | ||
'params' => [ | ||
'plugin' => 'Company/TestPluginThree', | ||
'controller' => 'Ovens', | ||
'action' => 'index', | ||
] | ||
]); | ||
$result = $this->factory->create($request, $this->response); | ||
$this->assertInstanceOf( | ||
'Company\TestPluginThree\Controller\OvensController', | ||
$result | ||
); | ||
$this->assertSame($request, $result->request); | ||
$this->assertSame($this->response, $result->response); | ||
} | ||
|
||
/** | ||
* Test building a prefixed plugin controller | ||
* | ||
* @return void | ||
*/ | ||
public function testPrefixedPluginController() | ||
{ | ||
$request = new Request([ | ||
'url' => 'test_plugin/admin/comments', | ||
'params' => [ | ||
'prefix' => 'admin', | ||
'plugin' => 'TestPlugin', | ||
'controller' => 'Comments', | ||
'action' => 'index', | ||
] | ||
]); | ||
$result = $this->factory->create($request, $this->response); | ||
$this->assertInstanceOf( | ||
'TestPlugin\Controller\Admin\CommentsController', | ||
$result | ||
); | ||
$this->assertSame($request, $result->request); | ||
$this->assertSame($this->response, $result->response); | ||
} | ||
|
||
/** | ||
* @expectedException \Cake\Routing\Exception\MissingControllerException | ||
* @expectedExceptionMessage Controller class Abstract could not be found. | ||
* @return void | ||
*/ | ||
public function testAbstractClassFailure() | ||
{ | ||
$request = new Request([ | ||
'url' => 'abstract/index', | ||
'params' => [ | ||
'controller' => 'Abstract', | ||
'action' => 'index', | ||
] | ||
]); | ||
$this->factory->create($request, $this->response); | ||
} | ||
|
||
/** | ||
* @expectedException \Cake\Routing\Exception\MissingControllerException | ||
* @expectedExceptionMessage Controller class Interface could not be found. | ||
* @return void | ||
*/ | ||
public function testInterfaceFailure() | ||
{ | ||
$request = new Request([ | ||
'url' => 'interface/index', | ||
'params' => [ | ||
'controller' => 'Interface', | ||
'action' => 'index', | ||
] | ||
]); | ||
$this->factory->create($request, $this->response); | ||
} | ||
|
||
/** | ||
* @expectedException \Cake\Routing\Exception\MissingControllerException | ||
* @expectedExceptionMessage Controller class Invisible could not be found. | ||
* @return void | ||
*/ | ||
public function testMissingClassFailure() | ||
{ | ||
$request = new Request([ | ||
'url' => 'interface/index', | ||
'params' => [ | ||
'controller' => 'Invisible', | ||
'action' => 'index', | ||
] | ||
]); | ||
$this->factory->create($request, $this->response); | ||
} | ||
|
||
/** | ||
* @expectedException \Cake\Routing\Exception\MissingControllerException | ||
* @expectedExceptionMessage Controller class TestApp\Controller\CakesController could not be found. | ||
* @return void | ||
*/ | ||
public function testAbsoluteReferenceFailure() | ||
{ | ||
$request = new Request([ | ||
'url' => 'interface/index', | ||
'params' => [ | ||
'controller' => 'TestApp\Controller\CakesController', | ||
'action' => 'index', | ||
] | ||
]); | ||
$this->factory->create($request, $this->response); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
tests/test_app/Plugin/Company/TestPluginThree/src/Controller/OvensController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
namespace Company\TestPluginThree\Controller; | ||
|
||
use Cake\Controller\Controller; | ||
|
||
class OvensController extends Controller | ||
{ | ||
public function index() | ||
{ | ||
$this->autoRender = false; | ||
} | ||
} |
Oops, something went wrong.