Login   Register  
PHP Classes
elePHPant
Icontem

File: phon/PHONValidator.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Martin Alterisio  >  PHON  >  phon/PHONValidator.php  >  Download  
File: phon/PHONValidator.php
Role: Class source
Content type: text/plain
Description: File the for PHONValidator class
Class: PHON
Unserialize values exported with var_export
Author: By
Last change:
Date: 6 years ago
Size: 4,418 bytes
 

Contents

Class file image Download
<?php
/**
 * File the for PHONValidator class.
 * @package PHON
 */

/**
 * An utility class that validates PHON, preventing possible
 * security threats that could come from executing unvalidated
 * PHON data. It won't check that the PHON represents valid PHP
 * code, just that there is no harmful code.
 * @package PHON
 */
final class PHONValidator {
    
/**
     * Singleton instance.
     * @var PHONValidator
     */
    
private static $instance;
    
    
/**
     * Singleton access.
     * @return PHONValidator Singleton instance.
     */
    
public static function getInstance() {
        if (!
self::$instance) {
            
self::$instance = new PHONValidator();
        }
        return 
self::$instance;
    }
    
    
/**
     * An array of classes associated with a boolean flag which
     * indicates if this value can be safely created from PHON data.
     * If the class is not in the array it must be checked through
     * Reflection.
     * @var array
     */
    
private $classes;
    
    
/**
     * Constructor.
     */
    
private function __construct() {
        
$this->classes = array();
    }
    
    
/**
     * Indicates if a class can be safely created from PHON data.
     * @param string $classname The class name.
     * @return bool True if the class can be safely created from PHON data.
     */
    
public function isSecureClass($classname) {
        if (!isset(
$this->classes[$classname])) {
            
$class = new ReflectionClass($classname);
            
$this->classes[$classname] = $class->implementsInterface('SecurePHONClass');
        }
        return 
$this->classes[$classname];
    }
    
    
/**
     * Checks if PHON data can be safely executed as PHP code.
     * @param string $phon The PHON data.
     * @return bool True if the PHON data is secure.
     */
    
public function isSecure($phon) {
        
$tokens token_get_all("<?php $phon");
        
// we were forced to add the first token, so we remove it
        
array_shift($tokens);
        for (
$i 0$i count($tokens); $i++) {
            
$token $tokens[$i];
            
// check one char secure tokens
            
if ($token === ',') continue;
            if (
$token === '(') continue;
            if (
$token === ')') continue;
            
// no other one char token is secure
            
if (!is_array($token)) return false;
            
// whitespace and comments are ok
            
if ($token[0] === T_WHITESPACE) continue;
            if (
$token[0] === T_COMMENT) continue;
            if (
$token[0] === T_DOC_COMMENT) continue;
            
// literals are ok
            
if ($token[0] === T_CONSTANT_ENCAPSED_STRING) continue;
            if (
$token[0] === T_LNUMBER) continue;
            if (
$token[0] === T_DNUMBER) continue;
            
// array and => are ok
            
if ($token[0] === T_ARRAY) continue;
            if (
$token[0] === T_DOUBLE_ARROW) continue;
            
// T_STRING must be handled carefully
            
if ($token[0] === T_STRING) {
                
// T_STRING is ok if followed by ::__set_state
                // and is the name of a secure class
                
if ($this->lookAheadSetStateCall($tokens$i)) {
                    if (
$this->isSecureClass($token[1])) continue;
                    return 
false;
                }
                
// T_STRING is ok if represents a boolean literal
                
if ($token[1] === 'true') continue;
                if (
$token[1] === 'false') continue;
                return 
false;
            }
            
// otherwise, we found something we shouldn't
            
return false
        }
        
// nothing suspicious to report
        
return true;
    }
    
    
/**
     * Checks subsequent tokens for the pattern ::__set_state.
     * @param array $tokens The list of tokens.
     * @param int $i The index of the current token.
     *               If the pattern is found, this variable will point
     *               to the place where the __set_state was found.
     * @return bool True if the pattern was found.
     */
    
private function lookAheadSetStateCall($tokens, &$i) {
        
$foundDoubleColon false;
        for (
$j $i+1$j count($tokens); $j++) {
            
$token $tokens[$j];
            
// we do not expect one char tokens
            
if (!is_array($tokens)) return false;
            
// ignore whitespace and comments
            
if ($token[0] === T_WHITESPACE) continue;
            if (
$token[0] === T_COMMENT) continue;
            if (
$token[0] === T_DOC_COMMENT) continue;
            
// look for double colon
            
if (!$foundDoubleColon) {
                if (
$token[0] === T_DOUBLE_COLON) {
                    
$foundDoubleColon true;
                    continue;
                }
                return 
false;
            }
            
// look for __set_state
            
if ($token[0] === T_STRING) {
                if (
$token[1] === '__set_state') {
                    
$i $j;
                    return 
true;
                }
            }
            
// we didn't find what we were looking for
            
return false;
        }
        
// we ran out of tokens
        
return false;
    }
}