PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/core/utils/class.utils.system.php

http://buddypress-media.googlecode.com/
PHP | 717 lines | 302 code | 200 blank | 215 comment | 74 complexity | 5c82ad25e0350a40e2c18fdf45dc818a MD5 | raw file
Possible License(s): AGPL-1.0, Apache-2.0, GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * BP-MEDIA SYSTEM UTILITY FUNCTIONS
  4. * Utility functions that do commonly used minor tasks
  5. *
  6. * @version 0.1.9
  7. * @since 0.1.9
  8. * @package BP-Media
  9. * @subpackage Util
  10. * @license GPL v2.0
  11. * @link http://code.google.com/p/buddypress-media/
  12. *
  13. * ========================================================================================================
  14. */
  15. class BPM_sUtil {
  16. private function __construct() {}
  17. /**
  18. * Lofts a flat array of nodes into a rooted directed tree in O(n) time
  19. * with only O(n) extra memory. This is also known as the "in-place quick
  20. * union" algorithm.
  21. *
  22. * @link http://en.wikipedia.org/wiki/Tree_(graph_theory)
  23. * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks
  24. * @link http://en.wikipedia.org/wiki/Quicksort (in-place version)
  25. *
  26. * @version 0.1.9
  27. * @since 0.1.9
  28. * @param array $nodes | Flat array of nodes
  29. * @return array $result | Hierarchical array of nodes
  30. */
  31. public static function loftHierarchy($nodes) {
  32. $tree = array();
  33. foreach( $nodes as $node_id => $data){
  34. // Note: we can operate directly on the passed parameter, because unless
  35. // explicitly told not to by using the "&$" sigil, PHP passes copies
  36. // of variables into a function.
  37. $nodes[$node_id]["node_id"] = $node_id; // Insert the node_id into each node to make the data
  38. // structure easier to use. Note the unit tests are very
  39. // picky about the order this gets done in because it
  40. // affects its position in the output array.
  41. if( empty($data["parent"]) ){
  42. $tree["children"][$node_id] =& $nodes[$node_id];
  43. }
  44. else {
  45. $nodes[$data["parent"]]["children"][$node_id] =& $nodes[$node_id];
  46. }
  47. }
  48. return $tree;
  49. }
  50. /**
  51. * Finds the longest intersect between a walk and a tree.
  52. *
  53. * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks
  54. * @link http://en.wikipedia.org/wiki/Breadth-first_search
  55. *
  56. * @version 0.1.9
  57. * @since 0.1.9
  58. * @param array $walk | Walk array
  59. * @param array $tree | Tree array
  60. * @return array $result | Walk key and matching node id
  61. */
  62. public static function walkIntersectTree($walk, $tree) {
  63. if( !is_array($walk) ){
  64. throw new BPM_exception(array(
  65. 'numeric'=>1,
  66. 'text'=>"Walk is not a valid array",
  67. 'data'=>array( "walk"=>$walk, "tree"=>$tree),
  68. 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
  69. 'child'=>null
  70. )
  71. );
  72. }
  73. if( !is_array($tree) ){
  74. throw new BPM_exception(array(
  75. 'numeric'=>2,
  76. 'text'=>"Tree is not a valid array",
  77. 'data'=>array( "walk"=>$walk, "tree"=>$tree),
  78. 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
  79. 'child'=>null
  80. )
  81. );
  82. }
  83. // Loop through each child node, searching for the
  84. // child node with the longest walk
  85. // ================================================
  86. $min_offset = null;
  87. $min_node = null;
  88. foreach( $tree["children"] as $node_id => $data){
  89. if($data["slug"] == $walk[0]){
  90. $reduced_walk = array_slice($walk, 1);
  91. $intersect = self::walkIntersectTree_iterator($reduced_walk, $data);
  92. if( ($min_offset === null) || ($intersect["walk_offset"] < $min_offset) ){
  93. $min_offset = $intersect["walk_offset"];
  94. $min_node = $intersect["node_id"];
  95. }
  96. }
  97. }
  98. // Return the child node with the longest walk, or if
  99. // there was no matching child node, return this node
  100. // ================================================
  101. if($min_offset === null){
  102. $result = array(
  103. "endpoint_id"=>null,
  104. "endpoint_name"=>null,
  105. "walk_key"=>null,
  106. "transect"=>array()
  107. );
  108. }
  109. else {
  110. // Convert offset to array key number so functions further down
  111. // the chain can use array_slice() to find the tokens after the
  112. // endpoint that correspond to actions/arguements (if they exist)
  113. $walk_key = (count($walk) - $min_offset) - 1;
  114. $result = array(
  115. "endpoint_id" => $min_node,
  116. "endpoint_name"=>$walk[$walk_key],
  117. "walk_key" => $walk_key,
  118. "transect"=>array_slice($walk, ($walk_key +1) )
  119. );
  120. }
  121. return $result;
  122. }
  123. /**
  124. * Finds the longest intersect between the walk and the tree.
  125. *
  126. * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks
  127. * @link http://en.wikipedia.org/wiki/Breadth-first_search
  128. *
  129. * @version 0.1.9
  130. * @since 0.1.9
  131. * @param array $walk | Walk array
  132. * @param array $tree | Tree array
  133. * @return array $result | Walk offset and matching node id
  134. */
  135. public static function walkIntersectTree_iterator($walk, $tree) {
  136. // Calculate offsets
  137. // ================================================
  138. $walk_offset = count($walk);
  139. if( is_array($tree["children"]) ){
  140. $children_count = count($tree["children"]);
  141. }
  142. else {
  143. $children_count = 0;
  144. }
  145. // If either termination condition is met, return
  146. // ================================================
  147. if( ($walk_offset == 0) || ($children_count == 0) ){
  148. $result = array( "node_id"=>$tree["node_id"],
  149. "walk_offset"=>$walk_offset
  150. );
  151. return $result;
  152. }
  153. // Loop through each child node, searching for the
  154. // child node with the longest walk
  155. // ================================================
  156. $min_offset = null;
  157. $min_node = null;
  158. foreach( $tree["children"] as $node_id => $data){
  159. if($data["slug"] == $walk[0]){
  160. $reduced_walk = array_slice($walk, 1);
  161. $intersect = self::walkIntersectTree_iterator($reduced_walk, $data);
  162. if( ($min_offset === null) || ($intersect["walk_offset"] < $min_offset) ){
  163. $min_offset = $intersect["walk_offset"];
  164. $min_node = $intersect["node_id"];
  165. }
  166. }
  167. }
  168. // Return the child node with the longest walk, or if
  169. // there was no matching child node, return this node
  170. // ================================================
  171. if($min_offset === null){
  172. $result = array(
  173. "node_id"=>$tree["node_id"],
  174. "walk_offset"=>$walk_offset
  175. );
  176. }
  177. else {
  178. $result = array(
  179. "node_id"=>$min_node,
  180. "walk_offset"=>$min_offset
  181. );
  182. }
  183. return $result;
  184. }
  185. /**
  186. * Recursively key-intersects two arrays, preserving the values in the $master array.
  187. * For 1-level deep arrays, PHP's native array_intersect_key() is slightly faster.
  188. *
  189. * It's approximately 40 times faster to intersect arrays based on keys instead of values,
  190. * because of the way PHP stores arrays and because keys guarantee cardinality.
  191. *
  192. * @param array $master | Master array
  193. * @param array $slave | Slave array
  194. */
  195. public static function keyIntersect($master, $slave) {
  196. $intersect = array_intersect_key($master, $slave);
  197. foreach($intersect as $key => $val){
  198. if( is_array($val) && is_array($slave[$key]) ){
  199. $intersect[$key] = self::keyIntersect($val, $slave[$key]);
  200. }
  201. }
  202. unset($key, $val);
  203. return $intersect;
  204. }
  205. /**
  206. * Removes all empty walks to $depth from an associative array
  207. *
  208. * @param array $data | Source array
  209. * @param int $depth | Depth to search walks to
  210. * @return array | Pruned array
  211. */
  212. public static function arrayPrune($data, $depth) {
  213. if( !is_array($data) || ($depth == 0) ){
  214. return $data;
  215. }
  216. $depth--;
  217. foreach($data as $key => $val){
  218. if( count($val) != 0){
  219. $recur_result = self::arrayPrune($val, $depth);
  220. if( ($depth == 0) || (count($recur_result) > 0) ){
  221. $result[$key] = $recur_result;
  222. }
  223. }
  224. }
  225. unset($key, $val, $recur_result);
  226. return $result;
  227. }
  228. /**
  229. * Determines if a value exists in an array. Unlike array_search(), this function
  230. * will not crash if the variable or the parent array is null, and it returns a boolean
  231. * response, not an int offset, so a === test statement is not required.
  232. *
  233. * @param string $val | Value to search for
  234. * @param array $array | Array to search
  235. * @return bool | True if val exists. False if not.
  236. */
  237. public static function valExists($val, $array) {
  238. if( is_array($array) ){
  239. $offset = array_search($val, $array);
  240. if($offset === false){
  241. return false;
  242. }
  243. else {
  244. return true;
  245. }
  246. }
  247. else {
  248. return false;
  249. }
  250. }
  251. /**
  252. * Determines if a key exists in an array. Unlike array_key_exists(), this function
  253. * will not crash if the variable or the parent array is null.
  254. *
  255. * @param string $key | Name of the key
  256. * @param array $array | Array to search for key
  257. * @return bool | True if key exists. False if not.
  258. */
  259. public static function keyExists($key, $array) {
  260. if( is_array($array) ){
  261. return array_key_exists($key, $array);
  262. }
  263. else {
  264. return false;
  265. }
  266. }
  267. /**
  268. * Determines if a key in an array exists and is true. Unlike array_key_exists(),
  269. * this function will not crash if the variable or the parent array is null, and
  270. * won't trigger a PHP warning when testing against nonexistent keys.
  271. *
  272. * @param string $key | Name of the key
  273. * @param array $array | Array to search for key
  274. * @return bool | True if key exists. False if not.
  275. */
  276. public static function keyTrue($key, $array) {
  277. if( is_array($array) ){
  278. if( array_key_exists($key, $array) ){
  279. if($array[$key] == true){
  280. return true;
  281. }
  282. else {
  283. return false;
  284. }
  285. }
  286. else {
  287. return false;
  288. }
  289. }
  290. else {
  291. return false;
  292. }
  293. }
  294. /**
  295. * Returns the value of a key in an array if it exists, otherwise returns NULL. Unlike
  296. * array_key_exists(), this function will not crash if the variable or the parent array
  297. * is null, and won't trigger a PHP warning when fetching nonexistent keys.
  298. *
  299. * @param string $key | Name of the key
  300. * @param array $array | Array to search for key
  301. * @return bool | If key exists, value of key. Otherwise null.
  302. */
  303. public static function keyVal($key, $array) {
  304. if( is_array($array) ){
  305. if( array_key_exists($key, $array) ){
  306. return $array[$key];
  307. }
  308. else {
  309. return null;
  310. }
  311. }
  312. else {
  313. return null;
  314. }
  315. }
  316. /**
  317. * Converts a size stored in an ini var in bytes
  318. *
  319. * @param $varname ini var name
  320. * @return int size in bytes
  321. */
  322. public static function ini_get_bytes ( $varname ) {
  323. $val = ini_get( $varname );
  324. $result = BPM_math::formattedSizeToBytes( $val );
  325. return $result;
  326. }
  327. /**
  328. * Truncates a string to specific length, automatically handling strings that are formatted
  329. * with unicode (two byte) encoding
  330. *
  331. * @param string $string | text string to trim
  332. * @param int $length | number of characters to truncate string to
  333. * @param bool $unicode | true: process as two-byte characters, false: process as single-byte characters, null: auto-detect
  334. * @return string | truncated text string
  335. */
  336. public static function i18n_trunc($string, $length, $unicode=null) {
  337. // The purpose of this function is to centralize all our string truncates in one place
  338. // so it's easy to change our algorithms.
  339. if($unicode === null){
  340. if ( mb_check_encoding($s,"UTF-8") == true )
  341. {
  342. $unicode = true;
  343. }
  344. else {
  345. $unicode = false;
  346. }
  347. }
  348. if($unicode){
  349. return mb_substr($string, 0, $length);
  350. }
  351. else {
  352. return substr($string, 0, $length );
  353. }
  354. }
  355. /**
  356. * Truncates a string to specific length, automatically handling strings that are formatted
  357. * with unicode (two byte) encoding
  358. *
  359. * @param string $string | text string to trim
  360. * @param int $length | number of characters to truncate string to
  361. * @param bool $unicode | true: process as two-byte characters, false: process as single-byte characters, null: auto-detect
  362. * @return string | truncated text string
  363. */
  364. public static function i18n_elipsis($string, $length, $unicode=null) {
  365. // The purpose of this function is to centralize all our string truncates in one place
  366. // so it's easy to change our algorithms.
  367. if($unicode === null){
  368. if ( mb_check_encoding($s,"UTF-8") == true )
  369. {
  370. $unicode = true;
  371. }
  372. else {
  373. $unicode = false;
  374. }
  375. }
  376. if($unicode){
  377. $result = mb_substr($string, 0, $length);
  378. if($string != $result){
  379. $result = trim($result); // Is this valid for mb_ strings?
  380. $result .= '&#8230;';
  381. }
  382. return $result;
  383. }
  384. else {
  385. $result = substr($string, 0, $length);
  386. if($string != $result){
  387. $result = trim($result);
  388. $result .= '&#8230;';
  389. }
  390. return $result;
  391. }
  392. }
  393. /**
  394. * Recursively traverses a directory tree, returning the paths to all files within
  395. * the tree. The $pattern string has to consist of a filepath plus an actual pattern.
  396. * Example: "C:\foo\*", "C:\bar\*.JPG". Paths and extensions are case sensitive on
  397. * both Windows and Linux systems.
  398. *
  399. * @param string $pattern | @see http://ca.php.net/manual/en/function.glob.php
  400. * @param string $flags | @see http://ca.php.net/manual/en/function.glob.php
  401. * @return array $files | Array of paths to all files in the tree.
  402. */
  403. public static function glob_recursive($pattern, $flags = 0) {
  404. $files = glob($pattern, $flags);
  405. foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir)
  406. {
  407. $files = array_merge($files, self::glob_recursive($dir.'/'.basename($pattern), $flags));
  408. }
  409. return $files;
  410. }
  411. /**
  412. * Get a random file path
  413. *
  414. * @param string $dir
  415. * @param string $extension
  416. * @return string random and unique file path
  417. */
  418. public static function random_filepath( $dir, $extension='' ) {
  419. $dir = untrailingslashit($dir);
  420. $rand_string = self::random_string();
  421. if ( glob( "$dir/$rand_string*" ) ) // The odds of a collision are ridiculous, but still worth checking
  422. return self::random_filepath($dir, $extension);
  423. elseif($extension)
  424. return "$dir/$rand_string.$extension";
  425. else
  426. return "$dir/$rand_string";
  427. }
  428. /**
  429. * Random string
  430. *
  431. * @param int $length string length
  432. * @return random string
  433. */
  434. public static function random_string($length=16) {
  435. $rand_string ='';
  436. $aZ09 = array_merge(range('A', 'Z'), range('a', 'z'), range(0, 9));
  437. for($c=0; $c < $length; $c++)
  438. $rand_string .= $aZ09[mt_rand(0,count($aZ09)-1)];
  439. return $rand_string;
  440. }
  441. /**
  442. * Write data to file
  443. *
  444. * @param string $data data to write
  445. * @param string $file_path
  446. * @return string|WP_Error file path or error
  447. */
  448. public static function write_file($data, $file_path) {
  449. $handle = self::new_file_handle($file_path);
  450. if( is_wp_error( $handle ) )
  451. return $handle;
  452. fwrite( $handle, $data );
  453. fclose( $handle );
  454. clearstatcache();
  455. return $file_path;
  456. }
  457. /**
  458. * Open a new file handle
  459. *
  460. * @param string $file_path
  461. * @return resource|WP_Error handle or error
  462. */
  463. public static function &new_file_handle($file_path) {
  464. if ( ! wp_mkdir_p( dirname( $file_path ) ) )
  465. return new WP_Error( 'bp_media:filesystem:cannot_create_dir', sprintf( __('Cannot create the directory %s.',"bp-media"), dirname( $file_path ) ) );
  466. $handle = @fopen($file_path, 'wb');
  467. if ( ! $handle )
  468. return new WP_Error( 'bp_media:filesystem:cannot_create_file', sprintf( __('Cannot create the file %s.',"bp-media"), $file_path ) );
  469. return $handle;
  470. }
  471. /**
  472. * Takes a POST value as received from PHP and applies whatever processing is necessary to
  473. * turn it back into what the user actually entered into the form.
  474. *
  475. * @param string $raw | Raw value as received from PHP
  476. * @return string $result | Processed string
  477. */
  478. public static function formVal($raw) {
  479. // PHP likes to add backslashes to escape backslash "\" characters entered into forms. But, this
  480. // can be disabled by setting "magic_quotes_gpc" false in the php.ini file. However, PHP has deprecated
  481. // this setting, and the function used to check for it, get_magic_quotes_gpc(), doesn't return the correct
  482. // value on my Windows-based dev system (PHP adds backslashes but get_magic_quotes_gpc() returns false)
  483. // This function makes sure all of our form un-escaping tasks happen in one place so it's easy to change
  484. // when PHP decides what they're doing. NOTE: this function cannot be unit-tested with our current automated
  485. // test system because it requires sending POST commands to the server.
  486. if( !is_array($raw) ){
  487. $result = stripslashes($raw);
  488. }
  489. else {
  490. // If the form value is an array, recursively traverse any sub-arrays
  491. // and strip slashes
  492. foreach($raw as $key => $val ){
  493. $result[$key] = self::formVal($val);
  494. }
  495. }
  496. return $result;
  497. }
  498. /**
  499. * Converts one or more filepaths based in the BPM plugin folder to a URL that
  500. * can be accessed via a web server URL
  501. *
  502. * @param string/array $paths | Single path as string. Multiple paths as array of strings.
  503. * @return string/array $URL | Single URL as string. Multiple URL's as array of strings.
  504. */
  505. public static function pluginPathToURL($paths) {
  506. if( is_string($paths) ){
  507. $paths = str_replace(BPM_PATH_BASE, BPM_URL_BASE, $paths);
  508. }
  509. else {
  510. foreach($paths as $key => $path){
  511. $paths[$key] = str_replace(BPM_PATH_BASE, BPM_URL_BASE, $path);
  512. }
  513. }
  514. return $paths;
  515. }
  516. } // End of class BPM_sUtil
  517. ?>