首页 / PHP / php – HMVC路由中的歧义
php – HMVC路由中的歧义
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了php – HMVC路由中的歧义,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含9811字,纯文字阅读大概需要15分钟。
内容图文
![php – HMVC路由中的歧义](/upload/InfoBanner/zyjiaocheng/816/72df522ef1a94dbb8ce295ccadea8a83.jpg)
我有一个路由机制,通过依赖文件系统结构来分派请求:
function Route($root) {
$root = realpath($root) . '/';
$segments = array_filter(explode('/',
substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']))
), 'strlen');
if ((count($segments) == 0) || (is_dir($root) === false)) {
return true; // serve index
}
$controller = null;
$segments = array_values($segments);
while ((is_null($segment = array_shift($segments)) !== true)
&& (is_dir($root . $controller . $segment . '/'))) {
$controller .= $segment . '/';
}
if ((is_file($controller = $root . $controller . $segment . '.php')) {
$class = basename($controller . '.php');
$method = array_shift($segments) ?: $_SERVER['REQUEST_METHOD'];
require($controller);
if (method_exists($class = new $class(), $method)) {
return call_user_func_array(array($class, $method), $segments);
}
}
throw new Exception('/' . implode('/', self::Segment()), 404); // serve 404
}
基本上,它尝试将尽可能多的URL段映射到目录,将以下段与实际控制器(具有相同名称的.php文件)相匹配.如果提供了更多的段,则第一个定义要调用的操作(回退到HTTP方法),并将剩余的段定义为操作参数.
问题是(取决于文件系统结构)存在一些含糊之处.考虑一下:
- /controllers
- /admin
- /company
- /edit.php (has get() & post() methods)
- /company.php (has get($id = null) method)
现在的歧义 – 当我访问domain.tld / admin / company / edit / edit.php控制器服务请求(应该如此),但访问domain.tld / admin / company /通过GET或domain.tld / admin / company / get /直接抛出404错误,因为公司段已映射到相应的目录,即使其余段在文件系统中没有映射.我该如何解决这个问题?优选地,不会在磁盘中投入太多精力.
关于这个问题,SO中已经有很多类似的问题,我看了一些问题,但我找不到能够提供可靠有效解决方案的单一答案.
解决方法:
对于像这样的关键内容,使用PHPUnit等测试框架编写测试非常重要.
像这里描述的那样安装它(你需要梨):
https://github.com/sebastianbergmann/phpunit/
我还使用虚拟文件系统,因此您的测试文件夹不会混乱:https://github.com/mikey179/vfsStream/wiki/Install
我只是将Route函数放入一个名为Route.php的文件中.在同一目录中,我现在创建了一个test.php文件,其中包含以下内容:
<?php
require_once 'Route.php';
class RouteTest extends PHPUnit_Framework_TestCase {
}
要检查是否一切正常,请打开命令行并执行以下操作:
$cd path/to/directory
$phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 1.50Mb
There was 1 failure:
1) Warning
No tests found in class "RouteTest".
FAILURES!
Tests: 1, Assertions: 0, Failures: 1.
如果这看起来正确安装了PHPUnit并且您已准备好编写测试.
为了使Route功能更好地测试并且更少地耦合到服务器和文件系统,我稍微修改了它:
// new parameter $request instead of relying on server variables
function Route($root, $request_uri, $request_method) {
// vfsStream doesn't support realpath(). This will do.
$root .= '/';
// replaced server variable with $request_uri
$segments = array_filter(explode('/', $request_uri), 'strlen');
if ((count($segments) == 0) || (is_dir($root) === false)) {
return true; // serve index
}
$controller = null;
$all_segments = array_values($segments);
$segments = $all_segments;
while ((is_null($segment = array_shift($segments)) !== true)
&& (is_dir($root . $controller . $segment . '/'))) {
$controller .= $segment . '/';
}
if (is_file($controller = $root . $controller . $segment . '.php')) {
$class = basename($controller . '.php');
// replaced server variable with $request_method
$method = array_shift($segments) ?: $request_method;
require($controller);
if (method_exists($class = new $class(), $method)) {
return call_user_func_array(array($class, $method), $segments);
}
}
// $all_segments variable instead of a call to self::
throw new Exception('/' . implode('/', $all_segments), 404); // serve 404
}
让我们添加一个测试来检查如果请求索引路由,函数返回true:
public function testIndexRoute() {
$this->assertTrue(Route('.', '', 'get'));
$this->assertTrue(Route('.', '/', 'get'));
}
因为您的测试类扩展了PHPUnit_Framework_TestCase,所以您现在可以使用像$this-> assertTrue这样的方法
检查某个语句的评估结果是否为真.让我们再次运行它:
$phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 1.75Mb
OK (1 test, 2 assertions)
这个测试通过了!让我们测试array_filter是否正确删除空段:
public function testEmptySegments() {
$this->assertTrue(Route('.', '//', 'get'));
$this->assertTrue(Route('.', '//////////', 'get'));
}
如果路由的$root目录不存在,还可以测试是否请求索引路由.
public function testInexistentRoot() {
$this->assertTrue(Route('./inexistent', '/', 'get'));
$this->assertTrue(Route('./does-not-exist', '/some/random/route', 'get'));
}
为了测试更多的东西,我们现在需要包含带方法的类的文件.因此,在运行每个测试之前,让我们使用我们的虚拟文件系统来设置包含文件的目录结构.
require_once 'Route.php';
require_once 'vfsStream/vfsStream.php';
class RouteTest extends PHPUnit_Framework_TestCase {
public function setUp() {
// intiialize stuff before each test
}
public function tearDown() {
// clean up ...
}
PHPUnit有一些特殊的方法来处理这类事情. setUp方法在此测试类中的每个测试方法之前执行.并且测试方法执行后的tearDown方法.
现在我使用vfsStream创建一个目录结构. (如果您正在寻找一个教程来执行此操作:https://github.com/mikey179/vfsStream/wiki是一个非常好的资源)
public function setUp() {
$edit_php = <<<EDIT_PHP
<?php
class edit {
public function get() {
return __METHOD__ . "()";
}
public function post() {
return __METHOD__ . "()";
}
}
EDIT_PHP;
$company_php = <<<COMPANY_PHP
<?php
class company {
public function get(\$id = null) {
return __METHOD__ . "(\$id)";
}
}
COMPANY_PHP;
$this->root = vfsStream::setup('controllers', null, Array(
'admin' => Array(
'company' => Array(
'edit.php' => $edit_php
),
'company.php' => $company_php
)
));
}
public function tearDown() {
unset($this->root);
}
vfsStream :: setup()现在创建一个具有给定文件结构和给定文件内容的虚拟目录.
正如您所看到的,我让控制器返回方法名称和参数字符串.
现在我们可以在我们的测试套件中添加一些测试:
public function testSimpleDirectMethodAccess() {
$this->assertEquals("edit::get()", Route(vfsStream::url('controllers'), '/controllers/admin/company/edit/get', 'get'));
}
但这次测试失败了:
$phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
...
Fatal error: Class 'edit.php.php' not found in C:\xampp\htdocs\r\Route.php on line 27
所以$class变量有问题.如果我们现在使用调试器(或一些回声)检查Route函数中的以下行.
$class = basename($controller . '.php');
我们可以看到$controller变量保存正确的文件名,但为什么会附加.php?
这种接缝是打字错误.我认为它应该是:
$class = basename($controller, '.php');
因为这会删除.php扩展名.我们得到正确的类名编辑.
现在让我们测试一下,如果我们请求一个在我们的目录结构中不存在的随机路径,就会抛出异常.
/**
* @expectedException Exception
* @expectedMessage /random-route-to-the/void
*/
public function testForInexistentRoute() {
Route(vfsStream::url('controllers'), '/random-route-to-the/void', 'get');
}
PHPUnit自动读取此注释并检查在执行此方法时是否抛出Exception类型Exception,并且Exception的消息是/ random-route-to-the / void
这种缝合起作用.让我们检查$request_method参数是否正常工作.
public function testMethodAccessByHTTPMethod() {
$this->assertEquals("edit::get()", Route(vfsStream::url('controllers'), '/admin/company/edit', 'get'));
$this->assertEquals("edit::post()", Route(vfsStream::url('controllers'), '/admin/company/edit', 'post'));
}
如果我们执行此测试,我们会遇到另一个问题:
$phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
....
Fatal error: Cannot redeclare class edit in vfs://controllers/admin/company/edit.php on line 2
看起来我们对同一个文件多次使用include / require.
require($controller);
让我们改变它
require_once($controller);
现在让我们面对你的问题并编写测试来检查目录公司和文件company.php是否互相干扰.
$this->assertEquals("company::get()", Route(vfsStream::url('controllers'), '/admin/company', 'get'));
$this->assertEquals("company::get()", Route(vfsStream::url('controllers'), '/admin/company/get', 'get'));
在这里,我们得到404异常,正如您在问题中所述:
$phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
.....E.
Time: 0 seconds, Memory: 2.00Mb
There was 1 error:
1) RouteTest::testControllerWithSubControllers
Exception: /admin/company
C:\xampp\htdocs\r\Route.php:32
C:\xampp\htdocs\r\test.php:69
FAILURES!
Tests: 7, Assertions: 10, Errors: 1.
这里的问题是,我们不知道什么时候进入子目录以及何时在.php文件中使用控制器.
所以我们需要指定你想要发生什么.我假设以下内容,因为它是有道理的.
>如果控制器不包含请求的方法,则仅输入子目录.
>如果控制器和子目录都没有包含请求的方法,则抛出404
所以不要像这里一样搜索目录:
while ((is_null($segment = array_shift($segments)) !== true)
&& (is_dir($root . $controller . $segment . '/'))) {
$controller .= $segment . '/';
}
我们需要搜索文件.如果我们找到一个不包含请求方法的文件,那么我们会搜索一个目录.
function Route($root, $request_uri, $request_method) {
$segments = array_filter(explode('/', $request_uri), 'strlen');
if ((count($segments) == 0) || (is_dir($root) === false)) {
return true; // serve index
}
$all_segments = array_values($segments);
$segments = $all_segments;
$directory = $root . '/';
do {
$segment = array_shift($segments);
if(is_file($controller = $directory . $segment . ".php")) {
$class = basename($controller, '.php');
$method = isset($segments[0]) ? $segments[0] : $request_method;
require_once($controller);
if (method_exists($class = new $class(), $method)) {
return call_user_func_array(array($class, $method), array_slice($segments, 1));
}
}
$directory .= $segment . '/';
} while(is_dir($directory));
throw new Exception('/' . implode('/', $all_segments), 404); // serve 404
}
此方法现在按预期工作.
我们现在可以添加更多的测试用例,但我不想再进行更多测试.
正如您所看到的,运行一组自动化测试以确保它非常有用
你的功能中的一些东西工作.它对调试也很有帮助,因为
你可以知道错误发生在哪里.我只想给你一个开始
如何做TDD以及如何使用PHPUnit,这样你就可以自己调试代码了.
“Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime.”
当然,您应该在编写代码之前编写测试.
这里有一些可能有趣的链接:
> PHPUnit手册:http://www.phpunit.de/manual/current/en/
>官方梨花网站:http://pear.php.net/
>测试驱动开发(TDD):http://en.wikipedia.org/wiki/Test-driven_development
内容总结
以上是互联网集市为您收集整理的php – HMVC路由中的歧义全部内容,希望文章能够帮你解决php – HMVC路由中的歧义所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。