- <?php 
- /* 
-  * Copyright (C) 2017 Karmabunny Pty Ltd. 
-  * 
-  * This file is a part of SproutCMS. 
-  * 
-  * SproutCMS 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. 
-  * 
-  * For more information, visit <http://getsproutcms.com>. 
-  */ 
-   
- namespace Sprout\Helpers; 
-   
- use Exception; 
- use InvalidArgumentException; 
- use ReflectionClass; 
-   
- use Kohana; 
-   
- use karmabunny\pdb\Exceptions\QueryException; 
-   
-   
- /** 
- * Useful functions for sprout in general 
- **/ 
- class Sprout 
- { 
-   
-   
-     /** 
-      * Determines the file path for a class, usually for autoloading 
-      * @param string $class The class, including namespace 
-      * @return string|false path or false if it couldn't be found 
-      */ 
-     public static function determineFilePath($class) 
-     { 
-         $sep = DIRECTORY_SEPARATOR; 
-         $sprout_ns = 'Sprout\\'; 
-         $sprout_ns_len = strlen($sprout_ns); 
-         $modules_ns = 'SproutModules\\'; 
-         $modules_ns_len = strlen($modules_ns); 
-         $file = false; 
-   
-         // Load Sprout core 
-         if (substr($class, 0, $sprout_ns_len) == $sprout_ns) { 
-             $file = substr($class, $sprout_ns_len); 
-             $dir = realpath(- __DIR__  . $sep . '..') . $sep;
 
-   
-         // Load modules 
-         } else if (substr($class, 0, $modules_ns_len) == $modules_ns) { 
-             $file = substr($class, $modules_ns_len); 
-   
-             // Strip vendor name to get directory within modules/ 
-             // e.g. SproutModules\Karmabunny\Pages => Pages 
-             $slash_pos = strpos($file, '\\'); 
-             if ($slash_pos === false) return; 
-             $file = substr($file, $slash_pos + 1); 
-   
-             $dir .= 'modules' . $sep; 
-   
-         } else { 
-             $dir = DOCROOT . 'vendor/'; 
-             $file = $class; 
-         } 
-   
-         if (!$file) return false; 
-   
-     } 
-   
-   
-     /** 
-      * Removes the namespaces from a class-like entity, 
-      * e.g. Sprout\Helpers\Text => Text 
-      * @param string $classlike 
-      * @return string 
-      */ 
-     public static function removeNs($classlike) 
-     { 
-         if ($pos !== false) { 
-             return substr($classlike, $pos + 1); 
-         } 
-         return $classlike; 
-     } 
-   
-   
-     /** 
-      * Creates an object of a class specified by a string name, with a list of possible namespaces to lookup if the 
-      * specified class name doesn't contain a namespace 
-      * @param string $class The class to instantiate 
-      * @param array $possible_nses Possible namespaces to try 
-      * @return object 
-      * @throws Exception if the class lookup failed 
-      */ 
-     public-  static  function-  nsNew ($class, array $possible_nses)
 
-     { 
-         if (strpos($class, '\\') !== false) { 
-                 return new $class; 
-             } else { 
-                 throw new Exception("Unable to load class {$class}"); 
-             } 
-         } 
-         foreach ($possible_nses as $ns) { 
-             $full_class = $ns . '\\' . $class; 
-                 return new $full_class; 
-             } 
-         } 
-         throw new Exception("Class lookup failed: {$class}"); 
-     } 
-   
-   
-     /** 
-      * Gets the full class name (including namespace) for a specified class, with a list of namespaces to search 
-      * @param string $class The class to instantiate, e.g. 'Fb' 
-      * @param array $possible_nses Possible namespaces to try, e.g. ['Sprout\Helpers'] 
-      * @return string 
-      * @throws Exception if the class lookup failed 
-      */ 
-     public-  static  function-  nsClass ($class, array $possible_nses)
 
