PageRenderTime 24ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/includes/functions.inc.php

https://github.com/mbehiels/pkp-lib
PHP | 336 lines | 196 code | 36 blank | 104 comment | 47 complexity | 8d9c67250cc1c635cc0d2473041efb64 MD5 | raw file
  1. <?php
  2. /**
  3. * @file functions.inc.php
  4. *
  5. * Copyright (c) 2000-2011 John Willinsky
  6. * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
  7. *
  8. * @ingroup index
  9. *
  10. * @brief Contains definitions for common functions used system-wide.
  11. * Any frequently-used functions that cannot be put into an appropriate class should be added here.
  12. */
  13. /**
  14. * Emulate a Java-style import statement.
  15. * Simply includes the associated PHP file (using require_once so multiple calls to include the same file have no effect).
  16. * @param $class string the complete name of the class to be imported (e.g. 'lib.pkp.classes.core.Core')
  17. */
  18. if (!function_exists('import')) {
  19. function import($class) {
  20. static $deprecationWarning = null;
  21. // Try to bypass include path for best performance
  22. $filePath = str_replace('.', '/', $class) . '.inc.php';
  23. if((@include_once BASE_SYS_DIR.'/'.$filePath) === false) {
  24. // Oops, we found a legacy include statement,
  25. // let's try the include path then.
  26. require_once($filePath);
  27. if (is_null($deprecationWarning) && class_exists('Config')) {
  28. $deprecationWarning = (boolean)Config::getVar('debug', 'deprecation_warnings');
  29. }
  30. if ($deprecationWarning) trigger_error('Deprecated import of a partially qualified class name.');
  31. }
  32. }
  33. }
  34. if (!function_exists('file_get_contents')) {
  35. // For PHP < 4.3.0
  36. function file_get_contents($file) {
  37. return join('', file($file));
  38. }
  39. }
  40. if (!function_exists('stream_get_contents')) {
  41. function stream_get_contents($fp) {
  42. fflush($fp);
  43. }
  44. }
  45. if (!function_exists('array_combine')) {
  46. // For PHP 4
  47. function array_combine($keys, $values) {
  48. if (count($keys) != count($values)) return false;
  49. if (empty($keys)) return false;
  50. $out = array();
  51. while(count($keys)) {
  52. $key = array_shift($keys);
  53. $value = array_shift($values);
  54. if (!is_integer($key) && !is_string($key)) $key = (string) $key;
  55. $out[$key] = $value;
  56. }
  57. return $out;
  58. }
  59. }
  60. /**
  61. * Wrapper around die() to pretty-print an error message with an optional stack trace.
  62. */
  63. function fatalError($reason) {
  64. // Because this method may be called when checking the value of the show_stacktrace
  65. // configuration string, we need to ensure that we don't get stuck in an infinite loop.
  66. static $isErrorCondition = null;
  67. static $showStackTrace = false;
  68. if ($isErrorCondition === null) {
  69. $isErrorCondition = true;
  70. $showStackTrace = Config::getVar('debug', 'show_stacktrace');
  71. $isErrorCondition = false;
  72. }
  73. echo "<h1>$reason</h1>";
  74. if ($showStackTrace && checkPhpVersion('4.3.0')) {
  75. echo "<h4>Stack Trace:</h4>\n";
  76. $trace = debug_backtrace();
  77. // Remove the call to fatalError from the call trace.
  78. array_shift($trace);
  79. // Back-trace pretty-printer adapted from the following URL:
  80. // http://ca3.php.net/manual/en/function.debug-backtrace.php
  81. // Thanks to diz at ysagoon dot com
  82. // FIXME: Is there any way to localize this when the localization
  83. // functions may have caused the failure in the first place?
  84. foreach ($trace as $bt) {
  85. $args = '';
  86. if (isset($bt['args'])) foreach ($bt['args'] as $a) {
  87. if (!empty($args)) {
  88. $args .= ', ';
  89. }
  90. switch (gettype($a)) {
  91. case 'integer':
  92. case 'double':
  93. $args .= $a;
  94. break;
  95. case 'string':
  96. $a = htmlspecialchars(substr($a, 0, 64)).((strlen($a) > 64) ? '...' : '');
  97. $args .= "\"$a\"";
  98. break;
  99. case 'array':
  100. $args .= 'Array('.count($a).')';
  101. break;
  102. case 'object':
  103. $args .= 'Object('.get_class($a).')';
  104. break;
  105. case 'resource':
  106. $args .= 'Resource('.strstr($a, '#').')';
  107. break;
  108. case 'boolean':
  109. $args .= $a ? 'True' : 'False';
  110. break;
  111. case 'NULL':
  112. $args .= 'Null';
  113. break;
  114. default:
  115. $args .= 'Unknown';
  116. }
  117. }
  118. $class = isset($bt['class'])?$bt['class']:'';
  119. $type = isset($bt['type'])?$bt['type']:'';
  120. $function = isset($bt['function'])?$bt['function']:'';
  121. $file = isset($bt['file'])?$bt['file']:'(unknown)';
  122. $line = isset($bt['line'])?$bt['line']:'(unknown)';
  123. echo "<strong>File:</strong> {$file} line {$line}<br />\n";
  124. echo "<strong>Function:</strong> {$class}{$type}{$function}($args)<br />\n";
  125. echo "<br/>\n";
  126. }
  127. }
  128. // Determine the application name. Use defensive code so that we
  129. // can handle errors during early application initialization.
  130. $application = null;
  131. if (class_exists('Registry')) {
  132. $application = Registry::get('application', true, null);
  133. }
  134. $applicationName = '';
  135. if (!is_null($application)) {
  136. $applicationName = $application->getName().': ';
  137. }
  138. error_log($applicationName.$reason);
  139. if (defined('DONT_DIE_ON_ERROR') && DONT_DIE_ON_ERROR == true) {
  140. // trigger an error to be catched outside the application
  141. trigger_error($reason);
  142. return;
  143. }
  144. die();
  145. }
  146. /**
  147. * Check to see if the server meets a minimum version requirement for PHP.
  148. * @param $version Name of version (see version_compare documentation)
  149. * @return boolean
  150. */
  151. function checkPhpVersion($version) {
  152. return (version_compare(PHP_VERSION, $version) !== -1);
  153. }
  154. /**
  155. * Create a PHP4/5 compatible shallow
  156. * copy of the given object.
  157. * @param $object object
  158. * @return object the cloned object
  159. */
  160. function &cloneObject(&$object) {
  161. if (checkPhpVersion('5.0.0')) {
  162. // We use the PHP5 clone() syntax so that PHP4 doesn't
  163. // raise a parse error.
  164. $clonedObject = clone($object);
  165. } else {
  166. // PHP4 always clones objects on assignment
  167. $clonedObject = $object;
  168. }
  169. return $clonedObject;
  170. }
  171. /**
  172. * Instantiates an object for a given fully qualified
  173. * class name after executing several checks on the class.
  174. *
  175. * The checks prevent certain vulnerabilities when
  176. * instantiating classes generically.
  177. *
  178. * NB: We currently only support one constructor
  179. * argument. If we need arbitrary arguments later
  180. * we can do that via func_get_args() which allows us
  181. * to handle an arbitrary number of optional
  182. * constructor arguments. The $constructorArg
  183. * parameter needs to be last in the parameter list
  184. * to be forward compatible with this potential use
  185. * case.
  186. *
  187. * @param $fullyQualifiedClassName string
  188. * @param $expectedTypes string|array the class
  189. * must conform to at least one of the given types.
  190. * @param $expectedPackages string|array the class
  191. * must be part of at least one of the given packages.
  192. * @param $expectedMethods string|array names of methods
  193. * that must all be present for the requested class.
  194. * @param $constructorArg mixed constructor argument
  195. *
  196. * @return object|boolean the instantiated object or false
  197. * if the class instantiation didn't result in the expected
  198. * type.
  199. */
  200. function &instantiate($fullyQualifiedClassName, $expectedTypes = null, $expectedPackages = null, $expectedMethods = null, $constructorArg = null) {
  201. $errorFlag = false;
  202. // Validate the class name
  203. if (!String::regexp_match('/^[a-zA-Z0-9.]+$/', $fullyQualifiedClassName)) {
  204. return $errorFlag;
  205. }
  206. // Validate the class package
  207. if (!is_null($expectedPackages)) {
  208. if (is_scalar($expectedPackages)) $expectedPackages = array($expectedPackages);
  209. $validPackage = false;
  210. foreach ($expectedPackages as $expectedPackage) {
  211. // No need to use String class here as class names are always US-ASCII
  212. if (substr($fullyQualifiedClassName, 0, strlen($expectedPackage)+1) == $expectedPackage.'.') {
  213. $validPackage = true;
  214. break;
  215. }
  216. }
  217. // Raise a fatal error if the class does not belong
  218. // to any of the expected packages. This is to prevent
  219. // certain types of code inclusion attacks.
  220. if (!$validPackage) {
  221. // Construct meaningful error message.
  222. $expectedPackageCount = count($expectedPackages);
  223. $separator = '';
  224. foreach($expectedPackages as $expectedPackageIndex => $expectedPackage) {
  225. if ($expectedPackageIndex > 0) {
  226. $separator = ($expectedPackageIndex == $expectedPackageCount-1 ? ' or ' : ', ' );
  227. }
  228. $expectedPackageString .= $separator.'"'.$expectedPackage.'"';
  229. }
  230. fatalError('Trying to instantiate class "'.$fullyQualifiedClassName.'" which is not in any of the expected packages '.$expectedPackageString.'.');
  231. }
  232. }
  233. // Import the requested class
  234. import($fullyQualifiedClassName);
  235. // Identify the class name
  236. $fullyQualifiedClassNameParts = explode('.', $fullyQualifiedClassName);
  237. $className = array_pop($fullyQualifiedClassNameParts);
  238. // Type check I: The requested class should be declared by now.
  239. if (!class_exists($className)) {
  240. fatalError('Cannot instantiate class. Class "'.$className.'" is not declared in "'.$fullyQualifiedClassName.'".');
  241. }
  242. // Check that the expected operation exists for the class.
  243. if (!is_null($expectedMethods)) {
  244. if (is_scalar($expectedMethods)) $expectedMethods = array($expectedMethods);
  245. // Lower case comparison for PHP4 compatibility.
  246. // We don't need the String class here as method names are
  247. // always US-ASCII.
  248. $declaredMethods = array_map('strtolower', get_class_methods($className));
  249. foreach($expectedMethods as $expectedMethod) {
  250. $requiredMethod = strtolower($expectedMethod);
  251. if (!in_array($requiredMethod, $declaredMethods)) {
  252. return $errorFlag;
  253. }
  254. }
  255. }
  256. // Instantiate the requested class
  257. if (is_null($constructorArg)) {
  258. $classInstance = new $className();
  259. } else {
  260. $classInstance = new $className($constructorArg);
  261. }
  262. // Type check II: The object must conform to the given interface (if any).
  263. if (!is_null($expectedTypes)) {
  264. if (is_scalar($expectedTypes)) $expectedTypes = array($expectedTypes);
  265. $validType = false;
  266. foreach($expectedTypes as $expectedType) {
  267. if (is_a($classInstance, $expectedType)) {
  268. $validType = true;
  269. break;
  270. }
  271. }
  272. if (!$validType) return $errorFlag;
  273. }
  274. return $classInstance;
  275. }
  276. /**
  277. * Remove empty elements from an array
  278. * @param $array array
  279. * @return array
  280. */
  281. function arrayClean(&$array) {
  282. if (!is_array($array)) return null;
  283. return array_filter($array, create_function('$o', 'return !empty($o);'));
  284. }
  285. /**
  286. * Recursively strip HTML from a (multidimensional) array.
  287. * @param $values array
  288. * @return array the cleansed array
  289. */
  290. function stripAssocArray($values) {
  291. foreach ($values as $key => $value) {
  292. if (is_scalar($value)) {
  293. $values[$key] = strip_tags($values[$key]);
  294. } else {
  295. $values[$key] = stripAssocArray($values[$key]);
  296. }
  297. }
  298. return $values;
  299. }
  300. ?>