PHPAPIExtension

Top > PluginDev > PHPAPIExtension
Table of Contents

PHP API Extension

get_basename, get_dirname

if (! function_exists('get_basename')) {
    /**
     * Get the basename of a path
     *
     * PHP's basename works as
     *  'Page/' => 'Page', 'Page/a' => 'a', 'Page' => 'Page'
     * This function works as
     *  'Page/' => '', 'Page/a' => 'a', 'Page' => 'Page'
     *
     * @param string $path
     * @param string $suffix cut suffix of the basename
     * @return string basename
     */
    function get_basename($path, $suffix = '')
    {
        if (($pos = strrpos($path, '/')) !== false) {
            $basename = substr($path, $pos + 1);
        } else {
            $basename = $path;
        }
        if (($pos = strrpos($basename, $suffix)) !== false) {
            $basename = substr($basename, 0, $pos);
        }
        return $basename;
    }
}
if (! function_exists('get_dirname')) {
    /**
     * Get the dirname of a path
     *
     * PHP's dirname works as
     *  'Page/' => '.', 'Page/a' => 'Page', 'Page' => '.'
     * This function works as
     *  'Page/' => 'Page', 'Page/a' => 'Page', 'Page' => ''
     *
     * @param string $path
     * @return string dirname
     */
    function get_dirname($path)
    {
        if (($pos = strrpos($path, '/')) !== false) {
            return substr($path, 0, $pos);
        } else {
            return '';
        }
    }
}

unhtmlspecialchars, glue_str, glue_url, realurl, unrealpath

