Php 验证上载文件的内容
我正在开发一个“即插即用”系统,在这个系统中,单个组件可以使用应用程序GUI注册并与上传的文件关联 但要真正做到“即插即用”,应用程序必须识别组件,因为每个组件都是一个类,所以我可以通过使用接口来实现这一点 但是我如何验证上传文件中搜索特定界面的内容呢 我的第一个想法是使用标记器,但事实证明这比我想象的要难。一个简单的测试组件文件,如下所示:Php 验证上载文件的内容,php,validation,upload,tokenize,Php,Validation,Upload,Tokenize,我正在开发一个“即插即用”系统,在这个系统中,单个组件可以使用应用程序GUI注册并与上传的文件关联 但要真正做到“即插即用”,应用程序必须识别组件,因为每个组件都是一个类,所以我可以通过使用接口来实现这一点 但是我如何验证上传文件中搜索特定界面的内容呢 我的第一个想法是使用标记器,但事实证明这比我想象的要难。一个简单的测试组件文件,如下所示: <?php class ValidComponent implements Serializable { public serializ
<?php
class ValidComponent implements Serializable {
public serialize() {}
public unserialize( $serialized ) {}
}
免责声明:
所以,你想建立一个“系统”,用户可以上传PHP文件,而不是反过来被所说的系统使用
除非您完全信任用户,或者在系统100%信任上传者的环境中使用,就像在开发环境中一样,这是非常不安全的
使用标记器自己解析文件:
这就是说,分析php文件而不运行它的最好的、可能也是唯一稍微明智的方法是使用标记器
例如,如果您只想知道该文件是否包含实现预定接口的类:
$source = file_get_contents('file.php');
$tokens = token_get_all($source);
function startsWithOpenTag($tokens)
{
return ($tokens[0][0] === T_OPEN_TAG);
}
function searchForInterface($tokens, $interfaceName)
{
$i = 0;
foreach ($tokens as $tk) {
if (isset($tk[1]) && strtolower($tk[1]) === 'implements') {
for ($ii = $i; $ii < count($tokens); ++$ii) {
if ($tokens[$ii] === '{') {
break;
} else {
if (isset($tokens[$ii][1]) && $tokens[$ii][2] === $interfaceName) {
return true;
}
}
}
}
++$i;
}
return false;
}
var_dump(startsWithOpenTag($tokens));
var_dump(searchForInterface($tokens, 'Serializable'));
sandbox.php
额外:
github上有几个PHP沙盒:
尽管如此,这些项目似乎不是很活跃…除非您完全信任用户,或者在系统100%信任上传者的环境中使用,比如在开发环境中,这是非常不安全的…最初我是这个应用程序的唯一管理员,一旦添加了所有组件,我可能只使用它们的副本或引用。它不像WordPress插件。我真的很喜欢第三种方法,听起来更简单,因为应用程序不会在内部网中运行。我不能保证我可以访问系统功能,所以我想我会继续使用Tokenizer。我很欣赏这个代码片段,我会很快测试它,我会给你一个反馈。我讨厌标记器。。。需要这么多嵌套循环。。。最后,我修改了您的解决方案,以查找类名而不是接口,并将使用反射。非常感谢。
$source = file_get_contents('file.php');
$tokens = token_get_all($source);
function startsWithOpenTag($tokens)
{
return ($tokens[0][0] === T_OPEN_TAG);
}
function searchForInterface($tokens, $interfaceName)
{
$i = 0;
foreach ($tokens as $tk) {
if (isset($tk[1]) && strtolower($tk[1]) === 'implements') {
for ($ii = $i; $ii < count($tokens); ++$ii) {
if ($tokens[$ii] === '{') {
break;
} else {
if (isset($tokens[$ii][1]) && $tokens[$ii][2] === $interfaceName) {
return true;
}
}
}
}
++$i;
}
return false;
}
var_dump(startsWithOpenTag($tokens));
var_dump(searchForInterface($tokens, 'Serializable'));
$sandBoxWrapperPath = realpath('sandbox.php');
$uploadedFile = realpath('file.php');
$className = "\ValidComponent";
$command = "php \"$sandBoxWrapperPath\" -f \"$uploadedFile\" -c \"$className\"";
$descriptorspec = array(
1 => array("pipe", "w"), // STDOUT
2 => array("pipe", "w") // STDERR
);
$phpSandBox = proc_open($command, $descriptorspec, $pipes);
if (is_resource($phpSandBox)) {
$stdOut = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stdErr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$exitCode = proc_close($phpSandBox);
echo "STDOUT: " . $stdOut . PHP_EOL . PHP_EOL;
echo "STDERR: " . $stdErr . PHP_EOL . PHP_EOL;
}
$shortopts = "";
$shortopts .= "f:"; // Uploaded File
$shortopts .= "c:"; // Name of the class, with namespace
$opts = getopt($shortopts);
if (!isset($opts['f'])) {
exit('File parameter is required');
}
// Instead, you can use tokenizer to pre parse the file.
// For instance, you can find class name this way
if (!isset($opts['c'])) {
exit('Class parameter is required');
}
$file = $opts['f'];
$className = $opts['c'];
require $file;
$refClass = new ReflectionClass($className);
//Do stuff with reflection