-     { 
-         if (strpos($class, '\\') !== false) { 
-         } 
-         foreach ($possible_nses as $ns) { 
-             $full_class = $ns . '\\' . $class; 
-         } 
-         throw new Exception("Class lookup failed: {$class}"); 
-     } 
-   
-   
-     /** 
-      * Gets the full name (including namespaced class) for a specified function 
-      * @param string $func The function to find, e.g. 'Fb::dropdown' 
-      * @param array $possible_nses Possible namespaces to try, e.g. ['Sprout\Helpers'] 
-      * @return string 
-      * @throws Exception if the function lookup failed 
-      */ 
-     public-  static  function-  nsFunc ($func, array $possible_nses)
 
-     { 
-         $class = ''; 
-         if (strpos($func, '::') !== false) { 
-         } 
-   
-         if (strpos($func, '\\') !== false) { 
-             if ($class) { 
-             } 
-         } 
-         foreach ($possible_nses as $ns) { 
-             if ($class) { 
-                 $full_class = $ns . '\\' . $class; 
-                 if (method_exists($full_class, $func)) return "{$full_class}::{$func}"; 
-             } else { 
-                 $full_fn = $ns . '\\' . $func; 
-             } 
-         } 
-         throw new Exception("Function lookup failed: {$func}"); 
-     } 
-   
-   
-     /** 
-      * Construct a new instance of a class with a given name 
-      * 
-      * @example 
-      *     $inst = Sprout::instance($widget_class, 'Sprout\\Widgets\\Widget'); 
-      * 
-      * @param string $class_name The name of the class 
-      * @param string|array $base_class_name The base class or interface which the class must extend/implement. 
-      *        Can be a string for a single check, or an array for multiple checks. 
-      *        NULL disables this check. 
-      * @throws InvalidArgumentException If the class does not exist 
-      * @return mixed The new instance 
-      */ 
-     public static function instance($class_name, $base_class_name = null) 
-     { 
-             throw new InvalidArgumentException("Class <{$class_name}> does not exist"); 
-         } 
-   
-         // Check the class isn't abstract 
-         $class = new ReflectionClass($class_name); 
-         if ($class->isAbstract()) { 
-             throw new InvalidArgumentException("Class <{$class_name}> is abstract"); 
-         } 
-   
-         // Check that the class extends/implements everything it's required to 
-         if (!empty($base_class_name)) { 
-                 $base_class_name = [$base_class_name]; 
-             } 
-             foreach ($base_class_name as $chk) { 
-                 if (!$class->isSubclassOf($chk)) { 
-                     throw new InvalidArgumentException("Class <{$class_name}> is not a sub-class of <{$chk}>"); 
-                 } 
-             } 
-         } 
-   
-         // Interesting little way to report an instantiation error even if display_errors is off. 
-         // On fatal errors, the output buffer gets flushed to the beowser, reporting our message. 
-         // On success, we just clear the buffer. 
-         // On the test server, we just let it die naturally. 
-         if (IN_PRODUCTION) { 
-             echo '<p><b>FATAL ERROR:</b><br>Unable to instance class "' . Enc::html($class_name) . '".</p>'; 
-             $inst = @new $class_name; 
-         } else { 
-             $inst = new $class_name; 
-         } 
-   
-         return $inst; 
-     } 
-   
-   
-     /** 
-      * Returns the current version of sprout 
-      * 
-      * @param bool $git_version Optional flag to return git version, returns branding version by default 
-      */ 
-     public static function getVersion($git_version = false) 
-     { 
-         if (!empty($git_version)) return-  Kohana ::config('core.version');
 
-         return Kohana::config('core.version_brand'); 
-     } 
-   
-     /** 
-     * Returns true if the specified module is currently installed, false otherwise 
-     **/ 
-     public static function moduleInstalled($module_name) 
-     { 
-         return in_array($module_name,-  Register ::getModules());
 
-     } 
-   
-   
-     /** 
-     * Gets a simplified backtrace with fewer elements and no recursion 
-     * @param array $trace If empty, the trace is automatically determined 
-     * @return array 
-     */ 
-     public-  static  function-  simpleBacktrace (array $trace = [])
 
