0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-12-22 08:21:52 +00:00

Initial commit for extra class hierarchies FSTools and ConfigSchema.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1518 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang 2008-01-27 05:31:06 +00:00
parent 4fe6661c64
commit 2598b26778
14 changed files with 394 additions and 143 deletions

4
NEWS
View File

@ -17,6 +17,10 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
HTMLPurifier.php is insufficient--in such cases include HTMLPurifier.autoload.php HTMLPurifier.php is insufficient--in such cases include HTMLPurifier.autoload.php
as well to register our autoload handler (or modify your autoload function as well to register our autoload handler (or modify your autoload function
to check HTMLPurifier_Bootstrap::getPath($class)). to check HTMLPurifier_Bootstrap::getPath($class)).
! Extra utility classes for testing and non-library operations can
be found in extras/. Specifically, these are FSTools and ConfigSchema.
You may find a use for these in your own project, but right now they
are highly experimental.
- Autoclose now operates iteratively, i.e. <span><span><div> now has - Autoclose now operates iteratively, i.e. <span><span><div> now has
both span tags closed. both span tags closed.
. Plugins now get their own changelogs according to project conventions. . Plugins now get their own changelogs according to project conventions.

View File

@ -0,0 +1,10 @@
<?php
/**
* Parses directive files, building up object representation of
* configuration directives for runtime use or for documentation.
*/
class ConfigSchema_DirectiveParser
{
}

152
extras/FSTools.php Normal file
View File

@ -0,0 +1,152 @@
<?php
/**
* Filesystem tools not provided by default; can recursively create, copy
* and delete folders. Some template methods are provided for extensibility.
*
* @note This class must be instantiated to be used, although it does
* not maintain state.
*/
class FSTools
{
private static $singleton;
/**
* Returns a global instance of FSTools
*/
static public function singleton() {
if (empty(FSTools::$singleton)) FSTools::$singleton = new FSTools();
return FSTools::$singleton;
}
/**
* Sets our global singleton to something else; useful for overloading
* functions.
*/
static public function setSingleton($singleton) {
FSTools::$singleton = $singleton;
}
/**
* Recursively creates a directory
* @param string $folder Name of folder to create
* @note Adapted from the PHP manual comment 76612
*/
public function mkdirr($folder) {
$folders = preg_split("#[\\\\/]#", $folder);
$base = '';
for($i = 0, $c = count($folders); $i < $c; $i++) {
if(empty($folders[$i])) {
if (!$i) {
// special case for root level
$base .= DIRECTORY_SEPARATOR;
}
continue;
}
$base .= $folders[$i];
if(!is_dir($base)){
$this->mkdir($base);
}
$base .= DIRECTORY_SEPARATOR;
}
}
/**
* Copy a file, or recursively copy a folder and its contents; modified
* so that copied files, if PHP, have includes removed
* @note Adapted from http://aidanlister.com/repos/v/function.copyr.php
*/
public function copyr($source, $dest) {
// Simple copy for a file
if (is_file($source)) {
return $this->copy($source, $dest);
}
// Make destination directory
if (!is_dir($dest)) {
$this->mkdir($dest);
}
// Loop through the folder
$dir = $this->dir($source);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
if (!$this->copyable($entry)) {
continue;
}
// Deep copy directories
if ($dest !== "$source/$entry") {
$this->copyr("$source/$entry", "$dest/$entry");
}
}
// Clean up
$dir->close();
return true;
}
/**
* Overloadable function that tests a filename for copyability. By
* default, everything should be copied; you can restrict things to
* ignore hidden files, unreadable files, etc. This function
* applies to copyr().
*/
public function copyable($file) {
return true;
}
/**
* Delete a file, or a folder and its contents
* @note Adapted from http://aidanlister.com/repos/v/function.rmdirr.php
*/
public function rmdirr($dirname)
{
// Sanity check
if (!$this->file_exists($dirname)) {
return false;
}
// Simple delete for a file
if ($this->is_file($dirname) || $this->is_link($dirname)) {
return $this->unlink($dirname);
}
// Loop through the folder
$dir = $this->dir($dirname);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
// Recurse
$this->rmdirr($dirname . DIRECTORY_SEPARATOR . $entry);
}
// Clean up
$dir->close();
return $this->rmdir($dirname);
}
/**
* Recursively globs a directory.
*/
public function globr($dir, $pattern, $flags = NULL) {
$files = $this->glob("$dir/$pattern", $flags);
foreach ($this->glob("$dir/*", GLOB_ONLYDIR) as $sub_dir) {
$sub_files = $this->globr($sub_dir, $pattern, $flags);
$files = array_merge($files, $sub_files);
}
return $files;
}
/**
* Allows for PHP functions to be called and be stubbed.
* @warning This function will not work for functions that need
* to pass references; manually define a stub function for those.
*/
public function __call($name, $args) {
return call_user_func_array($name, $args);
}
}

85
extras/FSTools/File.php Normal file
View File

