Php 验证上载文件的内容

Php 验证上载文件的内容,php,validation,upload,tokenize,Php,Validation,Upload,Tokenize,我正在开发一个“即插即用”系统,在这个系统中,单个组件可以使用应用程序GUI注册并与上传的文件关联 但要真正做到“即插即用”,应用程序必须识别组件,因为每个组件都是一个类,所以我可以通过使用接口来实现这一点 但是我如何验证上传文件中搜索特定界面的内容呢 我的第一个想法是使用标记器,但事实证明这比我想象的要难。一个简单的测试组件文件,如下所示: <?php class ValidComponent implements Serializable { public serializ

我正在开发一个“即插即用”系统,在这个系统中,单个组件可以使用应用程序GUI注册并与上传的文件关联

但要真正做到“即插即用”,应用程序必须识别组件,因为每个组件都是一个类,所以我可以通过使用接口来实现这一点

但是我如何验证上传文件中搜索特定界面的内容呢

我的第一个想法是使用标记器,但事实证明这比我想象的要难。一个简单的测试组件文件,如下所示:

<?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