【发布时间】:2019-11-08 18:59:12
【问题描述】:
我的网站上有一篇包含(降价)内容的文章:
# PHP Proper Class Name
Class names in PHP are case insensitve. If you have a class declaration like:
```php
class MyWeirdClass {}
```
you can instantiate it with `new myWEIRDclaSS()` or any other variation on the case. In some instances, you may want to know, what is the correct, case-sensitive class name.
### Example Use case
For example, in one of my libraries under construction [API Doccer](https://github.com/ReedOverflow/PHP-API-Doccer), I can view documentation for a class at url `/doc/class/My-Namespace-Clazzy/` and if you enter the wrong case, like `/doc/class/my-NAMESPACE-CLAzzy`, it should automatically redirect to the proper-cased class. To do this, I use the reflection method below as it is FAR more performant than the `get_delcared_classes` method
## Reflection - get proper case
Credit goes to [l00k on StackOverflow](https://stackoverflow.com/a/35222911/802469)
```php
$className = 'My\caseINAccuRATE\CLassNamE';
$reflection = new ReflectionClass($className);
echo $reflection->getName();
```
results in `My\CaseInaccurate\ClassName`;
Running the benchmark (see below) on localhost on my laptop, getting the proper case class name of 500 classes took about 0.015 seconds, as opposed to ~0.050 seconds using the `get_declared_classes` method below.
## get_declared_classes - get proper case
This was my idea, as I hadn't even considered using reflection, until I saw [l00k's answer on StackOverflow](https://stackoverflow.com/a/35222911/802469). Guessing it would be less efficient, I wrote the code and figured it out anyway, because it's fun!
```php
$wrongCaseName = 'Some\classy\THIng';
class_exists($wrongCaseName); //so it gets autoloaded if not already done
$classes = get_declared_classes();
$map = array_combine(array_map('strtolower',$classes),$classes);
$proper = $map[strtolower($wrongCaseName)];
```
results in `$proper = 'Some\Classy\Thing'`;
Running the bencmark (see below) on localhost on my laptop, getting the proper case class name of 500 classes took about 0.050 seconds, as opposed to ~0.015 seconds with reflection (above).
## Benchmark:
I used the following code to do the benchmark, removing the `classes` directory between each run of the benchmark. It's not perfect. At all. But it gets the job done well enough, I think:
```php
<?php
$times = [];
$times['begin'] = microtime(TRUE);
spl_autoload_register(function($className){
if (file_exists($name=__DIR__.'/classes/'.strtolower($className).'.php')){
include($name);
}
});
if (is_dir(__DIR__.'/classes'))return;
mkdir(__DIR__.'/classes');
function generateRandomString($length = 10) {
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
$times['start_file_write'] = microtime(TRUE);
$names = [];
for ($i=0;$i<500;$i++){
$className = generateRandomString(10);
$file = __DIR__.'/classes/'.strtolower($className).'.php';
if (file_exists($file)){
$i = $i-1;
continue;
}
$code = "<?php \n\n".'class '.$className.' {}'."\n\n ?>";
file_put_contents($file,$code);
$names[] = strtoupper($className);
}
$times['begin_get_declared_classes_benchmark'] = microtime(TRUE);
$propers = [];
// foreach($names as $index => $name){
// $wrongCaseName = strtoupper($name);
// class_exists($wrongCaseName); //so it gets autoloaded if not already done
// $classes = get_declared_classes();
// $map = array_combine(array_map('strtolower',$classes),$classes);
// $proper = $map[strtolower($wrongCaseName)];
// if ($index%20===0){
// $times['intermediate_bench_'.$index] = microtime(TRUE);
// }
// $propers[] = $proper;
// }
// the above commented lines are the get_declared_classes() method.
// the foreach below is for reflection.
foreach ($names as $index => $name){
$className = strtoupper($name);
$reflection = new ReflectionClass($className);
if ($index%20===0){
$times['intermediate_bench_'.$index] = microtime(TRUE);
}
$propers[] = $reflection->getName();
}
$times['end_get_declared_classes_benchmark'] = microtime(TRUE);
$start = $times['begin'];
$bench = $times['begin_get_declared_classes_benchmark'];
$lastTime = 0;
foreach($times as $key => $time){
echo "\nTime since begin:".($time-$start);
echo "\nTime since last: ".($time-$lastTime)." key was {$key}";
echo "\nTime since bench start: ".($time - $bench);
$lastTime = $time;
}
print_r($times);
print_r($propers);
exit;
```
### Results
```
// get_declared_classes method
//Time since bench start: 0.052499055862427 is total time for processing get_declared_classes w/ $i=500
//Time since bench start: 0.047168016433716
// last bench time Time since begin:0.062150955200195
// 100 intermediate bench: Time since bench start: 0.0063230991363525
// 200 : Time since bench start: 0.015070915222168
// 300 intermediate bench: Time since bench start: 0.02455997467041
// 400 intermediate bench: Time since bench start: 0.033944129943848
// 480 : Time since bench start: 0.044310092926025
//reflection method:
//Time since bench start: 0.01493501663208
//Time since bench start: 0.017416954040527
// 100 intermediate: Time since bench start: 0.0035450458526611
// 200 intermediate: Time since bench start: 0.0066778659820557
// 300 intermediate: Time since bench start: 0.010055065155029
// 400 intermediate: Time since bench start: 0.014182090759277
// 480 intermediate: Time since bench start: 0.01679801940918
```
#### Results' notes
- "Time since bench start" is the entire time it took to run all the iterations. I share this twice above.
- "100 Intermediate" (200, 300, etc) are actually the results at 120, 220, etc... I messed up in copy+pasting results & didn't want to do it again. Yes. I'm lazy :)
- The results would of course vary between runs of the code, but it's pretty clear that the reflection option is significantly faster.
- All was run on a localhost server on an Acer laptop.
- PHP Version 7.2.19-0ubuntu0.19.04.1 (from `php info()`)
如上所示,我可以提交文章并且一切都按预期工作 - 保存到数据库和一切。最后一行,如果我将php info() 更改为phpinfo()(删除空格),我会收到此错误:
不可接受!
在此服务器上找不到所请求资源的适当表示。此错误是由 Mod_Security 生成的。
当我尝试使用phpinfo()(没有空格)提交时,我的 PHP 根本没有执行,我只收到这个错误。 Firefox 中的网络选项卡显示状态代码为“406 Not Acceptable”。 $_SERVER['DOCUMENT_ROOT'].'/error_log' 的错误日志中没有写入任何内容,无论如何,这是所有 PHP 错误记录的地方。在我的主文件夹中,有一个 logs 文件夹,但它仍然是空的。 /etc/ 或 /etc/my_website_name.com 也没有登录。
什么可能导致这个问题? PHP.ini 中有什么我可以改变的吗? .htaccess 会影响这个吗?
至少,我该如何解决这个问题?
疑难解答
- 我可以提交一篇文章,正文中只包含 - PHP 版本 7.2.19-0ubuntu0.19.04.1(来自 `phpinfo()`)。
- 如果我删除 phpinfo() 并向帖子正文添加更多内容(提交的总数据更多),它会起作用
- 放置一个空格,例如
php info()使其工作,这也是帖子当前的存在方式。 - 我不知道还能做什么
我现在正在使用Simple MDE,但在我开始使用 Simple MDE 之前,它曾在其他多个场合发生过。只有相对较大的帖子也包含代码。
我正在与 HostGator 共享主机,使用 HTTPS:// 和 PHP 7.2.19
【问题讨论】:
-
您需要查看 Apache 错误日志。如果您无权访问,则需要让您的托管服务提供商寻找您。
-
Mod_Security 可能被配置为阻止
phpinfo,这可能是您在他的情况下遇到的问题。我可能会与 HostGator 核对。如果他们阻止它,至少不会让我感到惊讶。 -
我在使用 Hostgator 时遇到了类似的问题,但在我的情况下,如果我的任何 POST 字段包含两个
>字符,我会收到与您相同的 406 错误...... ;)
标签: php apache post mod-security http-status-code-406