@ -0,0 +1,85 @@
<?php
/**
* Represents a file in the filesystem
*/
class FSTools_File
{
/** Filename of file this object represents */
protected $name;
/** Handle for the file */
protected $handle = false;
/** Instance of FSTools for interfacing with filesystem */
protected $fs;
/**
* Filename of file you wish to instantiate.
* @note This file need not exist
*/
public function __construct($name, $fs = false) {
$this->name = $name;
$this->fs = $fs ? $fs : FSTools::singleton();
}
/** Returns the filename of the file. */
public function getName() {return $this->name;}
/** Returns directory of the file without trailing slash */
public function getDirectory() {return $this->fs->dirname($this->name);}
/**
* Retrieves the contents of a file
* @todo Throw an exception if file doesn't exist
*/
public function get() {
return $this->fs->file_get_contents($this->name);
}
/** Writes contents to a file, creates new file if necessary */
public function write($contents) {
return $this->fs->file_put_contents($this->name, $contents);
}
/** Deletes the file */
public function delete() {
return $this->fs->unlink($this->name);
}
/** Returns true if file exists and is a file. */
public function exists() {
return $this->fs->is_file($this->name);
}
/** Returns last file modification time */
public function getMTime() {
return $this->fs->filemtime($this->name);
}
/**
* Chmod a file
* @note We ignore errors because of some weird owner trickery due
* to SVN duality
*/
public function chmod($octal_code) {
return @$this->fs->chmod($this->name, $octal_code);
}
/** Opens file's handle */
public function open($mode) {
if ($this->handle) $this->close();
$this->handle = $this->fs->fopen($this->name, $mode);
return true;
}
/** Closes file's handle */
public function close() {
if (!$this->handle) return false;
$this->fs->fclose($this->handle);
$this->handle = false;
return true;
}
}

View File

@ -0,0 +1,9 @@
<?php
/**
* This is a stub include that automatically configures the include path.
*/
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifierExtras.php';
require_once 'HTMLPurifierExtras.autoload.php';

View File

@ -0,0 +1,7 @@
<?php
if (function_exists('spl_autoload_register')) {
spl_autoload_register(array('HTMLPurifierExtras', 'autoload'));
} elseif (!function_exists('__autoload')) {
function __autoload($class) {return HTMLPurifierExtras::autoload($class);}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* Meta-class for HTML Purifier's extra class hierarchies, similar to
* HTMLPurifier_Bootstrap.
*/
class HTMLPurifierExtras
{
public static function autoload($class) {
$path = HTMLPurifierExtras::getPath($class);
if (!$path) return false;
require $path;
}
public static function getPath($class) {
if (
strncmp('FSTools', $class, 7) !== 0 &&
strncmp('ConfigSchema', $class, 12) !== 0
) return false;
// Custom implementations can go here
// Standard implementation:
return str_replace('_', '/', $class) . '.php';
}
}

View File

@ -17,7 +17,7 @@ class HTMLPurifier_Bootstrap
public static function autoload($class) { public static function autoload($class) {
$file = HTMLPurifier_Bootstrap::getPath($class); $file = HTMLPurifier_Bootstrap::getPath($class);
if (!$file) return false; if (!$file) return false;
require_once $file; require $file;
return true; return true;
} }
@ -26,10 +26,12 @@ class HTMLPurifier_Bootstrap
*/ */
public static function getPath($class) { public static function getPath($class) {
if (strncmp('HTMLPurifier', $class, 12) !== 0) return false; if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
// Custom implementations
if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) { if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
$code = str_replace('_', '-', substr($class, 22)); $code = str_replace('_', '-', substr($class, 22));
return 'HTMLPurifier/Language/classes/' . $code . '.php'; return 'HTMLPurifier/Language/classes/' . $code . '.php';
} }
// Standard implementation
return str_replace('_', '/', $class) . '.php'; return str_replace('_', '/', $class) . '.php';
} }

View File

