diff --git a/NEWS b/NEWS index 51a42359..8bd8c3b6 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,8 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier the $config and $context member variables . HTML wrapper in DOMLex now takes DTD identifiers into account; doesn't really make a difference, but is good for completeness sake +. merge-library.php script refactored for greater code reusability and + PHP4 compatibility 2.1.1, released 2007-08-04 - Fix show-stopper bug in %URI.MakeAbsolute functionality diff --git a/maintenance/common.php b/maintenance/common.php index d5437b77..c38d351b 100644 --- a/maintenance/common.php +++ b/maintenance/common.php @@ -1,5 +1,7 @@ + * @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 + * @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); + } + + +} + + diff --git a/maintenance/compat-function-file-put-contents.php b/maintenance/compat-function-file-put-contents.php new file mode 100644 index 00000000..bf8effc9 --- /dev/null +++ b/maintenance/compat-function-file-put-contents.php @@ -0,0 +1,107 @@ +, Arpad Ray + * @link http://php.net/function.file_put_contents + * @author Aidan Lister + * @version $Revision: 1.27 $ + * @internal resource_context is not supported + * @since PHP 5 + * @require PHP 4.0.0 (user_error) + */ +function php_compat_file_put_contents($filename, $content, $flags = null, $resource_context = null) +{ + // If $content is an array, convert it to a string + if (is_array($content)) { + $content = implode('', $content); + } + + // If we don't have a string, throw an error + if (!is_scalar($content)) { + user_error('file_put_contents() The 2nd parameter should be either a string or an array', + E_USER_WARNING); + return false; + } + + // Get the length of data to write + $length = strlen($content); + + // Check what mode we are using + $mode = ($flags & FILE_APPEND) ? + 'a' : + 'wb'; + + // Check if we're using the include path + $use_inc_path = ($flags & FILE_USE_INCLUDE_PATH) ? + true : + false; + + // Open the file for writing + if (($fh = @fopen($filename, $mode, $use_inc_path)) === false) { + user_error('file_put_contents() failed to open stream: Permission denied', + E_USER_WARNING); + return false; + } + + // Attempt to get an exclusive lock + $use_lock = ($flags & LOCK_EX) ? true : false ; + if ($use_lock === true) { + if (!flock($fh, LOCK_EX)) { + return false; + } + } + + // Write to the file + $bytes = 0; + if (($bytes = @fwrite($fh, $content)) === false) { + $errormsg = sprintf('file_put_contents() Failed to write %d bytes to %s', + $length, + $filename); + user_error($errormsg, E_USER_WARNING); + return false; + } + + // Close the handle + @fclose($fh); + + // Check all the data was written + if ($bytes != $length) { + $errormsg = sprintf('file_put_contents() Only %d of %d bytes written, possibly out of free disk space.', + $bytes, + $length); + user_error($errormsg, E_USER_WARNING); + return false; + } + + // Return length + return $bytes; +} + + +// Define +if (!function_exists('file_put_contents')) { + function file_put_contents($filename, $content, $flags = null, $resource_context = null) + { + return php_compat_file_put_contents($filename, $content, $flags, $resource_context); + } +} diff --git a/maintenance/merge-library.php b/maintenance/merge-library.php index 2d73824b..50eedb78 100755 --- a/maintenance/merge-library.php +++ b/maintenance/merge-library.php @@ -19,7 +19,29 @@ assertCli(); $GLOBALS['loaded'] = array('HTMLPurifier.php' => true); /** - * @param $text Text to replace includes from + * Custom FSTools for this script that overloads some behavior + * @warning The overloading of copy() is not necessarily global for + * this script. Watch out! + */ +class MergeLibraryFSTools extends FSTools +{ + function copyable($entry) { + // Skip hidden files + if ($entry[0] == '.') { + return false; + } + return true; + } + function copy($source, $dest) { + copy_and_remove_includes($source, $dest); + } +} +$FS = new MergeLibraryFSTools(); + +/** + * Replaces the includes inside PHP source code with the corresponding + * source. + * @param string $text PHP source code to replace includes from */ function replace_includes($text) { return preg_replace_callback( @@ -32,6 +54,8 @@ function replace_includes($text) { /** * Removes leading PHP tags from included files. Assumes that there is * no trailing tag. + * @note This is safe for files that have internal mkdir($base . $dir); + } file_put_contents($base . $file, ''); } -/** - * Recursively creates a directory - * @note Adapted from the PHP manual comment 76612 - */ -function mkdir_deep($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 - * - * @author Aidan Lister - * @version 1.0.1 - * @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 copy_and_remove_includes($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; - } - // Skip hidden files - if ($entry[0] == '.') { - continue; - } - // Deep copy directories - if ($dest !== "$source/$entry") { - copyr("$source/$entry", "$dest/$entry"); - } - } - // Clean up - $dir->close(); - return true; -} - -/** - * Delete a file, or a folder and its contents - * - * @author Aidan Lister - * @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 - rmdirr($dirname . DIRECTORY_SEPARATOR . $entry); - } - - // Clean up - $dir->close(); - return rmdir($dirname); -} - /** * Copies the contents of a directory to the standalone directory + * @param string $dir Directory to copy */ function make_dir_standalone($dir) { - return copyr($dir, 'standalone/' . $dir); + global $FS; + return $FS->copyr($dir, 'standalone/' . $dir); } +/** + * Copies the contents of a file to the standalone directory + * @param string $file File to copy + */ function make_file_standalone($file) { - mkdir_deep('standalone/' . dirname($file)); + global $FS; + $FS->mkdir('standalone/' . dirname($file)); copy_and_remove_includes($file, 'standalone/' . $file); return true; } +/** + * Copies a file to another location recursively, if it is a PHP file + * remove includes + * @param string $file Original file + * @param string $sfile New location of file + */ function copy_and_remove_includes($file, $sfile) { $contents = file_get_contents($file); if (strrchr($file, '.') === '.php') $contents = replace_includes($contents); @@ -174,8 +114,13 @@ function copy_and_remove_includes($file, $sfile) { */ function replace_includes_callback($matches) { $file = $matches[1]; - // PHP 5 only file / PEAR files - $preserve = array('HTMLPurifier/Lexer/DOMLex.php'=>1, 'HTMLPurifier/Printer.php'=>1, 'XML/HTMLSax3.php'=>1); + $preserve = array( + // PHP 5 only + 'HTMLPurifier/Lexer/DOMLex.php' => 1, + 'HTMLPurifier/Printer.php' => 1, + // PEAR (external) + 'XML/HTMLSax3.php' => 1 + ); if (isset($preserve[$file])) { return $matches[0]; } @@ -200,16 +145,22 @@ file_put_contents('HTMLPurifier.standalone.php', $contents); echo ' done!' . PHP_EOL; echo 'Creating standalone directory...'; -rmdirr('standalone'); // ensure a clean copy -mkdir_deep('standalone/HTMLPurifier/DefinitionCache/Serializer'); +$FS->rmdirr('standalone'); // ensure a clean copy + +// data files +$FS->mkdir('standalone/HTMLPurifier/DefinitionCache/Serializer'); make_dir_standalone('HTMLPurifier/EntityLookup'); + +// non-standard inclusion setup make_dir_standalone('HTMLPurifier/Language'); + +// optional components make_file_standalone('HTMLPurifier/Printer.php'); make_dir_standalone('HTMLPurifier/Printer'); make_dir_standalone('HTMLPurifier/Filter'); -make_file_standalone('HTMLPurifier/Lexer/PEARSax3.php'); // not incl by default +make_file_standalone('HTMLPurifier/Lexer/PEARSax3.php'); + // PHP 5 only files make_file_standalone('HTMLPurifier/Lexer/DOMLex.php'); make_file_standalone('HTMLPurifier/Lexer/PH5P.php'); echo ' done!' . PHP_EOL; -