PHP Classes

File: phon/PHONValidator.php

Recommend this page to a friend!
  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: 15 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;
    }
}