@ -8,145 +8,5 @@ set environment variable PHP_IS_CLI to work around this).';
} }
} }
/** // Load useful stuff like FSTools
* Filesystem tools not provided by default; can recursively create, copy require_once '../extras/HTMLPurifierExtras.auto.php';
* and delete folders. Some template methods are provided for extensibility.
* @note This class must be instantiated to be used, although it does
* not maintain state.
*/
class FSTools
{
/**
* Recursively creates a directory
* @param string $folder Name of folder to create
* @note Adapted from the PHP manual comment 76612
*/
function mkdir($folder) {
$folders = preg_split("#[\\\\/]#", $folder);
$base = '';
for($i = 0, $c = count($folders); $i < $c; $i++) {
if(empty($folders[$i])) {
if (!$i) {
// special case for root level
$base .= DIRECTORY_SEPARATOR;
}
continue;
}
$base .= $folders[$i];
if(!is_dir($base)){
mkdir($base);
}
$base .= DIRECTORY_SEPARATOR;
}
}
/**
* Copy a file, or recursively copy a folder and its contents; modified
* so that copied files, if PHP, have includes removed
*
* @author Aidan Lister <aidan@php.net>
* @version 1.0.1-modified
* @link http://aidanlister.com/repos/v/function.copyr.php
* @param string $source Source path
* @param string $dest Destination path
* @return bool Returns TRUE on success, FALSE on failure
*/
function copyr($source, $dest) {
// Simple copy for a file
if (is_file($source)) {
return $this->copy($source, $dest);
}
// Make destination directory
if (!is_dir($dest)) {
mkdir($dest);
}
// Loop through the folder
$dir = dir($source);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
if (!$this->copyable($entry)) {
continue;
}
// Deep copy directories
if ($dest !== "$source/$entry") {
$this->copyr("$source/$entry", "$dest/$entry");
}
}
// Clean up
$dir->close();
return true;
}
/**
* Stub for PHP's built-in copy function, can be used to overload
* functionality
*/
function copy($source, $dest) {
return copy($source, $dest);
}
/**
* Overloadable function that tests a filename for copyability. By
* default, everything should be copied; you can restrict things to
* ignore hidden files, unreadable files, etc.
*/
function copyable($file) {
return true;
}
/**
* Delete a file, or a folder and its contents
*
* @author Aidan Lister <aidan@php.net>
* @version 1.0.3
* @link http://aidanlister.com/repos/v/function.rmdirr.php
* @param string $dirname Directory to delete
* @return bool Returns TRUE on success, FALSE on failure
*/
function rmdirr($dirname)
{
// Sanity check
if (!file_exists($dirname)) {
return false;
}
// Simple delete for a file
if (is_file($dirname) || is_link($dirname)) {
return unlink($dirname);
}
// Loop through the folder
$dir = dir($dirname);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
// Recurse
$this->rmdirr($dirname . DIRECTORY_SEPARATOR . $entry);
}
// Clean up
$dir->close();
return rmdir($dirname);
}
/**
* Recursively globs a directory.
*/
function globr($dir, $pattern, $flags = NULL) {
$files = glob("$dir/$pattern", $flags);
foreach (glob("$dir/*", GLOB_ONLYDIR) as $sub_dir) {
$sub_files = $this->globr($sub_dir, $pattern, $flags);
$files = array_merge($files, $sub_files);
}
return $files;
}
}

View File

@ -0,0 +1,7 @@
<?php
class ConfigSchema_DirectiveParserTest extends UnitTestCase
{
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Test harness that sets up a filesystem sandbox for file-emulation
* functions to safely unit test in.
*
* @todo Make an automatic FSTools mock or something
*/
class FSTools_FileSystemHarness extends UnitTestCase
{
protected $dir, $oldDir;
function __construct() {
parent::UnitTestCase();
$this->dir = 'tmp/' . md5(uniqid(rand(), true)) . '/';
mkdir($this->dir);
$this->oldDir = getcwd();
}
function __destruct() {
FSTools::singleton()->rmdirr($this->dir);
}
function setup() {
chdir($this->dir);
}
function tearDown() {
chdir($this->oldDir);
}
}

View File

@ -0,0 +1,35 @@
<?php
require_once 'FSTools/FileSystemHarness.php';
class FSTools_FileTest extends FSTools_FileSystemHarness
{
function test() {
$name = 'test.txt';
$file = new FSTools_File($name);
$this->assertFalse($file->exists());
$file->write('foobar');
$this->assertTrue($file->exists());
$this->assertEqual($file->get(), 'foobar');
$file->delete();
$this->assertFalse($file->exists());
}
function testGetNonExistent() {
$name = 'notfound.txt';
$file = new FSTools_File($name);
$this->expectError();
$this->assertFalse($file->get());
}
function testHandle() {
$file = new FSTools_File('foo.txt');
$this->assertFalse($file->exists());
$file->open('w');
$this->assertTrue($file->exists());
$file->close();
}
}

View File

@ -32,6 +32,19 @@ htmlpurifier_parse_args($AC, $aliases);
// clean out cache if necessary // clean out cache if necessary
if ($AC['flush']) shell_exec('php ../maintenance/flush-definition-cache.php'); if ($AC['flush']) shell_exec('php ../maintenance/flush-definition-cache.php');
// setup our own autoload for earlier PHP versions
if (!function_exists('spl_autoload_register')) {
function __autoload($class) {
return // we're using the fact that once one OR is true, the rest is skipped
HTMLPurifier_Bootstrap::autoload($class) ||
HTMLPurifierExtras::autoload($class);
}
}
// initialize and load alternative classes
require_once '../extras/HTMLPurifierExtras.auto.php';
// initialize and load HTML Purifier // initialize and load HTML Purifier
// use ?standalone to load the alterative standalone stub // use ?standalone to load the alterative standalone stub
if ($AC['standalone']) { if ($AC['standalone']) {

View File

@ -130,3 +130,10 @@ if ($csstidy_location) {
// ... none yet // ... none yet
// FSTools auxiliary library
$test_files[] = 'FSTools/FileTest.php';
// ConfigSchema auxiliary library
$test_files[] = 'ConfigSchema/DirectiveParserTest.php';