-     { 
-         if (count($trace) == 0) { 
-   
-             // Remove this and its caller 
-         } 
-   
-         $simple_trace = []; 
-         foreach ($trace as $call) { 
-             $simple_call = []; 
-             if (isset($call['file'])) { 
-                 $file = $call['file']; 
-                 if (IN_PRODUCTION or @$_SERVER['SERVER_ADDR'] != @$_SERVER['REMOTE_ADDR']) { 
-                     } 
-                 } 
-                 $simple_call['file'] = $file; 
-             } 
-             if (isset($call['line'])) { 
-                 $simple_call['line'] = $call['line']; 
-             } 
-             if (!empty($call['function'])) { 
-                 $call_func = $call['function']; 
-                 if (isset($call['class'])) { 
-                     $class = $call['class']; 
-                     if (isset($call['type'])) { 
-                         $class .= $call['type']; 
-                     } else { 
-                         $class .= '-??-'; 
-                     } 
-                     $call_func = $class . $call_func; 
-                 } 
-                 $simple_call['function'] = $call_func; 
-             } 
-             if (!empty($call['args'])) { 
-                 $args = []; 
-                 foreach ($call['args'] as $akey => $aval) { 
-                         $args[$akey] = "array({$len}): " . self::condenseArray($aval); 
-                     } else { 
-                         $args[$akey] = self::readableVar($aval); 
-                     } 
-                 } 
-                 $simple_call['args'] = $args; 
-             } 
-             $simple_trace[] = $simple_call; 
-         } 
-         return $simple_trace; 
-     } 
-   
-   
-     /** 
-      * Converts a variable into something human readable 
-      * @param mixed $var 
-      * @return string 
-      */ 
-     public static function readableVar($var) 
-     { 
-         if (is_array($var)) return self::condenseArray($var); 
-         if (is_bool($var)) return $var- ?  'true': 'false';
 
-         } 
-         return 'unknown'; 
-     } 
-   
-   
-     /** 
-      * Condenses an array into a string 
-      */ 
-     public-  static  function-  condenseArray (array $arr)
 