if (! function_exists('unhtmlspecialchars')) {
    /**
     * Undo htmlspecialchars
     *
     * @access public
     * @param string 
     * @return string Undone htmlspecialchars
     * @see htmlspecialchars()
     * @example unhtmlspecialchars.php
     */
    function &unhtmlspecialchars($string)
    {
        $string = str_replace('&' , '&' , $string);
        $string = str_replace(''', '\'', $string);
        $string = str_replace('"', '"', $string);
        $string = str_replace('&lt;'  , '<' , $string);
        $string = str_replace('&gt;'  , '>' , $string);
        return $string;
    }
}
/**
 * reverse parse_str
 *
 * Note: parse_str does rawurldecode and convert . into _ for keys
 *
 * @access public
 * @param array $queries outputs by parse_str
 * @return string reversed parse_str
 * @see parse_str()
 */
function glue_str($queries)
{
    if (! is_array($queries))
        return false;
    
    $url_query = array();
    foreach ($queries as $key => $value) {
        $arg = ($value === '') ? rawurlencode($key) : 
            rawurlencode($key) . '=' . rawurlencode($value);
        array_push($url_query, $arg);
    }
    return implode('&', $url_query);
}
if (! function_exists('glue_url')) {
    /**
     * reverse parse_url
     *
     * @access public
     * @param array $parsed outputs by parse_url
     * @return string reversed parse_url
     * @see parse_url()
     */
    function glue_url($parsed) 
    {
        if (!is_array($parsed)) return false;
        $uri = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '' : '//') : '';
        $uri .= isset($parsed['user']) ? $parsed['user'].(isset($parsed['pass']) ? ':'.$parsed['pass'] : '').'@' : '';
        $uri .= isset($parsed['host']) ? $parsed['host'] : '';
        $uri .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
        if(isset($parsed['path'])) {
            $uri .= (substr($parsed['path'], 0, 1) == '/') ? $parsed['path'] : ('/'.$parsed['path']);
        }
        $uri .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
        $uri .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
        return $uri;
    }
}

uses glue_url

if (! function_exists('realurl')) {
    /**
     * Get absolute URL
     *
     * @access public
     * @param string $base base url
     * @param string $url relative url
     * @return string absolute url
     * @see parse_url()
     * @see realpath()
     * @uses glue_url()
     */
    function realurl($base, $url)
    {
        if (! strlen($base)) return $url;
        if (! strlen($url)) return $base;
        
        if (preg_match('!^[a-z]+:!i', $url)) return $url;
        $base = parse_url($base);
        if ($url{0} == "#") { 
            // fragment
            $base['fragment'] = substr($url, 1);
            return glue_url($base);
        }
        unset($base['fragment']);
        unset($base['query']);
        if (substr($url, 0, 2) == "//") {
            // FQDN
            $base = array(
                'scheme'=>$base['scheme'],
                'path'=>substr($url,2),
            );
            return glue_url($base);
        } elseif ($url{0} == "/") {
            // absolute path reference
            $base['path'] = $url;
        } else {
            // relative path reference
            $path = explode('/', $base['path']);
            $url_path = explode('/', $url);
            // drop file from base
            array_pop($path);
            // append url while removing "." and ".." from
            // the directory portion
            $end = array_pop($url_path);
            foreach ($url_path as $segment) {
                if ($segment == '.') {
                    // skip
                } elseif ($segment == '..' && $path && $path[sizeof($path)-1] != '..') {
                    array_pop($path);
                } else {
                    $path[] = $segment;
                }
            }
            // remove "." and ".." from file portion
            if ($end == '.') {
                $path[] = '';
            } elseif ($end == '..' && $path && $path[sizeof($path)-1] != '..') {
                $path[sizeof($path)-1] = '';
            } else {
                $path[] = $end;
            }
            $base['path'] = join('/', $path);
        }
        return glue_url($base);
    }
}

ToDo: look unrealpath http://us.php.net/manual/ja/function.realpath.php

if (! function_exists('unrealpath')) {
    /**
     * Get relative path from a source path to a target path
     *
     * @static
     * @param string $source
     * @param string $target
     * @return string 
     */
    function unrealpath($source, $target)
    {
        $source_dirs = explode('/', $source);
        $target_dirs = explode('/', $target);
        foreach ($source_dirs as $i => $source_dir) {
            if ($source_dirs[$i] == $target_dirs[$i]) {
                unset($source_dirs[$i]);
                unset($target_dirs[$i]);
            } else {
                break;
            }
        }
        $source = implode('/', $source_dirs);
        $target = implode('/', $target_dirs);
        $relative = str_repeat('../', substr_count($source, '/'));
        return $relative . $target;
    }
}

mythrow, mycatch

    /*
     * Error Handling in PHP4
     * throw an error message
     *
     * PHP5)
     * function a_function_throw_inside()
     * {
     *   throw(new Exception('Throw Error'));
     * }
     * try {
     *   a_function_throw_inside();
     *   echo 'Never Executed';
     * } catch (Exception $e) {
     *   echo $e->getMessage() . "\n";
     * }
     *
     * This)
     * function a_function_throw_inside()
     * {
     *   sonots::mythrow('Throw Error'); return;
     * }
     * do {
     *   a_function_throw_inside();
     *   if (sonots::mycatch()) break; // burdensome
     *   echo 'Never Executed';
     * } while (FALSE);
     * if (sonots::mycatch()) {
     *   echo sonots::mycatch();
     * }
     *
     * @param string $errmsg
     * @global $php_errmsg
     * @return void
     */
    function mythrow($errmsg)
    {
        set_error_handler(create_function('$errno, $errstr, $errfile, $errline', 
                                          '$GLOBALS["php_errmsg"] = $errstr;'));
        @trigger_error($errmsg, E_USER_ERROR);
        restore_error_handler();
    }
    /**
     * Error Handling in PHP4
     * catch an error message
     *
     * @return string $errmsg
     * @global $php_errmsg
     */
    function mycatch()
    {
        global $php_errmsg;
        return $php_errmsg;
    }

set_members, get_members

    /**
     * Set members into array of class objects recursively
     *
     * @access static
     * @param array $objects array of objects
     * @param string $name member name
     * @param array $members array of member variables, 
     *   size and keys must be same with $objects.
     * @return void
     */
    function set_members(&$objects, $name, &$members)
    {
        foreach ($objects as $i => $object) {
            $objects[$i]->$name = $members[$i];
        }
    }
    /**
     * Get specific members from array of class objects
     *
     * @access static
     * @param array $objects array of objects
     * @param string $name member name
     * @return array array of members, keys are preserved. 
     */
    function &get_members(&$objects, $name)
    {
        $array = array();
        foreach ($objects as $i => $object) {
            $array[$i] = $object->$name;
        }
        return $array;
    }

parse_interval, conv_interval

  /**
   * Parse an interval num string
   *
   * Example)
   *  1:5   means 1st to 5th returns array(0, 5)
   *  2:    means 2nd to end returns array(1, NULL)
   *  :3    means 1st to 2rd returns array(0, 3)
   *  4     means 4th returns array(3, 1)
   *  -5:   means last 5th to end returns array(-5, NULL)
   *  :-5   means 1st to last 5th returns array(0, -4)
   *  1+2   means 1st to 3rd returns array(0, 3)
   *
   * @access static
   * @param string $interval
   * @return mixed array($offset, $length) or NULL
   * @see array_slice, array_splice
   */
function parse_interval($interval)
{
    $mini = 1; 
    if (strpos($interval, ':') !== FALSE) {
        list($min, $max) = explode(':', $interval, 2);
        if (is_numeric($min)) {
            $min = (int)$min;
        } else {
            $min = $mini;
        }
        if (is_numeric($max)) {
            $max = (int)$max;
            $len = $max - $mini + 1;
            if ($len == -1) $len = NULL;
            if ($len < 0) $len++;
        } else {
            $len = NULL;
        }
    } elseif (strpos($interval, '+') !== FALSE) {
        list($min, $len) = explode("+", $interval, 2);
        if (is_numeric($min)) {
            $min = (int)$min;
        } else {
            $min = $mini;
        }
        if (is_numeric($len)) {
            $len = (int)$len + 1;
        } else {
            $len = NULL;
        }
    } else {
        if (is_numeric($interval)) {
            $min = (int)$interval;
            $len = 1;
        } else {
            return NULL;
            //$min = $mini;
            //$max = $maxi;
        }
    }
    if ($min > 0) $min--;
    return array($min, $len);
}
/*
print_r(parse_interval('1:5'));
print_r(parse_interval('2:'));
print_r(parse_interval(':3'));
print_r(parse_interval('4'));
print_r(parse_interval('-5:'));
print_r(parse_interval(':-5'));
print_r(parse_interval('1+2'));
list($offset, $length) = parse_interval('2:-1');
if (is_null($length)) {
    print_r(array_slice(range(1,10), $offset));
} else {
    print_r(array_slice(range(1,10), $offset, $length));
}
*/
/**
 * Convert ($offset, $length) interval form
 *   to ($start, $end) interval form.
 *
 * Example)
 *  Assume min = 1, max = 10
 *  array(0, 5) to array(1, 5)
 *  array(1, NULL) to array(2, 10)
 *  array(3, 1) to array(4, 4)
 *  array(-5, NULL) to array(6, 10)
 *  array(0, -4) to array(1, 6)
 *
 * @access static
 * @param int $offset
 * @param int $length
 * @param int $min
 * @param int $max
 * @return array array($start, $end)
 * @see range
 */
function conv_interval($offset, $length, $min, $max)
{
    // minus means index from back
    if ($offset < 0) {
        $start = $offset + $max + 1;
    } else {
        $start = $offset + $min;
    }
    // minus means length from back
    if ($length < 0) {
        $end = $length + $max;
    } elseif ($length > 0) {
        $end = $length + $start - 1;
    } else {
        $end = $max;
    }
    // make sure
    if (! isset($start) || $start < $min) {
        $start = $min;
    }
    if (! isset($end) || $end > $max) {
        $end = $max;
    }
    return array($start, $end);
}
/*
print_r(conv_interval(0, 5, 1, 10));
print_r(conv_interval(1, NULL, 1, 10));
print_r(conv_interval(3, 1, 1, 10));
print_r(conv_interval(-5, NULL, 1, 10));
print_r(conv_interval(0, -4, 1, 10));
*/

array_to_string, string_to_array, trim_array

if (! function_exists('array_to_string')) {
    /**
     * Convert an array to a string
     * 
     * Example
     *
     * $arr = array('A', 'B', 'indC' => 'C', array('D', 'E'), 'indF'=>'F');
     * echo array_to_string($arr);
     * 
     * Output:
     * A,B,indC:C,(D,E),indF:F
     *
     * @access static
     * @param array $array
     * @param string $hashsep A character to be used as a hash key and val seperator
     * @param string $elemsep A character to be used as a elememt separator
     * @param string $openarray A character to be used as an open bracket of an array
     * @param string $closearray A character to be used as a close bracket of an array
     * @return string
     * @see string_to_array
     */
    function array_to_string($array, 
                             $hashsep = ':', $elemsep = ',', $openarray = '(', $closearray = ')')
    {
        $string = "";
        foreach($array as $key => $value){
            if(is_array($value)){
                $value = self::array_to_string($value, $hashsep, $elemsep, $openarray, $closearray);
                $value = $openarray . $value . $closearray;
            } else { // escape
                $value = urlencode($value);
            }
            if (is_int($key)) {
                $string .= $elemsep . $value;
            } else {
                $string .= $elemsep . urlencode($key) . $hashsep . $value;
            }
        }
        $string = substr($string, 1);
        return $string;
    }
}
if (! function_exists('string_to_array')) {
    /**
     * Restore a string to an array
     * 
     * Example
     *
     * $string = 'A,B,indC:C,(0:D,1:E),indF:F'
     * $array = string_to_array($string)
     * 
     * Output:
     * array('A', 'B', 'indC' => 'C', array('D', 'E'), 'indF'=>'F');
     * 
     *
     * @param string $string
     * @param string $hashsep A character to be used as a hash key and val seperator
     * @param string $elemsep A character to be used as a elememt separator
     * @param string $openarray A character to be used as an open bracket of an array
     * @param string $closearray A character to be used as a close bracket of an array
     * @return array
     * @see array_to_string
     */
    function string_to_array($string, $hashsep = ':', $elemsep = ',', 
                             $openarray = '(', $closearray = ')')
    {
        /// parse the first element
        $result = array();
        $hashsep_pos = strpos($string, $hashsep);
        $elemsep_pos = strpos($string, $elemsep);
        $openarray_pos = strpos($string, $openarray);
        // there is a key or not for the 1st element
        if ($hashsep_pos !== FALSE &&
            ($elemsep_pos === FALSE || $hashsep_pos < $elemsep_pos) &&
            ($openarray_pos === FALSE || $hashsep_pos < $openarray_pos)) {
            $key =  urldecode(substr($string, 0, $hashsep_pos));
            $string = trim(substr($string , $hashsep_pos+1));
        } else {
            $key = NULL;
        }
        $openarray_pos = strpos($string, $openarray);
        if ($openarray_pos === FALSE || $openarray_pos > 0) { // hash val is not an array
            $elemsep_pos = strpos($string, $elemsep);
            if ($elemsep_pos === FALSE) {
                $val = urldecode($string);
                $string = "";
            }else{
                $val = urldecode(substr($string, 0, $elemsep_pos));
                $string = substr($string, $elemsep_pos+1);
            }
        } elseif ($openarray_pos == 0) { // hash val is an array
            $string = substr($string, 1);
            $num_openarray = 1;
            // search where is a corresponding closet
            $string_char_array = str_split($string);
            for($index = 0; count($string_char_array); $index++) {
                if ($string_char_array[$index] == $openarray) {
                    $num_openarray++;
                }else if ($string_char_array[$index] == $closearray) {
                    $num_openarray--;
                }
                if ($num_openarray == 0) {
                    break;
                }
            }
            $val = string_to_array(substr($string, 0, $index), 
                $hashsep, $elemsep, $openarray, $closearray);
            $string = substr($string, $index+2);
        }
        if (is_null($key)) {
            $result[] = $val;
        } else {
            $result[$key] = $val;
        }
        /// next element
        if (strlen($string) != 0) {
            $result = array_merge($result, string_to_array($string,
                $hashsep, $elemsep, $openarray, $closearray));
        }
    
        return $result;
    }
}
    /**
     * trim elements of array
     *
     * @access static
     * @param array $array
     * @param boolean $recursive recursively
     * @param boolean $trimkey trim key too
     * @return array
     */
    function trim_array($array, $recursive = FALSE, $trimkey = FALSE)
    {
        $outarray = array();
        foreach ($array as $key => $val) {
            unset($array[$key]); // save memory
            if ($recursive && is_array($val)) {
                $val = self::trim_array($val, $recursive, $trimkey);
            } elseif (is_string($val)) {
                $val = trim($val);
            }
            if ($trimkey && is_string($key)) {
                $key = trim($key);
            }
            $outarray[$key] = $val;
        }
        return $outarray;
    }

ereg_grep

if (! function_exists('ereg_grep')) {  
    /**
     * Grep an array by ereg expression
     *
     * @param string $pattern
     * @param array $input
     * @param int $flags
     * @return array
     */
    if (! defined('EREG_GREP_INVERT')) define('EREG_GREP_INVERT', PREG_GREP_INVERT);
    function &ereg_grep($pattern, $input, $flags = 0)
    {
        if ($flag & EREG_GREP_INVERT) {
            foreach ($input as $i => $string) {
                if (ereg($pattern, $string)) {
                    unset($input[$i]); // unset rather than stack for memory saving
                }
            }
        } else {
            foreach ($input as $i => $string) {
                if (! ereg($pattern, $string)) {
                    unset($input[$i]);
                }
            }
        }
        return $input;
    }
}

deep_copy, is_ref, (array_deep_copy)

if (! function_exists('deep_copy')) {
    /**
     * Deep copy 
     *
     * PHP Extension
     *
     * @param array $source
     * @return array
     */
    function deep_copy($source)
    {
        return unserialize(serialize($source));
    }
}
if (! function_exists('is_ref')) {
    /**
     * is reference?
     *
     * PHP Extension
     *
     * @param object &$a
     * @param object &$b
     * @return boolean
     */
    function is_ref(&$a, &$b){

        if(gettype($a) !== gettype($b)) return false;

        $same = false;
        if(is_array($a)){

            //Look for an unused index in $a
            $key = uniqid("is_ref_", true);
            while(isset($a[$key]))$key = uniqid("is_ref_", true);

            //The two variables differ in content ... They can't be the same
            if(isset($b[$key])) return false;

            //The arrays point to the same data if changes are reflected in $b
            $data = uniqid("is_ref_data_", true);
            $a[$key] =& $data;
            //There seems to be a modification ...
            $same = ((isset($b[$key])) and ($b[$key] === $data));

            //Undo our changes ...
            unset($a[$key]);

        }elseif(is_object($a)){

            //The same objects are required to have equal class names ;-)
            if(get_class($a) !== get_class($b)) return false;

            //Look for an unused property in $a
            $key = uniqid("is_ref_", true);
            while(isset($a->$key))$key = uniqid("is_ref_", true);

            //The two variables differ in content ... They can't be the same
            if(isset($b->$key)) return false;

            //The arrays point to the same data if changes are reflected in $b
            $data = uniqid("is_ref_data_", true);
            $a->$key =& $data;
            //There seems to be a modification ...
            $same = ((isset($b->$key)) and ($b->$key === $data));

            //Undo our changes ...
            unset($a->$key);

        }elseif(is_resource($a)){

            if(get_resource_type($a) !== get_resource_type($b))return false;
            $same = ((string) $var1) === ((string) $var2);

        }else{

            if($a !== $b) return false;

            //To check for a reference of a variable with simple type
            //simply store its old value and check against modifications of the second variable ;-)

            $data = uniqid("is_ref_", true);
            while($data === $a) $data = uniqid("is_ref_", true);

            $save = $a;             //WE NEED A COPY HERE!!!
            $a    = $data;          //Set a to the value of $data (copy)
            $same = ($a === $b);    //Check if $var2 was modified too ...
            $a    = $save;          //Undo our changes ...

        }
        return $same;
    }
}

old version. Use deep_copy now

if (! function_exists('array_deep_copy')) {
    /**
     * Deep copy of an array
     *
     * PHP Extension
     *
     * @param array &$source
     * @param array &$target
     * @param integer $maxdepth maxdepth to deeply copy
     * @param integer $depth parameter used in recursive call (you will not use)
     * @uses is_ref
     */
    function array_deep_copy(&$source, &$target, $maxdepth = 50, $depth = 0)
    {
        if($depth > $maxdepth) { 
            $target = $source; 
            return; 
        }
        if(! is_array($target)) {
            $target = array();
        }
        foreach($source as $k => $v) {
            if(is_array($v) && ! is_ref($source, $v)) {
                array_deep_copy($v,$target[$k],$maxdepth,++$depth);
            } else {
                $target[$k] = $v;
            }
        }
    }
}

Temporary copy of $GLOBALS

       $GLOBALS_TMP = array();
       array_deep_copy($GLOBALS, $GLOBALS_TMP);
       // modify $GLOBALS
       foreach ($GLOBALS as $key => $val) {
           if ($key !== 'GLOBALS') unset($GLOBALS[$key]);
       }
       array_deep_copy($GLOBALS_TMP, $GLOBALS);

No!

       $GLOBALS_TMP = array();
       foreach ($GLOBALS as $key => $val) {
           if ($key !== 'GLOBALS') $GLOBALS_TMP[$key] = $val;
       }
       // modify $GLOBALS
       foreach ($GLOBALS as $key => $val) {
           if ($key !== 'GLOBALS') unset($GLOBALS[$key]);
       }
       foreach ($GLOBALS_TMP as $key => $val) {
           $GLOBALS[$key] = $val;
       }

was enough.

r_strpos

if (! function_exists('r_strpos')) {
    /**
     * Find positions of occurrence of a string
     *
     * PHP Extension
     *
     * @param string $str
     * @param string $substr
     * @return array positions
     */
    function r_strpos($str, $substr)
    {
        $r_pos = array();
        while(true) {
            $pos = strpos($str, $substr);
            if ($pos === false) break;
            array_push($r_pos, $pos);
            $str = substr($str, $pos + 1);
        }
        return $r_pos;
    }
}

sort_filenames

if (! function_exists('sort_filenames')) {
    /**
     * Sort filenames
     *
     * This function assures that files under a directory will be
     * followed by its parent directory as
     *  Foo
     *  Foo/Bar
     *  FooBar
     * not
     *  Foo
     *  FooBar
     *  Foo/Bar
     * This function could be useful when filenames include multi-byte words
     *
     * @param array &$filenames
     */
    function sort_filenames(&$filenames)
    {
        $filenames = str_replace('/', "\0", $filenames);
        sort($filenames, SORT_STRING);
        $filenames = str_replace("\0", '/', $filenames);
    }
}

ただのソートでは

$filenames = array(
"Hogeほげ",
'Hoge/Hoge',
'Hoge');

Hoge
Hogeほげ
Hoge/Hoge

になってしまう(危険性がある。natsort なら上のような結果になる)。Hoge ディレクトリの次は Hoge ディレクトリ内のファイルが来て欲しい。つまり、

Hoge
Hoge/Hoge
Hogeほげ

になってほしい。

function sort_dirtree(&$filenames, $sort = 'sort', $sortflag = SORT_STRING)
{
    $filenames = str_replace('/', "\0", $filenames);
    switch ($sort) {
    case 'natsort':
    case 'natcasesort':
        call_user_func($sort, $filenames);
        break;
    default:
        call_user_func($sort, $filenames, $sortflag);
        break;
    }
    $filenames = str_replace("\0", '/', $filenames);
}

うーん、natsort では期待通りにいかない・・・。

$ignorecase

function sort_filenames(&$filenames, $ignorecase = FALSE)
{
    if ($ignorecase) {
        $tmp = $filenames;
        foreach (array_keys($filenames) as $i) {
            if (is_callable('mb_strtolower')) {
                $filenames[$i] = mb_strtolower($filenames[$i]);
            } else {
                $filenames[$i] = strtolower($filenames[$i]);
            }
        }
        print_r($filenames);
        $filenames = str_replace('/', "\0", $filenames);
        asort($filenames, SORT_STRING);
        $j = 0; $ind = array_keys($filenames); $filenames = array();
        foreach ($ind as $i) {
            $filenames[$j++] = $tmp[$i];
        }
    } else {
        $filenames = str_replace('/', "\0", $filenames);
        sort($filenames, SORT_STRING);
        $filenames = str_replace("\0", '/', $filenames);
    }
}

get_existfiles

if (! function_exists('get_existfiles')) {
    /**
     * Get list of files in a directory
     *
     * PHP Extension
     *
     * @access public
     * @param string $dir Directory Name
     * @param string $ext File Extension
     * @param bool $recursive Traverse Recursively
     * @return array array of filenames
     * @uses is_dir()
     * @uses opendir()
     * @uses readdir()
     */
    function &get_existfiles($dir, $ext = '', $recursive = FALSE) 
    {
        if (($dp = @opendir($dir)) == FALSE)
            return FALSE;
        $pattern = '/' . preg_quote($ext, '/') . '$/';
        $dir = ($dir[strlen($dir)-1] == '/') ? $dir : $dir . '/';
        $dir = ($dir == '.' . '/') ? '' : $dir;
        $files = array();
        while (($file = readdir($dp)) !== false ) {
            if($file != '.' && $file != '..' && is_dir($dir . $file) && $recursive) {
                $files = array_merge($files, get_existfiles($dir . $file, $ext, $recursive));
            } else {
                $matches = array();
                if (preg_match($pattern, $file, $matches)) {
                    $files[] = $dir . $file;
                }
            }
        }
        closedir($dp);
        return $files;
    }
}

is_includable

if (! function_exists('is_includable')) {
    /**
     * Check if file is includable
     *
     * @param string $filename
     * @param boolean $returnpaths return all paths where $filename is includable
     * @return boolean (or array if $returnpaths is true)
     */
    function is_includable($filename, $returnpaths = false) {
        $include_paths = explode(PATH_SEPARATOR, ini_get('include_path'));
        foreach ($include_paths as $path) {
            $include = $path . DIRECTORY_SEPARATOR . $filename;
            if (is_file($include) && is_readable($include)) {
                if ($returnpaths == true) {
                    $includable_paths[] = $path;
                } else {
                    return true;
                }
            }
        }
        return (isset($includeable_paths) && $returnpaths == true) ?
            $includeable_paths : false;
    }
}

include や require は成功、失敗で TRUE, FALSE を返さなず、失敗すると Warning を返すだけなので事前チェックが必要(@ で Warning はとめられるが)。

fopen にだけ bool use_include_path 引数があるようだが、is_file など他にはない模様。 fopen もまた、失敗した場合 Warning を返してしまうので事前チェックが必要でなので、その機能を使った is_includable は作れない。

array_asort_key, array_sort_key

    /**
     * sort array in the given key sequence maintaining key association
     *
     * Example)
     *   $japanese  = array('orange'=>'mikan', 'apple'=>'ringo');
     *   $price = array('orange'=> 100, 'apple'= 50);
     *   asort($price, SORT_NUMERIC); // array('apple'=> 50, 'orange'= 100);
     *   array_asort_key($favor, $price); // array('orange'=>'ringo', 'apple'=>'apple');
     *
     * @param array $array array to be sorted
     * @param array $sorted array having keys in sorting sequence
     *   keys of $array and $sorted must be all common, i.e.,
     *   count($array) == count(array_intersect_key($array, $sorted)
     * @param return void
     */
    function array_asort_key(&$array, &$sorted)
    {
        $outarray = array();
        foreach ($sorted as $key=> $tmp) {
            $outarray[$key] = $array[$key]; // change the pointer sequences
            unset($array[$key]);
        }
        $array = $outarray;
    }
    /**
     * sort array in the given key sequence 
     *
     * Example)
     *   $fruits  = array(0 => 'orange', 1 => 'apple');
     *   $price   = array(0 => 100, 1 => 50);
     *   asort($price, SORT_NUMERIC); // array(1 => 50, 0 => 100)
     *   array_sort_key($favor, $price); // array(0 => 'apple', 1 => 'orange')
     *
     * @param array $array array to be sorted
     * @param array $sorted array having keys in sorting sequence
     *   keys of $array and $sorted must be all common, i.e.,
     *   count($array) == count(array_intersect_key($array, $sorted)
     * @param return void
     */
    function array_sort_key(&$array, &$sorted)
    {
        $outarray = array();
        foreach ($sorted as $key=> $tmp) {
            $outarray[] = $array[$key]; // rebuild array
            unset($array[$key]);
        }
        $array = $outarray;
    }

array_serach_by, in_array_by, max_by

if (! function_exists('array_search_by')) {
    /**
     * array_search by a specific field
     *
     * @param mixed $needle
     * @param array $haystack
     * @param mixed $fieldname
     * @param bool $strict
     * @return mixed the key of detected value or false if not found
     */
    function array_search_by($needle, $haystack, $fieldname = null, $strict = false)
    {
        if ($strict) {
            foreach ($haystack as $key => $val) {
                if ($needle === $val[$fieldname]) {
                    return $key;
                }
            }
        } else {
            foreach ($haystack as $key => $val) {
                if ($needle == $val[$fieldname]) {
                    return $key;
                }
            }
        }
        return false;
    }
}
if (! function_exists('in_array_by')) {
    /**
     * in_array by a specific field
     *
     * @param mixed $needle
     * @param array $haystack
     * @param mixed $fieldname
     * @param bool $strict
     * @return bool
     */
    function in_array_by($needle, $haystack, $fieldname = null, $strict = false)
    {
        if ($strict) {
            foreach ($haystack as $key => $val) {
                if ($needle === $val[$fieldname]) {
                    return true;
                }
            }
        } else {
            foreach ($haystack as $key => $val) {
                if ($needle == $val[$fieldname]) {
                    return true;
                }
            }
        }
        return false;
    }
}
if (! function_exists('max_by')) {
    /**
     * max func by a specific field for array
     *
     * @param array $array
     * @param string $fieldname
     * @return number 
     */
    function max_by($array, $fieldname = null)
    {
        $field_array = array();
        foreach ($array as $i => $befree) {
            $field_array[$i] = $array[$i][$fieldname];
        }
        return max($field_array);
    }
}

sort_by, sort_assoc_by, is_associative_array

後記:array_sort_key で充分かもしれない。

ポインタの並びだけでなく、キー値も 0, 1, 2 … におきかえる。

if (! function_exists('sort_by')) {
    /**
     * Sort an array by a specific field without maintaining key association
     * (replace keys into 0, 1, 2, ....)
     *
     * PHP Extension
     *
     * @access public
     * @param array &$array Input Array
     * @param string $fieldname Field Name
     * @param string $sort Sort Function (sort == asort or rsort == arsort or natsort or natcasesort)
     * @return string $sortflag Sort Flag for asort or arsort such as SORT_REGULAR
     * @uses asort()
     * @uses arsort()
     * @uses natsort()
     * @uses natcasesort()
     */
    function sort_by(&$array,  $fieldname = null, $sort, $sortflag = SORT_REGULAR)
    {
        $field_array = array();
        // store the field values in a seperate array
        foreach ($array as $i => $befree) {
            $field_array[$i] = $array[$i][$fieldname];
        }
        switch ($sort) {
        case 'asort' || 'sort':
            // sort an array and maintain index association...
            asort($field_array, $sortflag);
        break;
        case 'arsort' || 'rsort':
            // sort an array in reverse order and maintain index association
            arsort($field_array, $sortflag);
        break;
        case 'natsort':
            natsort($field_array);
        case 'natcasesort':
            // sort an array using a case insensitive "natural order" algorithm
            natcasesort($field_array);
            break;
        }
        // rebuild the array
        $outarray = array();
        foreach ( $field_array as $i=> $befree) {
            $outarray[] = $array[$i];
            unset($array[$i]);
        }
        $array = $outarray;
    }
}

usort 版

フィールド名が確定しているなら、usort を使うことも可能。 usort はポインタ(配列の並び順)だけではなく、キー番号も書き換える(完璧に作り直す)ので同等。

function filetimecmp($a, $b)
{
    $atime = $a['timestamp'];
    $btime = $a['timestamp'];
    if ($atime == $btime) {
        return 0;
    }
    return ($atime < $btime) ? -1 : 1;
}
usort( $array, 'filetimecmp' ); // array($this, 'filetimecmp');

フィールド名を変数にしたい。

function &sort_by(&$array,  $fieldname = null)
{
    function cmp($a, $b)
    {
        $a = $a[$fieldname];
        $b = $a[$fieldname];
        if ($a == $b) return 0;
        return ($a < $b) ? - 1 : 1;
    }
    usort( $array, 'cmp' );
    return $array;
}

しかし、関数 cmp は関数 sort_by の中だけで使用できる関数というわけではないので、だめ。

ならば、と create_function を使用したいところだが、関数から抜けたときに、create_function 用のメモリを開放するわけではないらしい。なので、呼び出すたびにたまっていき、メモリリーク発生。

よって、フィールド名が決まっている場合のみ、usort でも可能。

sort_by_multi

multisort works to sort by two fields as SQL

// Obtain a list of columns
foreach ($data as $key => $row) {
   $volume[$key]  = $row['volume'];
   $edition[$key] = $row['edition'];
}

// Sort the data with volume descending, edition ascending
// Add $data as the last parameter, to sort by the common key
array_multisort($volume, SORT_DESC, $edition, SORT_ASC, $data);

後記:array_asort_key で充分かもしれない。

/**
 * Sort an array by a specific field maintaining key association
 *
 * PHP Extension
 *
 * @access public
 * @param array &$array Input Array
 * @param string $fieldname Field Name
 * @param string $sort Sort Function (sort == asort or rsort == arsort or natsort or natcasesort)
 * @return string $sortflag Sort Flag for asort or arsort such as SORT_REGULAR
 * @uses asort()
 * @uses arsort()
 * @uses natsort()
 * @uses natcasesort()
 */
function sort_assoc_by(&$array,  $fieldname = null, $sort, $sortflag = SORT_REGULAR)
{
    $field_array = array();
    # store the keyvalues in a seperate array
    foreach ($array as $i => $befree) {
        $field_array[$i] = $array[$i][$fieldname];
    }
    switch ($sort) {
    case 'asort' || 'sort':
        # sort an array and maintain index association...
        asort($field_array, $sortflag);
        break;
    case 'arsort' || 'rsort':
        # sort an array in reverse order and maintain index association
        arsort($field_array, $sortflag);
        break;
    case 'natsort':
        natsort($field_array);
    case 'natcasesort':
        # sort an array using a case insensitive "natural order" algorithm
        natcasesort($field_array);
    break;
    }
    # rebuild the old array
    $outarray = array();
    foreach ( $field_array as $key=> $befree) {
        $outarray[$key] = $array[$key]; // change the pointer sequences
        unset($array[$key]);
    }
    $array = $outarray;
}

Example

$arr = array();
$arr['a'] = array('time'=>20);
$arr['b'] = array('time'=>10);
sort_assoc_by($arr, 'time', 'asort');
print_r($arr);
/*
Array
(
    [b] => Array
        (
            [time] => 10
        )

    [a] => Array
        (
            [time] => 20
        )

)
*/

sort_key

natsort, natcasesort にも対応した ksort (krsort)

function sort_key(&$array, $sort, $sortflag = SORT_REGULAR)
{
    $key_array = array_keys($array);
    switch ($sort) {
    case 'asort' || 'sort':
        # sort an array and maintain index association...
        asort($key_array, $sortflag);
        break;
    case 'arsort' || 'rsort':
        # sort an array in reverse order and maintain index association
        arsort($key_array, $sortflag);
        break;
    case 'natsort':
        natsort($key_array);
    case 'natcasesort':
        # sort an array using a case insensitive "natural order" algorithm
        natcasesort($key_array);
    break;
    }
    # rebuild the old array
    $outarray = array();
    foreach ( $key_array as $i => $key) {
        $outarray[$key] = $array[$key]; // change the pointer sequences
        unset($array[$key]);
    }
    $array = $outarray;
}

Example

$arr = array();
$arr['b'] = 1;
$arr['a'] = 2;
sort_key($arr, 'sort');
print_r($arr);
/*
Array
(
    [a] => 2
    [b] => 1
)
*/
if (! function_exists('is_associative_array')) {
    /**
     * Check if an array is an associative array
     *
     * PHP Extension
     *
     * @param array $array
     * @return boolean
     */
    function is_associative_array($array) 
    {
        if (!is_array($array) || empty($array))
            return false;
        $keys = array_keys($array);
        return array_keys($keys) !== $keys;
        // or
        //return is_array($array) && !is_numeric(implode(array_keys($array)));
    }
}

move

if (! function_exists('move')) {
    /**
     * Move a file (rename does not overwrite if $newname exists on Win, but move does)
     *
     * @param string $oldname
     * @param string $newname
     * @return boolean
     */
    function move($oldname, $newname) {
        if (! rename($oldname, $newname)) {
            if (copy ($oldname, $newname)) {
                unlink($oldname);
                return TRUE;
            }
            return FALSE;
        }
        return TRUE;
    }
}

http_filename

未完成?

function http_filename($url) {
    $headers = get_headers($url, 1);
    static $loop = 0;
    while (isset($headers['Location']) && $loop++ < 4) {
        $url = $headers['Location'];
        $headers = get_headers($url, 1);
    }
    if (isset($headers['Content-Disposition'])) {
        $matches = array();
        if (preg_match('/; ?filename="?([^"]+)"?;?/', $headers['Content-Disposition'], $matches)) {
            return $matches[1];
        }
    }
    if (isset($headers['Content-Type'])) {
        $matches = array();
        if (preg_match('/; ?name="?([^"]+)"?;?/', $headers['Content-Type'], $matches)) {
            return $matches[1];
        }
    }
    $parse = parse_url($url);
    return basename($parse['path']);
}

extract_mb

function extract_mb(&$str)
{
    $matches = array();
    preg_match_all('/[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+/u', $str, $matches);
    return $matches;
}

検索ワードをエンコードしないとだめだよな・・・

http://phpspot.org/blog/archives/2005/11/php_17.html

英語部分と日本語部分を分離して、pukiwiki の autolink の処理をわけたりすると楽しそう。 #現状は auto というページがあると <a href="xxx">auto</a>link のようになるが、英語なら部分一致ではなく、英単語単位で、とやればこんな変なオートリンクはされずにすむ。

array_minus_plus

use array_diff twice

if (! function_exists('array_minus_plus')) {
    /**
     * Get difference between two arrays (minus elements and plus elements)
     *
     * @param array $oldarray
     * @param array $newarray
     * @return array array((array)$minus, (array)$plus)
     */
    function array_minus_plus($oldarray, $newarray)
    {
        $common = array_intersect($oldarray, $newarray);
        $minus  = array_diff($oldarray, $common);
        $plus   = array_diff($newarray, $common);
        return array($minus, $plus);
    }
}
$minus = array_diff($oldarray, $newarray);
$plus  = array_diff($newarray, $oldarray);

でいいか。

file_head

from pukiwiki

if (! function_exists('file_head')) {
    /*
     * Reads heads of file into an array
     *
     * @param string  $file filename
     * @param int     $count number of executed fgets, usually equivalent to number of lines
     * @param boolean $lock use lock or not 
     * @param int     $buffer number of bytes to be read in one fgets
     * @return array
     */
    function file_head($file, $count = 1, $lock = TRUE, $buffer = 8192)
    {
        $array = array();
        
        $fp = @fopen($file, 'r');
        if ($fp === FALSE) return FALSE;
        set_file_buffer($fp, 0);
        if ($lock) @flock($fp, LOCK_SH);
        rewind($fp);
        $index = 0;
        while (! feof($fp)) {
            $line = fgets($fp, $buffer);
            if ($line != FALSE) $array[] = $line;
            if (++$index >= $count) break;
        }
        if ($lock) @flock($fp, LOCK_UN);
        if (! fclose($fp)) return FALSE;
        
        return $array;
    }
}

csv_explode, csv_implode

from pukiwiki

// Explode Comma-Separated Values to an array
function csv_explode($separator, $string)
{
        $retval = $matches = array();

        $_separator = preg_quote($separator, '/');
        if (! preg_match_all('/("[^"]*(?:""[^"]*)*"|[^' . $_separator . ']*)' .
            $_separator . '/', $string . $separator, $matches))
                return array();

        foreach ($matches[1] as $str) {
                $len = strlen($str);
                if ($len > 1 && $str{0} == '"' && $str{$len - 1} == '"')
                        $str = str_replace('""', '"', substr($str, 1, -1));
                $retval[] = $str;
        }
        return $retval;
}
// Test...
$csv_str = 'a,"b",c,"this \"should\" work","and ""also"" this"';
echo "test: <pre>".print_r( csv_explode($csv_str), true )."</pre>";

from pukiwiki

// Implode an array with CSV data format (escape double quotes)
function csv_implode($glue, $pieces)
{
        $_glue = ($glue != '') ? '\\' . $glue{0} : '';
        $arr = array();
        foreach ($pieces as $str) {
                if (ereg('[' . $_glue . '"' . "\n\r" . ']', $str))
                        $str = '"' . str_replace('"', '""', $str) . '"';
                $arr[] = $str;
        }
        return join($glue, $arr);
}

PHP Compat

MyException

hmmm? not good

if(! class_exists('MyException')){
	class MyException {
		var $_message = '';
		var $_code = 0;
		var $_line = 0;
		var $_file = '';
		var $_trace = NULL;

		function MyException($message = 'Unknown exception', $code = 0){
			$this->_message = $message;
			$this->_code = $code;
			$this->_trace = debug_backtrace();
			$x = array_shift($this->_trace);
			$this->_file = $x['file'];
			$this->_line = $x['line'];
		}

		function __construct($message = 'Unknown exception', $code = 0){
			$this->MyException($message, $code);
		}

		function getMessage(){
			return $this->_message;
		}
		function getCode(){
			return $this->_code;
		}
		function getFile(){
			return $this->_file;
		}
		function getLine(){
			return $this->_line;
		}
		function getTrace(){
			return $this->_trace;
		}
		function getTraceAsString(){
			$s = '';
			foreach($this->_trace as $i=>$item){
				foreach($item['args'] as $j=>$arg)
					$item['args'][$j] = print_r($arg, TRUE);
				$s .= "#$i " . (isset($item['class']) ? $item['class'] . $item['type'] : '') . $item['function']
                    . '(' . implode(', ', $item['args']) . ") at [$item[file]:$item[line]]\n";
			}
			return $s;
		}
		function printStackTace(){
			echo $this->getTraceAsString();
		}
		function toString(){
			return $this->getMessage();
		}
		function __toString(){
			return $this->toString();
		}
    }
 }
if (! function_exists('MyException_throw')) {
    function MyException_throw($exception) {
        $level = error_reporting( E_ALL );
        @trigger_error( serialize($exception) );
        error_reporting ( $level );
    }
}
if (! function_exists('MyException_catch')) {
    function MyException_catch($classname) {
        global $php_errormsg;
        $exception = unserialize($php_errormsg);
        if (class_of($exception) == $classname) {
            return $exception;
        }
        return NULL;
    }
}

create_clone

if (! function_exists('create_clone')) {
    /**
     * Create a clone of object
     *
     * PHP Compat
     *
     * @param object $object
     * @return object cloned object (has same reference/address)
     */
    function create_clone($object) {
        if (version_compare(phpversion(), '5.0') < 0) {
            return $object;
        } else {
            return @clone($object);
        }
    }
}

_

if (! function_exists('_')) {
    /**
     * i18n gettext
     *
     * PHP Compat
     *
     * @param string $str
     * @return string
     */
    function _($str)
    {
        return $str;
    }
}

file_put_contents

if (! function_exists('file_put_contents')) {
    if (! defined('FILE_APPEND')) define('FILE_APPEND', 8);
    if (! defined('FILE_USE_INCLUDE_PATH')) define('FILE_USE_INCLUDE_PATH', 1);
    /**
     * Write a string to a file (PHP5 has this function)
     *
     * PHP Compat
     *
     * @param string $filename
     * @param string $data
     * @param int $flags
     * @return int the amount of bytes that were written to the file, or FALSE if failure
     */
    function file_put_contents($filename, $data, $flags = 0)
    {
        $mode = ($flags & FILE_APPEND) ? 'a' : 'w';
        $fp = fopen($filename, $mode);
        if ($fp === false) {
            return false;
        }
        if (is_array($data)) $data = implode('', $data);
        if ($flags & LOCK_EX) flock($fp, LOCK_EX);
        $bytes = fwrite($fp, $data);
        if ($flags & LOCK_EX) flock($fp, LOCK_UN);
        fclose($fp);
        return $bytes;
    }
}

get_headers

if(!function_exists('get_headers'))
{
   function get_headers($url, $format = 0)
   {
       $url = parse_url($url);
       $end = "\r\n\r\n";
       $url['port'] = empty($url['port']) ? 80 : $url['port'];
       if (($fp = @fsockopen($url['host'], $url['port'], $errno, $errstr, 30)) === FALSE) {
           return FALSE;
       }
       $req  = "GET ".@$url['path']."?".@$url['query']." HTTP/1.1\r\n";
       $req .= "Host: ".@$url['host'].':'.$url['port']."\r\n";
       $req .= "Connection: close\r\n";
       $req .= "\r\n";
       $response  = '';
       if (fwrite($fp, $req) === FALSE) {
           fclose($fp);
           return FALSE;
       }
       while (! feof($fp)) {
           $response .= fgets($fp, 1280);
           if(strpos($response, $end)) {
               break;
           }
       }
       fclose($fp);

       $response = preg_replace("/\r\n\r\n.*\$/", '', $response);
       $response = explode("\r\n", $response);
       if ($format) {
           foreach($response as $i => $val) {
               if(preg_match('/^([a-zA-Z -]+): +(.*)$/', $val, $matches)) {
                   unset($response[$i]);
                   $response[$matches[1]] = $matches[2];
               }
           }
       }
       return $response;
   }
}

r_mkdir

if (! function_exists('r_mkdir')) {
    /**
     * mkdir recursively (mkdir of PHP5 has recursive flag)
     *
     * @param string $dir
     * @param int $mode
     * @return boolean success or failure
     */
    function r_mkdir($dir, $mode = 0755)
    {
        if (is_dir($dir) || @mkdir($dir,$mode)) return TRUE;
        if (! r_mkdir(dirname($dir),$mode)) return FALSE;
        return @mkdir($dir,$mode);
    }
}