Code

Added ability to build array of used functions.
authorjanw <janw@594d385d-05f5-0310-b6e9-bd551577e9d8>
Mon, 26 Sep 2005 13:48:26 +0000 (13:48 +0000)
committerjanw <janw@594d385d-05f5-0310-b6e9-bd551577e9d8>
Mon, 26 Sep 2005 13:48:26 +0000 (13:48 +0000)
gen_function_list.php will export php-readable output to include/functions_list.inc.
This will be used by setup function soon.

git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@1422 594d385d-05f5-0310-b6e9-bd551577e9d8

gen_function_list.php [new file with mode: 0755]
include/functions_list.inc [new file with mode: 0644]
include/functions_test.inc [new file with mode: 0755]

diff --git a/gen_function_list.php b/gen_function_list.php
new file mode 100755 (executable)
index 0000000..7b48581
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/php -q
+<?php
+/*
+/*
+ * This code is part of GOsa (https://gosa.gonicus.de)
+ * Copyright (C) 2003 Cajus Pollmeier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* This file builds a list of used php_functions in all files matching $pattern
+ * (walks recursively through current directory but skips dirs matching $skip_dirs).
+ *
+ * 1. builds an array of filenames
+ * 2. get contents from files
+ * 2. fetches user defined functions using content
+ * 3. fetches functions calls using content
+ * 4. checks function calls for matching known functions
+ */
+
+include('include/functions_test.inc');
+
+// Build Array of called functions
+$array= test_defined_functions('./');
+
+// Write List to file
+$file= 'include/functions_list.inc';
+
+// If file does not exist, create it
+if(!file_exists($file)) {
+       touch($file);
+}
+
+// Write Array to file
+if(is_writable($file)) {
+       // Open filehandle
+       $fh= fopen($file,'w');
+       if($fh != null) {
+               fwrite($fh,"<?php\n".var_export($array,true)."\n?>");
+       }
+}
+?>
diff --git a/include/functions_list.inc b/include/functions_list.inc
new file mode 100644 (file)
index 0000000..9013ec2
--- /dev/null
@@ -0,0 +1,264 @@
+<?php
+array (
+  0 => 'print_r',
+  1 => 'test_defined_functions',
+  2 => 'touch',
+  3 => 'fopen',
+  4 => 'fwrite',
+  5 => 'var_export',
+  6 => 'session_start',
+  7 => 'header',
+  8 => 'error_reporting',
+  9 => 'putenv',
+  10 => 'setlocale',
+  11 => 'bindtextdomain',
+  12 => 'textdomain',
+  13 => 'sprintf',
+  14 => 'str_replace',
+  15 => 'gmdate',
+  16 => 'imageAlphaBlending',
+  17 => 'imageSaveAlpha',
+  18 => 'imagesx',
+  19 => 'imagesy',
+  20 => 'imagecopy',
+  21 => 'imagePng',
+  22 => 'mysql_pconnect',
+  23 => 'mysql_select_db',
+  24 => 'mysql_query',
+  25 => 'mysql_fetch_array',
+  26 => 'mysql_result',
+  27 => 'mysql_close',
+  28 => 'tempnam',
+  29 => 'fclose',
+  30 => 'popen',
+  31 => 'fread',
+  32 => 'pclose',
+  33 => 'unlink',
+  34 => 'imagick_blob2image',
+  35 => 'imagick_image2blob',
+  36 => 'preg_match',
+  37 => 'base64_decode',
+  38 => 'preg_replace',
+  39 => 'srand',
+  40 => 'strlen',
+  41 => 'date',
+  42 => 'preg_split',
+  43 => 'trim',
+  44 => 'chr',
+  45 => 'chunk_split',
+  46 => 'is_dir',
+  47 => 'is_writable',
+  48 => 'session_destroy',
+  49 => 'substr',
+  50 => 'tidy_parse_string',
+  51 => 'tidy_clean_repair',
+  52 => 'tidy_error_count',
+  53 => 'tidy_warning_count',
+  54 => 'nl2br',
+  55 => 'intval',
+  56 => 'ImageFontWidth',
+  57 => 'ImageFontHeight',
+  58 => 'imagecolorallocate',
+  59 => 'imagerectangle',
+  60 => 'imagefilledrectangle',
+  61 => 'imagestring',
+  62 => 'imagepng',
+  63 => 'imagedestroy',
+  64 => 'is_int',
+  65 => 'session_id',
+  66 => 'define',
+  67 => 'base64_encode',
+  68 => 'debug_backtrace',
+  69 => 'htmlentities',
+  70 => 'flush',
+  71 => 'dirname',
+  72 => 'set_error_handler',
+  73 => 'ini_set',
+  74 => 'chdir',
+  75 => 'opendir',
+  76 => 'readdir',
+  77 => 'closedir',
+  78 => 'explode',
+  79 => 'microtime',
+  80 => 'split',
+  81 => 'strtoupper',
+  82 => 'syslog',
+  83 => 'md5',
+  84 => 'arsort',
+  85 => 'reset',
+  86 => 'ltrim',
+  87 => 'strstr',
+  88 => 'rtrim',
+  89 => 'array_flip',
+  90 => 'preg_quote',
+  91 => 'func_get_args',
+  92 => 'cups_get_dest_list',
+  93 => 'cups_get_printer_attributes',
+  94 => 'exec',
+  95 => 'session_unregister',
+  96 => 'pow',
+  97 => 'key',
+  98 => 'current',
+  99 => 'array_merge',
+  100 => 'strtolower',
+  101 => 'mt_srand',
+  102 => 'mt_rand',
+  103 => 'mb_substr',
+  104 => 'is_link',
+  105 => 'sort',
+  106 => 'array_reverse',
+  107 => 'is_readable',
+  108 => 'is_file',
+  109 => 'filesize',
+  110 => 'dechex',
+  111 => 'crypt',
+  112 => 'imap_close',
+  113 => 'is_array',
+  114 => 'imap_listmailbox',
+  115 => 'imap_utf7_decode',
+  116 => 'imap_last_error',
+  117 => 'imap_getacl',
+  118 => 'imap_setacl',
+  119 => 'strpos',
+  120 => 'is_integer',
+  121 => 'implode',
+  122 => 'file',
+  123 => 'uniqid',
+  124 => 'htmlspecialchars',
+  125 => 'serialize',
+  126 => 'hexdec',
+  127 => 'gettype',
+  128 => 'get_object_vars',
+  129 => 'is_object',
+  130 => 'mysql_num_fields',
+  131 => 'mysql_field_name',
+  132 => 'mysql_data_seek',
+  133 => 'preg_match_all',
+  134 => 'stripcslashes',
+  135 => 'get_class',
+  136 => 'ldap_connect',
+  137 => 'ldap_set_option',
+  138 => 'ldap_set_rebind_proc',
+  139 => 'ldap_get_values_len',
+  140 => 'ldap_unbind',
+  141 => 'asort',
+  142 => 'function_exists',
+  143 => 'sha1',
+  144 => 'get_declared_classes',
+  145 => 'escapeshellarg',
+  146 => 'get_class_vars',
+  147 => 'array_keys',
+  148 => 'ldap_bind',
+  149 => 'ldap_read',
+  150 => 'ldap_get_entries',
+  151 => 'version_compare',
+  152 => 'shell_exec',
+  153 => 'system',
+  154 => 'ini_get',
+  155 => 'posix_getgrgid',
+  156 => 'xml_parser_create',
+  157 => 'xml_set_object',
+  158 => 'xml_set_element_handler',
+  159 => 'chop',
+  160 => 'xml_error_string',
+  161 => 'xml_get_current_line_number',
+  162 => 'printf',
+  163 => 'kadm5_init_with_password',
+  164 => 'kadm5_chpass_principal',
+  165 => 'kadm5_destroy',
+  166 => 'recode',
+  167 => 'iconv',
+  168 => 'ord',
+  169 => 'time',
+  170 => 'stat',
+  171 => 'array_slice',
+  172 => 'ksort',
+  173 => 'strip_tags',
+  174 => 'getcwd',
+  175 => 'fgets',
+  176 => 'is_callable',
+  177 => 'call_user_func_array',
+  178 => 'array_unique',
+  179 => 'usort',
+  180 => 'substr_count',
+  181 => 'end',
+  182 => 'strtr',
+  183 => 'strftime',
+  184 => 'each',
+  185 => 'array_values',
+  186 => 'array_pop',
+  187 => 'array_count_values',
+  188 => 'array_push',
+  189 => 'array_splice',
+  190 => 'in_array',
+  191 => 'array_shift',
+  192 => 'addslashes',
+  193 => 'rsort',
+  194 => 'is_numeric',
+  195 => 'settype',
+  196 => 'ob_start',
+  197 => 'ob_get_contents',
+  198 => 'ob_end_clean',
+  199 => 'file_exists',
+  200 => 'filemtime',
+  201 => 'urlencode',
+  202 => 'crc32',
+  203 => 'basename',
+  204 => 'array_unshift',
+  205 => 'next',
+  206 => 'round',
+  207 => 'rawurlencode',
+  208 => 'bin2hex',
+  209 => 'preg_grep',
+  210 => 'str_repeat',
+  211 => 'extract',
+  212 => 'preg_replace_callback',
+  213 => 'ucfirst',
+  214 => 'wordwrap',
+  215 => 'substr_replace',
+  216 => 'ceil',
+  217 => 'floor',
+  218 => 'array_map',
+  219 => 'range',
+  220 => 'strtotime',
+  221 => 'mktime',
+  222 => 'parse_url',
+  223 => 'fsockopen',
+  224 => 'fputs',
+  225 => 'token_get_all',
+  226 => 'unserialize',
+  227 => 'realpath',
+  228 => 'strncmp',
+  229 => 'array_search',
+  230 => 'array_key_exists',
+  231 => 'mysql_fetch_row',
+  232 => 'mysql_error',
+  233 => 'utf8_encode',
+  234 => 'file_get_contents',
+  235 => 'stripslashes',
+  236 => 'natcasesort',
+  237 => 'uasort',
+  238 => 'strcasecmp',
+  239 => 'rand',
+  240 => 'imagick_ReadImage',
+  241 => 'imagick_free',
+  242 => 'strrchr',
+  243 => 'sleep',
+  244 => 'array_chunk',
+  245 => 'natsort',
+  246 => 'pg_connect',
+  247 => 'pg_exec',
+  248 => 'pg_close',
+  249 => 'pg_set_client_encoding',
+  250 => 'pg_freeresult',
+  251 => 'getdate',
+  252 => 'is_uploaded_file',
+  253 => 'ldap_start_tls',
+  254 => 'ldap_mod_del',
+  255 => 'ldap_close',
+  256 => 'kadm5_get_principals',
+  257 => 'kadm5_delete_principal',
+  258 => 'kadm5_create_principal',
+  259 => 'join',
+)
+?>
\ No newline at end of file
diff --git a/include/functions_test.inc b/include/functions_test.inc
new file mode 100755 (executable)
index 0000000..713ba63
--- /dev/null
@@ -0,0 +1,470 @@
+<?
+/*
+ * This code is part of GOsa (https://gosa.gonicus.de)
+ * Copyright (C) 2003 Cajus Pollmeier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* This file builds a list of used php_functions in all files matching $pattern
+ * (walks recursively through current directory but skips dirs matching $skip_dirs).
+ *
+ * 1. builds an array of filenames
+ * 2. get contents from files
+ * 2. fetches user defined functions using content
+ * 3. fetches functions calls using content
+ * 4. checks function calls for matching known functions
+ */
+
+/** 
+ * Print status messages if set to true (useful for command-line use).
+ * default: false */
+$verbose= false;
+
+/** 
+ * Print debug messages if set to true.
+ * default: false 
+ */
+$debug= false;
+
+/**
+ * Used by command-line client.
+ */
+function cmd_test_functions($path) {
+  global $verbose;
+  $verbose= true;
+  test_functions($path);
+}
+
+/**
+ * Scans files in $path an checks for calls of functions.
+ * @param $path Path to search for files in (default: "./")
+ * @param $pattern RegEx-Pattern for matching PHP-Files (default: "/.+\.(php[34]?$)|(inc$)/")
+ * @param $skip_dirs RegEx-Pattern for directories to ignore (default: ".svn")
+ * @return Array of used function-names
+ */
+function test_defined_functions($path="",$pattern="",$skip_dirs="") {
+  global $debug,$verbose;
+
+  // Check for empty parameters
+  if(strlen($path)==0) {
+    $path= "./";
+  }
+  
+  if(strlen($pattern)==0) {
+    $pattern= "/.+\.(php[34]?$)|(inc$)/";
+  }
+
+  if(strlen($skip_dirs)==0) {
+    $skip_dirs= ".svn";
+  }
+  
+  // Create file-list
+  $array= read_php_files($path,$pattern,$skip_dirs);
+
+  $functions= array();
+  $user_functions= array();
+  
+  // Scan files for used defined functions
+  foreach($array as $file) {
+    // Build array of user defined functions
+    $user_functions= array_merge($user_functions, extract_user_functions($file));
+  }
+  
+  // Scan files for called functions
+  foreach($array as $file) {
+    // Build array of called functions
+    array_push($functions, extract_php_functions($file));
+  }
+  
+  // Make Array 'flat'
+  $functions= array_values_recursive($functions);
+
+  // remove duplicates
+  $functions= array_unique($functions);
+
+  // remove 'non-real' functions
+  $reduced_functions= array_filter($functions,"filter_meta_functions");
+
+  // remove user-defined functions
+  $reduced_functions= array_diff($reduced_functions, $user_functions);
+
+  // Now the array keys are messed up. Build an array with 'normal' keys (1,2,3,...).
+  $functions= array_values($reduced_functions);
+  
+  return $functions;
+}
+
+/**
+ * Scans files in $path an checks for calls of functions that are undefined.
+ * Edit $debug and $verbose to control output. Default is 'false' for both to disallow
+ * console functionality.
+ * @param $path Path to search for files in (default: "./")
+ * @param $pattern RegEx-Pattern for matching PHP-Files (default: "/.+\.(php[34]?$)|(inc$)/")
+ * @param $skip_dirs RegEx-Pattern for directories to ignore (default: ".svn")
+ * @return Array of invalid function-names (Scalars)
+ */
+function test_functions($path="",$pattern="",$skip_dirs="") {
+  global $debug,$verbose;
+
+  // Check for empty parameters
+  if(strlen($path)==0) {
+    $path= "./";
+  }
+  
+  if(strlen($pattern)==0) {
+    $pattern= "/.+\.(php[34]?$)|(inc$)/";
+  }
+
+  if(strlen($skip_dirs)==0) {
+    $skip_dirs= ".svn";
+  }
+  
+  if($debug || $verbose) {
+    print("Scanning Directory...");
+  }
+
+  // Create file-list
+  $array= read_php_files($path,$pattern,$skip_dirs);
+  if($debug || $verbose) {
+    print("done\n");
+  }
+
+  $functions= array();
+  $user_functions= array();
+  $invalid_functions= array();
+  
+  if($debug || $verbose) {
+    print("Importing user functions...");
+  }
+  
+  // Scan files for used defined functions
+  foreach($array as $file) {
+    // Build array of user defined functions
+    $user_functions= array_merge($user_functions, extract_user_functions($file));
+  }
+  
+  if($debug || $verbose) {
+    print("done\n");
+  }
+  
+  if($debug || $verbose) {
+    print("Checking function calls...");
+  }
+  
+  // Scan files for called functions
+  foreach($array as $file) {
+    // Build array of called functions
+    $functions= extract_php_functions($file);
+    foreach($functions as $key => $value) {
+      foreach($value as $fn_key => $function_name) {
+        if(!check_function($function_name) &&
+           // array_search returns key of entry - this can be 0
+           !is_int(array_search($function_name,$user_functions))
+          ) {
+            array_push($invalid_functions, $function_name);
+        }
+      }
+    }
+  }
+  
+  if($debug || $verbose) {
+    print("done\n");
+  }
+  
+  // remove duplicates
+  $invalid_functions= array_unique($invalid_functions);
+  
+  if($debug || $verbose) {
+    foreach($invalid_functions as $function_name) {
+      print("\nFound invalid function ".$function_name);
+    }
+    print("\n");
+  }
+
+  return $invalid_functions;
+}
+
+/**
+ * @see
+ * @return array of content from PHP-Files scanned
+ */
+function read_php_files($path,$pattern,$skip_dirs) {
+  $result= array();
+  
+  if(is_file($path)) {
+    $file= $path;
+
+    $php_content= "";
+    
+    // Open Filehandle to process
+    $fh= popen("`which php` -w $file", 'r');
+      while(!feof($fh)) {
+        // Read block-wise
+        $php_content.=fread($fh,1024);
+      }
+    
+    // Close Filehandle
+    pclose($fh);
+
+    array_push($result, $php_content);
+  } else {
+   $files= list_directory($path,$pattern,$skip_dirs);
+
+   // Walk through files
+   foreach ($files as $key => $file) {
+     $php_content= "";
+     
+     // Open Filehandle to process
+     $fh= popen("`which php` -w $file", 'r');
+       while(!feof($fh)) {
+         // Read block-wise
+         $php_content.=fread($fh,1024);
+       }
+     
+     // Close Filehandle
+     pclose($fh);
+
+     array_push($result, $php_content);
+   }
+  }
+
+  return $result;
+}
+
+/** Only used as callback function when doing array_filter().
+ * @return true if $function is not meta-function or keyword, false otherwise
+ */
+function filter_meta_functions ($function) {
+  $result= true;
+  if(is_meta_function($function)||is_keyword($function)) {
+    $result= false;
+  }
+  return $result;
+}
+
+function check_function($function) {
+  $result= false;
+  if(is_keyword($function)) {
+    $result= true;
+  } else if (is_meta_function($function)) {
+    $result= true;
+  } else if (function_exists($function)) {
+    $result= true;
+  }
+  return $result;
+}
+
+/** @return true if $function is meta-function, false otherwise */
+function is_meta_function($function) {
+  $meta_functions= array(
+      "print","array","isset","exit","unset",
+      "die","list","eval","empty"
+    );
+  return(in_array($function,$meta_functions));
+}
+
+/** @return true if $param is keyword, false otherwise */
+function is_keyword($function) {
+  $keywords= array(
+     "if","else","elseif","while","do","case",
+    "for","foreach","break","continue",
+    "switch","declare","return","require",
+    "include","require_once","include_once",
+    "try","catch"
+  );
+
+    return(in_array($function,$keywords));
+}
+
+function extract_php_functions($string) {
+  // Function names have to be A-z or _ for the first letter
+  // plus 0-9, +, - for the following letters
+  // Length is 2 minimum
+  $rx_name="[A-Za-z_][A-Za-z0-9_+-]+";
+
+  // Function calls can be after ',', '.', '(' or spaces
+  // Note: Method calls are not detected - this is wanted
+  $rx_function="[.,(\s]\s*($rx_name)\s*\(";
+
+  // Get code inside <?php ... (question-mark)>
+  $string= trim(array_pop(get_php_code($string)));
+  $string= remove_strings($string);
+  $result= array();
+  preg_match_all("/$rx_function/",$string,$result);
+  array_shift($result);
+
+
+  // We need to check if "function" is actual a class created by new operator
+  // negative lookbehind isn't possible with php yet
+  $classes= array();
+  foreach($result[0] as $key => $function) {
+    $match= array();
+    if(preg_match("/new\s+($function)/",$string,$match)) {
+      array_shift($match);
+      array_push($classes,$match);
+    }
+  }
+
+  // We need to manually unset the keys that we found above
+  foreach($classes as $key => $value) {
+    for($i=0;$i<count($value);$i++) {
+      if(isset($result[0])) {
+        unset($result[0][array_search($value[$i],$result[0])]);
+      }
+    }
+  }
+
+  return $result;
+}
+
+/**
+ * Extracts function-calls in php-code
+ * @param $string php-code to extract function calls from
+ * @return array of functions called
+ */
+function extract_user_functions($string) {
+  $rx_name="[A-Za-z_][A-Za-z0-9_+-]*";
+  $rx_user_function="function\s+($rx_name)\s*\(";
+
+  // Get code inside <?php ... (question-mark)>
+  $string= trim(array_pop(get_php_code($string)));
+  $string= remove_strings($string);
+  $result= array();
+  preg_match_all("/$rx_user_function/s",$string,$result);
+  array_shift($result);
+  
+  return $result= array_pop($result);
+}
+
+/**
+ * @return php-code without <?php ... markers
+ */
+function get_php_code($string) {
+  $array= array();
+  preg_match_all('/\<(\?php|\%)(.*)(\?|\%)\>/sx', $string, $array);
+
+  // Pop the first two entries as we don't want them
+  $match= array_shift($array);
+  $match= array_shift($array);
+
+  // Do the same for the last entry
+  $match= array_pop($array);
+
+  // The array $result only contains regex group (.*) now
+  return array_pop($array);
+}
+
+/**
+ * Returns 'print ();' for 'print ("hello world!\n");'
+ * AND:
+ * Returns '$message= sprintf(_()...); for
+ *  $message= sprintf(_("Command '%s', specified as POSTREMOVE for plugin '%s' doesn't seem to exist.")...);
+ * Note the "doesn't"
+ * @param $string code with strings
+ * @return code with strings removed
+ */
+function remove_strings($string) {
+  $result= "";
+  
+  $inside_string=false;
+  $inside_string_uni=false;
+  $inside_string_double=false;
+  
+  // Walk through $string
+  for($i=0;$i<strlen($string);$i++) {
+    if($string[$i]=="'" && ($i>0 && ($string[$i-1] != "\\"))) {
+      if(!$inside_string_uni) {
+        // We're now inside
+        if(!$inside_string_double) {
+          $inside_string_uni= true;
+          $inside_string= true;
+        }
+      } else {
+        // We're now outside
+        $inside_string_uni= false;
+        if(!$inside_string_double) {
+          $inside_string= false;
+        }
+      }
+    } else if($string[$i]=='"' && ($i>0 && ($string[$i-1] != "\\"))) {
+      if(!$inside_string_double) {
+        // We're now inside
+        if(!$inside_string_uni) {
+          $inside_string_double= true;
+          $inside_string= true;
+        }
+      } else {
+        // We're now outside
+        $inside_string_double= false;
+        if(!$inside_string_uni) {
+          $inside_string= false;
+        }
+      }
+    } else {
+      // Push char to $result if not inside string
+      $result.= (!$inside_string)?$string[$i]:"";
+    }
+  }
+
+  // Return string
+  return $result;
+}
+
+/** 
+ * Iteratively scans directory $dir for files (filenames) matching regular expression $pattern
+ * @param $dir Initial Directory to start scan
+ * @param $pattern Regex-Pattern to match on files to scan
+ * @param $skip_dirs Regex-Patten to match on directories to skip
+ * @return file list
+ */
+function list_directory($dir, $pattern, $skip_dirs) {
+  $file_list= '';
+  $stack[]= $dir;
+  while ($stack) {
+    $current_dir= array_pop($stack);
+    if ($dh= opendir($current_dir)) {
+      while (($file= readdir($dh)) !== false) {
+         if ($file !== '.' && $file !== '..' && preg_match("/$skip_dirs/",$file)==0) {
+          $current_file= "{$current_dir}/{$file}";
+          if (is_file($current_file) && preg_match($pattern, $current_file)) {
+            $file_list[]= "{$current_dir}/{$file}";
+          } elseif (is_dir($current_file)) {
+            $stack[]= $current_file;
+          }
+        }
+      }
+    }
+  }
+  return $file_list;
+}
+
+function array_values_recursive($array) {
+  $arrayValues = array();
+  
+  foreach ($array as $value) {
+    if (is_scalar($value) OR is_resource($value)) {
+      $arrayValues[] = $value;
+    } elseif (is_array($value)) {
+      $arrayValues = array_merge($arrayValues, array_values_recursive($value));
+    }
+  }
+
+  return $arrayValues;
+}
+
+// vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
+?>
+