-     { 
-         $int_keys = true; 
-         foreach ($keys as $key) { 
-                 $int_keys = false; 
-                 break; 
-             } 
-         } 
-   
-         $str = '['; 
-         $arg_num = 0; 
-         foreach ($arr as $key => $val) { 
-             if (++$arg_num != 1) $str .= ', '; 
-             if (!$int_keys) $str .= self::readableVar($key) . ' => '; 
-             $str .= self::readableVar($val); 
-         } 
-         $str .= ']'; 
-         return $str; 
-     } 
-   
-   
-     /** 
-     * Checks a URL that is to be used for redirection is valid. 
-     * 
-     * Will allow remote URLs beginning with 'http://' and local URLs beginning with '/' 
-     **/ 
-     public static function checkRedirect($text) 
-     { 
-         $text = Enc::cleanfunky($text); 
-         if (preg_match('!^http(s?)://[a-z]!', $text)) return true; 
-   
-         return false; 
-     } 
-   
-   
-     /** 
-     * Returns an absolute URL for the web root of this server 
-     * 
-     * Example: 'http://thejosh.info/sprout_test/' 
-     * 
-     * @param string $protocol Protocol to use. 'http' or 'https'. 
-     *    Defaults to server config option, which if not set, uses current request protocol. 
-     **/ 
-     public static function absRoot($protocol = '') 
-     { 
-         if ($protocol == '') { 
-             $protocol = Kohana::config('config.site_protocol'); 
-         } 
-         if ($protocol == '') { 
-             $protocol = Request::protocol(); 
-         } 
-         if ($protocol == '') { 
-             $protocol = 'http'; 
-         } 
-   
-         return Url::base(true, $protocol); 
-     } 
-   
-   
-     /** 
-     * Takes a mysql DATETIME value (will probably also work with a TIME or DATE value) 
-     * and formats it according to the format codes specified by the PHP date() function. 
-     * 
-     * The format is optional, if omittted, uses 'd/m/Y g:i a' = '7/11/2010 5:27 pm' 
-     **/ 
-     public static function formatMysqlDatetime($date, $format = 'd/m/Y g:i a') 
-     { 
-     } 
-   
-   
-     /** 
-     * Returns a string of random numbers and letters 
-     **/ 
-     public static function randStr($length = 16, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890') 
-     { 
-         $num_chars = strlen($chars) - 1; 
-   
-         $string = ''; 
-         for ($i = 0; $i < $length; $i++) { 
-             $string .= $chars[mt_rand(0, $num_chars)]; 
-         } 
-   
-         return $string; 
-     } 
-   
-   
-     /** 
-     * Returns a time in 'x minutes ago' format. 
-     * 
-     * Very small times (0, 1 seconds) are considered 'Just now'. 
-     * Times are represneted in seconds, minutes, hours or days. 
-     * 
-     * @param int $timediff Amount of time that has passed, in seconds. 
-     **/ 
-     public static function timeAgo($timediff) 
-     { 
-         $timediff = (int) $timediff; 
-   
-         if ($timediff < 2) return 'Just now'; 
-   
-         if ($timediff >= 86400) { 
-             $unit = ' day'; 
-             $time = floor($timediff / 86400); 
-   
-         } else if ($timediff >= 3600) { 
-             $unit = ' hour'; 
-             $time = floor($timediff / 3600); 
-   
-         } else if ($timediff >= 60) { 
-             $unit = ' minute'; 
-             $time = floor($timediff / 60); 
-   
-         } else { 
-             $unit = ' second'; 
-             $time = $timediff; 
-   
-         } 
-   
-         return $time . $unit . ($time == 1 ? ' ago' : 's ago'); 
-     } 
-   
-   
-     /** 
-     * Load the text for an extra page. 
-     * Returns NULL on error. 
-     * 
-     * @param int $type The page type, should be one of the type constants 
-     **/ 
-     public static function extraPage($type) 
-     { 
-         $subsite_id = SubsiteSelector::$content_id; 
-   
-         $q = "SELECT text 
-             FROM ~extra_pages 
-             WHERE subsite_id = ? AND type = ? 
-             ORDER BY id 
-             LIMIT 1"; 
-         try { 
-             $row = Pdb::q($q, [$subsite_id, $type], 'row'); 
-         } catch (QueryException $ex) { 
-             return null; 
-         } 
-   
-         return $row['text']; 
-     } 
-   
-   
-     /** 
-     * Attempts to put the handbrake on a script which is doing malicious inserts to the database 
-     * 
-     * @param string $table The table name, not prefixed 
-     * @param string $column The column to check 
-     * @param string $value The value to check 
-     * @param string $limit The number of inserts allowed in the provided time 
-     * @param string $time The amount of time the limit applies for, in seconds. Default = 1 hour 
-     * @param array $conds Additional conditions for the WHERE clause, formatted as per {@see Pdb::buildClause} 
-     * @return bool True if the insert rate is OK 
-     **/ 
-     public-  static  function-  checkInsertRate ($table, $column, $value, $limit, $time = 3600, array $conds = [])
 
-     { 
-         Pdb::validateIdentifier($table); 
-         Pdb::validateIdentifier($column); 
-   
-         $params = [$value, (int)$time]; 
-         $clause = Pdb::buildClause($conds, $params); 
-         if ($clause) $clause = 'AND ' . $clause; 
-   
-         $q = "SELECT COUNT(id) AS C 
-             FROM ~{$table} 
-             WHERE {$column} LIKE ? 
-                 AND date_added > DATE_SUB(NOW(), INTERVAL ? SECOND) 
-                 {$clause}"; 
-         $count = Pdb::q($q, $params, 'val'); 
-   
-         if ($count >= $limit) return false; 
-   
-         return true; 
-     } 
-   
-   
-     /** 
-     * Back-end for link-checking tool 
-     **/ 
-     public static function linkChecker() 
-     { 
-         throw new Exception('Not in use any more; use the worker "WorkerLinkChecker".'); 
-     } 
-   
-   
-     /** 
-     * Takes two strings of text (which will be stripped of HTML tags) 
-     * and returns HTML which is a table showing the differences 
-     * in a nice colourful way 
-     **/ 
-     public static function colorisedDiff($orig, $new) 
-     { 
-         $tmp_name1 = tempnam('/tmp', 'dif'); 
-   
-         $tmp_name2 = tempnam('/tmp', 'dif'); 
-   
-         $diff = shell_exec("diff -yat --left-column --width=3004 {$tmp_name1} {$tmp_name2}"); 
-   
-   
-         // Colorise diff 
-         $out = '<table cellpadding="5" cellspacing="3">'; 
-         $out .= '<tr><td> </td>'; 
-         $out .= '<th style="width: 420px;" bgcolor="#CECECE">Old revision (paragraph-by-paragraph)</th>'; 
-         $out .= '<th style="width: 420px;" bgcolor="#CECECE">New revision (paragraph-by-paragraph)</th>'; 
-         $out .= '</tr>'; 
-   
-         foreach ($diff as &$line) { 
-             if (! preg_match('/^(.{1,1500}) (.) ? ?(.{1,1500})?$/', $line, $matches)) continue; 
-             @list($nop, $left, $char, $right) = $matches; 
-   
-             if ($left == '' and $right == '') continue; 
-   
-             $line = $left . '<b>' . $char . '</b>' . $right; 
-   
-             if (strlen($left) >= 1500) $left .= '...'; 
-             if (strlen($right) >= 1500) $right .= '...'; 
-   
-             switch ($char) { 
-                 case '(': 
-                     //$out .= '<tr><td><b>Not changed</b></td><td>' . $left . '</td><td>' . $left . '</td></tr>'; 
-                     break; 
-   
-                 case '|': 
-                     $out .= '<tr><td><b>Changed</b></td><td bgcolor="#D8F1FF">' . $left . '</td><td bgcolor="#D8F1FF">' . $right . '</td></tr>'; 
-                     break; 
-   
-                 case '<': 
-                     $out .= '<tr><td><b>Removed</b></td><td bgcolor="#FCA7AE">' . $left . '</td><td bgcolor="#FFDDDF"> </td></tr>'; 
-                     break; 
-   
-                 case '>': 
-                     $out .= '<tr><td><b>Added</b></td><td bgcolor="#E6FADD"> </td><td bgcolor="#C9FFB3">' . $right . '</td></tr>'; 
-                     break; 
-             } 
-         } 
-         $out .= '</table>'; 
-   
-         return $out; 
-     } 
-   
-   
-     /** 
-     * Set the etag header, and some expiry headers. 
-     * Checks if the etag matches - if it does, terminates the script with '304 Not Modified'. 
-     * 
-     * ETag should be specified as a string. 
-     * Expires should be specified as a number of seconds, after that time the URL will expire. 
-     * 
-     * ETags should be something which is unique for that version of the URL. They should use 
-     * something which is collission-resistant, such as MD5. They should vary based on the 
-     * Accept-Encoding header, or any other 'accept' headers, if you are supporting them. 
-     **/ 
-     public static function etag($etag, $expires) 
-     { 
-         header('ETag: "' . $etag . '"'); 
-         header('Cache-Control: store, cache, must-revalidate, max-age=' . $expires); 
-   
-         if ($_SERVER['HTTP_IF_NONE_MATCH']) { 
-             $match = str_replace('"', '', $_SERVER['HTTP_IF_NONE_MATCH']); 
-             if ($match == $etag) { 
-                 header('HTTP/1.0 304 Not Modified'); 
-             } 
-         } 
-     } 
-   
-   
-     /** 
-     * Translate an array which may contain a page_id, filename or url into the final URL to use 
-     **/ 
-     public static function translateLink($row) 
-     { 
-         if ($row['page_id']) { 
-             $root = Navigation::getRootNode(); 
-             $page = $root->findNodeValue('id', $row['page_id']); 
-             if ($page) { 
-                 return $page->getFriendlyUrl(); 
-             } 
-         } 
-   
-         if ($row['filename']) { 
-             return File::absUrl($row['filename']); 
-         } 
-   
-         if ($row['url']) { 
-             return $row['url']; 
-         } 
-   
-         return null; 
-     } 
-   
-   
-     /** 
-     * Return the last-modified date of all pages on the (sub-)site 
-     * Returns NULL on error. 
-     * 
-     * The date is formatted using the php date function. 
-     * The default date format is "d/m/Y". 
-     * 
-     * @param string $date_format The date format to return the date in 
-     * @return string Last modified date 
-     * @return null On error 
-     **/ 
-     public static function lastModified($date_format = 'd/m/Y') 
-     { 
-         try { 
-             $q = "SELECT date_modified 
-                 FROM ~pages 
-                 WHERE subsite_id = ? 
-                 ORDER BY date_modified DESC 
-                 LIMIT 1"; 
-             $date = Pdb::query($q, [SubsiteSelector::$content_id], 'val'); 
-   
-         } catch (QueryException $ex) { 
-             return null; 
-         } 
-     } 
-   
-   
-     /** 
-     * Adds classes, analytics and target-blank to file links. 
-     * Also adds a random string, which prevents caching, solving some problems we were having with some clients. 
-     **/ 
-     public static function specialFileLinks($html) 
-     { 
-         // Grabs <a> links, with href containing: 
-         //  - optional something 
-         //  - "files/" 
-         //  - something 
-         //  - "." 
-         //  - some letters (a-z) 
-         // and the A must only have non HTML content (doesn't contain < or >) 
-         // 
-             '!<a[^>]+href="([^"]*)files/([^"]+\.([a-z]+))"[^>]*>([^<>]+)</a>!', 
-   
-             function($matches) { 
-   
-                 // Only mangle local URLs; leave remote URLs alone 
-                 $http_pattern = '#^(?:https?:)?//([^/]*)#'; 
-                 $link_matches = []; 
-                 $link_matches_pattern = preg_match($http_pattern, $matches[1], $link_matches); 
-                 $own_domain_matches = []; 
-                 $url_base = Subsites::getAbsRoot(SubsiteSelector::$subsite_id); 
-                 $own_domain_matches_pattern = preg_match($http_pattern, $url_base, $own_domain_matches); 
-   
-                 // Local URLs 
-                 $url = File::relUrl($matches[2]) . '?v=' . mt_rand(100, 999); 
-   
-                 // Remote URLs 
-                 if ($link_matches_pattern and $own_domain_matches_pattern) { 
-                     $link_domain = preg_replace('/^www\./', '', $link_matches[1]); 
-                     $own_domain = preg_replace('/^www\./', '', $own_domain_matches[1]); 
-                     if ($link_domain != $own_domain) { 
-                         return $matches[0]; 
-                     } 
-                 } 
-   
-                 $class = 'document document-' . $matches[3]; 
-                 $onclick = "ga('send', 'event', 'Document', 'Download', '" . Enc::js($matches[2]) . "');"; 
-   
-                 if (preg_match('!class="([^"]+)"!', $matches[0], $m)) { 
-                 } 
-   
-                 $out = '<a href="' . Enc::html($url) . '"'; 
-                 $out .= ' class="' .-  Enc ::html(trim($class)) . '"';
 
-                 $out .= ' target="_blank"'; 
-                 $out .= ' data-ext="' . Enc::html($matches[3]) . '"'; 
-                 $out .= ' data-size="' .-  Enc ::html(File::humanSize(File::size($matches[2]))) . '"';
 
-                 $out .= ' onclick="' . Enc::html($onclick) . '">'; 
-                 $out .= Enc::html($matches[4]); 
-                 $out .= '</a>'; 
-   
-                 return $out; 
-             }, 
-   
-             $html 
-         ); 
-     } 
-   
-   
-     /** 
-     * Return true if the browser supports drag-and-drop uploads. 
-     **/ 
-     public static function browserDragdropUploads() 
-     { 
-             'Firefox' => '4.0.0', 
-             'Internet Explorer' => '10.0', 
-             'Chrome' => '13.0.0', 
-             'Safari' => '6.0.0', 
-         ); 
-   
-         if (! isset($supported[- Kohana ::userAgent('browser')])) {
 
-             return false; 
-         } 
-   
-         $min_version = $supported[Kohana::userAgent('browser')]; 
-   
-     } 
-   
-   
-     /** 
-     * @deprecated Use {@see Security::passwordComplexity} instead 
-     **/ 
-     public static function passwordComplexity($str) 
-     { 
-         $errs = Security::passwordComplexity($str, 8, 0, false); 
-         if (count($errs) == 0) return true; 
-         return $errs; 
-     } 
-   
-   
-     /** 
-     * Return a list of admins to send emails to. 
-     * 
-     * The return value is an array of arrays. 
-     * The inner arrays contains the keys "name" and "email". 
-     **/ 
-     public static function adminEmails() 
-     { 
-   
-         $ops = AdminPerms::getOperatorsWithAccess('access_reportemail'); 
-         foreach ($ops as $row) { 
-                 'name' => $row['name'], 
-                 'email' => $row['email'], 
-             ); 
-         } 
-   
-         return $out; 
-     } 
-   
-   
-     /** 
-     * Check an IP against a list of IP addresses, with logic for CIDR ranges 
-     * 
-     * @return bool True if the IP is in the list, false if it's not 
-     **/ 
-     public static function ipaddressInArray($needle, $haystack) 
-     { 
-         foreach ($haystack as $check) { 
-   
-             if (count($parts) == 1) { 
-                 // Plain IP 
-                 if ($needle == $parts[0]) return true; 
-   
-             } else { 
-                 // CIDR 
-                 list($subnet, $mask) = $parts; 
-                 $mask = ~((1 << (32 - $mask)) - 1); 
-   
-                 // Correctly handle unaligned subnets 
-                 $subnet = ip2long($subnet) & $mask; 
-                 if ((ip2long($needle) & $mask) === $subnet) { 
-                     return true; 
-                 } 
-             } 
-         } 
-   
-         return false; 
-     } 
-   
-   
-     /** 
-      * Returns the memory limit in bytes. If there is no limit, returns INT_MAX. 
-      * 
-      * @return int Bytes 
-      */ 
-     public static function getMemoryLimit() 
-     { 
-         $memory_limit = ini_get('memory_limit'); 
-   
-         if ($memory_limit == -1) return INT_MAX; 
-   
-         if (preg_match('/^(\d+)(.)$/', $memory_limit, $matches)) { 
-             if ($matches[2] == 'G') return $matches[1] * 1024 * 1024 * 1024; 
-             if ($matches[2] == 'M') return $matches[1] * 1024 * 1024; 
-             if ($matches[2] == 'K') return $matches[1] * 1024; 
-         } else { 
-             return $memory_limit; 
-         } 
-     } 
-   
-     /** 
-      * Gets the first key value pair of an iterable 
-      * 
-      * This is to replace `reset` which has been deprecated in 7.2. While this lacks the 
-      * stateful behaviour of the original (i.e. changing the internal pointer) it does 
-      * recreate the most used feature: fetching the first element without knowing its key. 
-      * 
-      * @param iterable $iter An array or Traversable 
-      * @return array|null An array of [key, value] or null if the iterable is empty 
-      * @example 
-      *          list ($key, $value) = Sprout::iterableFirst(['an' => 'array']); 
-      */ 
-     public static function iterableFirst($iter) 
-     { 
-         foreach ($iter as $k => $v) { 
-             return [$k, $v]; 
-         } 
-   
-         return null; 
-     } 
-   
-     /** 
-      * Gets the first key of an iterable 
-      * 
-      * @param iterable $iter An array or Traversable 
-      * @return mixed|null The value or null if the iterable is emtpy 
-      */ 
-     public static function iterableFirstKey($iter) 
-     { 
-         return @static::iterableFirst($iter)[0]; 
-     } 
-   
-     /** 
-      * Gets the first value of an iterable 
-      * 
-      * @param iterable $iter An array or Traversable 
-      * @return mixed|null The value or null if the iterable is empty 
-      */ 
-     public static function iterableFirstValue($iter) 
-     { 
-         return @static::iterableFirst($iter)[1]; 
-     } 
- } 
-