diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7d38942 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,44 @@ +# Autodetect text files (convert CRLF => LF) +* text=auto + +# ...Unless the name matches the following overriding patterns + +# Definitively text files +*.c text +*.clj text +*.cpp text +*.css text +*.h text +*.htm text +*.html text +*.java text +*.js text +*.json text +*.less text +*.lisp text +*.php text +*.py text +*.rb text +*.script text +*.sql text +*.txt text +*.xml text +*.yml text + +# Ensure those won't be messed up with +*.gif binary +*.jpg binary +*.png binary + +*.jar binary + +*.doc binary +*.DOC binary +*.docx binary +*.DOCX binary +*.dot binary +*.DOT binary +*.pdf binary +*.PDF binary +*.rtf binary +*.RTF binary \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..778247a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +# See: http://about.travis-ci.org/docs/user/build-configuration/ + +language: php +php: + - 5.3 + +before_script: + - cp phpunit-dist.xml phpunit.xml + - composer install +script: phpunit --configuration phpunit.xml + +notifications: + on_success: always + on_failure: always diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cbc32d7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +ChangeLog +========= + +## Version 1.1.0 (2013-05-19) + +First release on Github. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..02bbb60 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8e8cd2 --- /dev/null +++ b/README.md @@ -0,0 +1,260 @@ +# Helpers +[![Build Status](https://secure.travis-ci.org/geoffroy-aubry/Helpers.png?branch=stable)](http://travis-ci.org/geoffroy-aubry/Helpers) + +Some helpers used in several personal packages. + +## Description +Static methods of `Helpers` class: + +* [arrayMergeRecursiveDistinct](#desc.arrayMergeRecursiveDistinct) +* [exec](#desc.exec) +* [flattenArray](#desc.flattenArray) +* [intToMultiple](#desc.intToMultiple) +* [numberFormat](#desc.numberFormat) +* [isAssociativeArray](#desc.isAssociativeArray) +* [round](#desc.round) +* [stripBashColors](#desc.stripBashColors) +* [strPutCSV](#desc.strPutCSV) +* [ucwordWithDelimiters](#desc.ucwordWithDelimiters) +* [utf8Encode](#desc.utf8Encode) + + +### arrayMergeRecursiveDistinct() +```php +/** + * array_merge_recursive() does indeed merge arrays, but it converts values with duplicate + * keys to arrays rather than overwriting the value in the first array with the duplicate + * value in the second array, as array_merge does. I.e., with array_merge_recursive(), + * this happens (documented behavior): + * + * array_merge_recursive(array('key' => 'org value'), array('key' => 'new value')); + * ⇒ array('key' => array('org value', 'new value')); + * + * arrayMergeRecursiveDistinct() does not change the datatypes of the values in the arrays. + * Matching keys' values in the second array overwrite those in the first array, as is the + * case with array_merge, i.e.: + * + * arrayMergeRecursiveDistinct(array('key' => 'org value'), array('key' => 'new value')); + * ⇒ array('key' => array('new value')); + * + * EVO on indexed arrays: + * Before: + * arrayMergeRecursiveDistinct(array('a', 'b'), array('c')) => array('c', 'b') + * Now: + * ⇒ array('c') + * + * @param array $aArray1 + * @param array $aArray2 + * @return array An array of values resulted from strictly merging the arguments together. + * @author Daniel + * @author Gabriel Sobrinho + * @author Geoffroy Aubry + * @see http://fr2.php.net/manual/en/function.array-merge-recursive.php#89684 + */ +public static function arrayMergeRecursiveDistinct (array $aArray1, array $aArray2); +``` + + +### exec() +```php +/** + * Executes the given shell command and returns an array filled with every line of output from the command. + * Trailing whitespace, such as \n, is not included in this array. + * On shell error (error code <> 0), throws a RuntimeException with error message.. + * + * @param string $sCmd shell command + * @param string $sOutputPath optional redirection of standard output + * @return array array filled with every line of output from the command + * @throws RuntimeException if shell error + */ +public static function exec ($sCmd, $sOutputPath=''); +``` + + +### flattenArray() +```php +/** + * Flatten a multidimensional array (keys are ignored). + * + * @param array $aArray + * @return array a one dimensional array. + * @see http://stackoverflow.com/a/1320156/1813519 + */ +public static function flattenArray (array $aArray); +``` +Example: +```php +$a = array( + 1, + 'a' => array( + 'b' => array('c', 2), + 'd' + ) +); +print_r(Helpers::flattenArray($a)); +``` +⇒ +```php +Array( + [0] => 1 + [1] => 'c' + [2] => 2 + [3] => 'd' +) +``` + + +### intToMultiple() +```php +/** + * Returns specified value in the most appropriate unit, with that unit. + * If $bBinaryPrefix is FALSE then use SI units (i.e. k, M, G, T), + * else use IED units (i.e. Ki, Mi, Gi, Ti). + * @see http://en.wikipedia.org/wiki/Binary_prefix + * + * @param int $iValue + * @param bool $bBinaryPrefix + * @return array a pair constituted by specified value in the most appropriate unit and that unit + */ +public static function intToMultiple ($iValue, $bBinaryPrefix=false); +``` +Example: +```php +print_r(Helpers::intToMultiple(17825792, false)); +print_r(Helpers::intToMultiple(17825792, true)); +``` +⇒ +```php +Array( + [0] => 17.825792 + [1] => 'M' +) +Array( + [0] => 17 + [1] => 'Mi' +) +``` + + +### numberFormat() +```php +/** + * Format a number with grouped thousands. + * It is an extended version of number_format() that allows do not specify $decimals. + * + * @param float $fNumber The number being formatted. + * @param string $sDecPoint Sets the separator for the decimal point. + * @param string $sThousandsSep Sets the thousands separator. Only the first character of $thousands_sep is used. + * @param int $iDecimals Sets the number of decimal points. + * @return string A formatted version of $number. + */ +public static function numberFormat ($fNumber, $sDecPoint='.', $sThousandsSep=',', $iDecimals=null); +``` + + +### isAssociativeArray() +```php +/** + * Returns TRUE iff the specified array is associative. + * Returns FALSE if the specified array is empty. + * + * http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential + * + * @param array $aArray + * @return bool true ssi iff the specified array is associative + */ +public static function isAssociativeArray (array $aArray); +``` + + +### round() +```php +/** + * Rounds specified value with precision $iPrecision as native round() function, but keep trailing zeros. + * + * @param float $fValue value to round + * @param int $iPrecision the optional number of decimal digits to round to (can also be negative) + * @return string + */ +public static function round ($fValue, $iPrecision=0); +``` + + +### stripBashColors() +```php +/** + * Remove all Bash color sequences from the specified string. + * + * @param string $sMsg + * @return string specified string without any Bash color sequence. + */ +public static function stripBashColors ($sMsg); +``` + + +### strPutCSV() +```php +/** + * Formats a line passed as a fields array as CSV and return it, without the trailing newline. + * Inspiration: http://www.php.net/manual/en/function.str-getcsv.php#88773 + * + * @param array $aInput + * @param string $sDelimiter + * @param string $sEnclosure + * @return string specified array converted into CSV format string + */ +public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"'); +``` + + +### ucwordWithDelimiters() +```php +/** + * Returns a string with the first character of each word in specified string capitalized, + * if that character is alphabetic. + * Additionally, each character that is immediately after one of $aDelimiters will be capitalized too. + * + * @param string $sString + * @param array $aDelimiters + * @return string + */ +public static function ucwordWithDelimiters ($sString, array $aDelimiters=array()); +``` +Eaxmple: +```php +echo Helpers::ucwordWithDelimiters("hel-lo wo'rld", array('-', "'")); +``` +⇒ +```php +"Hel-Lo Wo'Rld" +``` + + +### utf8Encode() +```php +/** + * Returns the UTF-8 translation of the specified string, only if not already in UTF-8. + * + * @param string $s + * @return string the UTF-8 translation of the specified string, only if not already in UTF-8. + */ +public static function utf8Encode ($s); +``` + +## Copyrights & licensing +Licensed under the GNU Lesser General Public License v3 (LGPL version 3). +See [LICENSE](https://github.com/geoffroy-aubry/Helpers/blob/stable/LICENSE) file for details. + +## ChangeLog +See [CHANGELOG](https://github.com/geoffroy-aubry/Helpers/blob/stable/CHANGELOG.md) file for details. + +## Running tests +To run the test suite, simply: + +```bash +$ cp phpunit-dist.php phpunit.php # and adapt, if necessary +$ phpunit -c phpunit.xml +``` + +## Git branching model +The git branching model used for development is the one described and assisted by `twgit` tool: [https://github.com/Twenga/twgit](https://github.com/Twenga/twgit). diff --git a/composer.json b/composer.json index f9795f6..e4e1d31 100644 --- a/composer.json +++ b/composer.json @@ -1,19 +1,19 @@ { - "name": "geoffroy-aubry/tools", - "description": "bla bla", - "keywords": ["bla"], + "name": "geoffroy-aubry/helpers", + "description": "Some helpers used in several personal packages.", + "keywords": ["helpers"], "type": "library", "license": "LGPL-3.0+", "authors": [ { "name": "Geoffroy Aubry", - "email": "gaubry@hi-media.com" + "email": "geoffroy.aubry@free.fr" } ], "require": { "php": ">=5.3.3" }, "autoload": { - "psr-0": {"GAubry\\Tools": "src/"} + "psr-0": {"GAubry\\Helpers": "src/"} } } diff --git a/phpunit-dist.xml b/phpunit-dist.xml index 570b6a1..9708f90 100644 --- a/phpunit-dist.xml +++ b/phpunit-dist.xml @@ -9,7 +9,7 @@ convertWarningsToExceptions="true" syntaxCheck="true" processIsolation="false" - colors="true" + colors="false" strict="true" verbose="true" stopOnFailure="false" @@ -27,7 +27,7 @@ - + tests @@ -35,7 +35,7 @@ + * Licensed under the GNU Lesser General Public License v3 (LGPL version 3). + * + * @copyright 2013 Geoffroy Aubry + * @license http://www.gnu.org/licenses/lgpl.html + */ +class Helpers +{ + private function __construct() + { + } + + /** + * Flatten a multidimensional array (keys are ignored). + * + * @param array $aArray + * @return array a one dimensional array. + * @see http://stackoverflow.com/a/1320156/1813519 + */ + public static function flattenArray (array $aArray) { + $aFlattened = array(); + array_walk_recursive($aArray, function($a) use (&$aFlattened) {$aFlattened[] = $a;}); + return $aFlattened; + } + + /** + * Returns the UTF-8 translation of the specified string, only if not already in UTF-8. + * + * @param string $s + * @return string the UTF-8 translation of the specified string, only if not already in UTF-8. + */ + public static function utf8Encode ($s) + { + return (utf8_encode(utf8_decode($s)) == $s ? $s : utf8_encode($s)); + } + + /** + * Executes the given shell command and returns an array filled with every line of output from the command. + * Trailing whitespace, such as \n, is not included in this array. + * On shell error (error code <> 0), throws a RuntimeException with error message.. + * + * @param string $sCmd shell command + * @param string $sOutputPath optional redirection of standard output + * @return array array filled with every line of output from the command + * @throws RuntimeException if shell error + */ + public static function exec ($sCmd, $sOutputPath='') + { + if (empty($sOutputPath)) { + $sFullCmd = '( ' . $sCmd . ' ) 2>&1'; + } else { + $sFullCmd = "( $sCmd ) 1>$sOutputPath 2>&1 & echo $!"; + } + exec($sFullCmd, $aResult, $iReturnCode); + if ($iReturnCode !== 0) { + throw new RuntimeException( + "Exit code not null: $iReturnCode. Result: '" . implode("\n", $aResult) . "'", + $iReturnCode + ); + } + return $aResult; + } + + /** + * Remove all Bash color sequences from the specified string. + * + * @param string $sMsg + * @return string specified string without any Bash color sequence. + */ + public static function stripBashColors ($sMsg) + { + return preg_replace('/\x1B\[([0-9]{1,2}(;[0-9]{1,2}){0,2})?[m|K]/', '', $sMsg); + } + + /** + * Rounds specified value with precision $iPrecision as native round() function, but keep trailing zeros. + * + * @param float $fValue value to round + * @param int $iPrecision the optional number of decimal digits to round to (can also be negative) + * @return string + */ + public static function round ($fValue, $iPrecision=0) + { + $sPrintfPrecision = max(0, $iPrecision); + return sprintf("%01.{$sPrintfPrecision}f", round($fValue, $iPrecision)); + } + + /** + * Returns a string with the first character of each word in specified string capitalized, + * if that character is alphabetic. + * Additionally, each character that is immediately after one of $aDelimiters will be capitalized too. + * + * @param string $sString + * @param array $aDelimiters + * @return string + */ + public static function ucwordWithDelimiters ($sString, array $aDelimiters=array()) + { + $sReturn = ucwords($sString); + foreach ($aDelimiters as $sDelimiter) { + if (strpos($sReturn, $sDelimiter) !== false) { + $sReturn = implode($sDelimiter, array_map('ucfirst', explode($sDelimiter, $sReturn))); + } + } + return $sReturn; + } + + /** + * Returns specified value in the most appropriate unit, with that unit. + * If $bBinaryPrefix is FALSE then use SI units (i.e. k, M, G, T), + * else use IED units (i.e. Ki, Mi, Gi, Ti). + * @see http://en.wikipedia.org/wiki/Binary_prefix + * + * @param int $iValue + * @param bool $bBinaryPrefix + * @return array a pair constituted by specified value in the most appropriate unit and that unit + */ + public static function intToMultiple ($iValue, $bBinaryPrefix=false) + { + static $aAllPrefixes = array( + 10 => array(12 => 'T', 9 => 'G', 6 => 'M', 3 => 'k', 0 => ''), + 2 => array(40 => 'Ti', 30 => 'Gi', 20 => 'Mi', 10 => 'Ki', 0 => ''), + ); + + $iBase = ($bBinaryPrefix ? 2 : 10); + $aPrefixes = $aAllPrefixes[$iBase]; + $m = 0; + foreach (array_keys($aPrefixes) as $iMultiple) { + if ($iValue >= pow($iBase, $iMultiple)) { + $m = $iMultiple; + break; + } + } + + return array($iValue / pow($iBase, $m), $aPrefixes[$m]); + } + + /** + * Format a number with grouped thousands. + * It is an extended version of number_format() that allows do not specify $decimals. + * + * @param float $fNumber The number being formatted. + * @param string $sDecPoint Sets the separator for the decimal point. + * @param string $sThousandsSep Sets the thousands separator. Only the first character of $thousands_sep is used. + * @param int $iDecimals Sets the number of decimal points. + * @return string A formatted version of $number. + */ + public static function numberFormat ($fNumber, $sDecPoint='.', $sThousandsSep=',', $iDecimals=null) + { + if ($iDecimals !== null) { + return number_format($fNumber, $iDecimals, $sDecPoint, $sThousandsSep); + } else { + $tmp = explode('.', $fNumber); + $out = number_format($tmp[0], 0, $sDecPoint, $sThousandsSep); + if (isset($tmp[1])) { + $out .= $sDecPoint.$tmp[1]; + } + return $out; + } + } + + /** + * Formats a line passed as a fields array as CSV and return it, without the trailing newline. + * Inspiration: http://www.php.net/manual/en/function.str-getcsv.php#88773 + * + * @param array $aInput + * @param string $sDelimiter + * @param string $sEnclosure + * @return string specified array converted into CSV format string + */ + public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"') + { + // Open a memory "file" for read/write... + $fp = fopen('php://temp', 'r+'); + fputcsv($fp, $aInput, $sDelimiter, $sEnclosure); + // ... rewind the "file" so we can read what we just wrote... + rewind($fp); + $sData = fgets($fp); + fclose($fp); + // ... and return the $data to the caller, with the trailing newline from fgets() removed. + return rtrim($sData, "\n"); + } + + /** + * array_merge_recursive() does indeed merge arrays, but it converts values with duplicate + * keys to arrays rather than overwriting the value in the first array with the duplicate + * value in the second array, as array_merge does. I.e., with array_merge_recursive(), + * this happens (documented behavior): + * + * array_merge_recursive(array('key' => 'org value'), array('key' => 'new value')); + * ⇒ array('key' => array('org value', 'new value')); + * + * arrayMergeRecursiveDistinct() does not change the datatypes of the values in the arrays. + * Matching keys' values in the second array overwrite those in the first array, as is the + * case with array_merge, i.e.: + * + * arrayMergeRecursiveDistinct(array('key' => 'org value'), array('key' => 'new value')); + * ⇒ array('key' => array('new value')); + * + * EVO on indexed arrays: + * Before: + * arrayMergeRecursiveDistinct(array('a', 'b'), array('c')) ⇒ array('c', 'b') + * Now: + * ⇒ array('c') + * + * @param array $aArray1 + * @param array $aArray2 + * @return array An array of values resulted from strictly merging the arguments together. + * @author Daniel + * @author Gabriel Sobrinho + * @author Geoffroy Aubry + * @see http://fr2.php.net/manual/en/function.array-merge-recursive.php#89684 + */ + public static function arrayMergeRecursiveDistinct (array $aArray1, array $aArray2) + { + $aMerged = $aArray1; + if (self::isAssociativeArray($aMerged)) { + foreach ($aArray2 as $key => &$value) { + if (is_array($value) && isset($aMerged[$key]) && is_array($aMerged[$key])) { + $aMerged[$key] = self::arrayMergeRecursiveDistinct($aMerged[$key], $value); + } else { + $aMerged[$key] = $value; + } + } + } else { + $aMerged = $aArray2; + } + return $aMerged; + } + + /** + * Returns TRUE iff the specified array is associative. + * Returns FALSE if the specified array is empty. + * + * http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential + * + * @param array $aArray + * @return bool true ssi iff the specified array is associative + */ + public static function isAssociativeArray (array $aArray) { + foreach (array_keys($aArray) as $key) { + if ( ! is_int($key)) { + return true; + } + } + return false; + } +} diff --git a/src/GAubry/Tools/Tools.php b/src/GAubry/Tools/Tools.php deleted file mode 100644 index 740b24d..0000000 --- a/src/GAubry/Tools/Tools.php +++ /dev/null @@ -1,196 +0,0 @@ - 0), lance une exception incluant le message d'erreur. - * - * @param string $sCmd - * @return array tableau indexé du flux de sortie shell découpé par ligne - * @throws RuntimeException en cas d'erreur shell - */ - public static function exec ($sCmd, $sOutputPath='') - { - if (empty($sOutputPath)) { - $sFullCmd = '( ' . $sCmd . ' ) 2>&1'; - } else { - $sFullCmd = "( $sCmd ) 1>$sOutputPath 2>&1 & echo $!"; - } - exec($sFullCmd, $aResult, $iReturnCode); - if ($iReturnCode !== 0) { - throw new RuntimeException( - "Exit code not null: $iReturnCode. Result: '" . implode("\n", $aResult) . "'", - $iReturnCode - ); - } - return $aResult; - } - - public static function stripBashColors ($sMsg) - { - return preg_replace('/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/', '', $sMsg); - } - - // Allow negative precision. - public static function round ($fValue, $iPrecision=0) - { - $sPrintfPrecision = max(0, $iPrecision); - return sprintf("%01.{$sPrintfPrecision}f", round($fValue, $iPrecision)); - } - - public static function ucwordWithDelimiters ($str, array $aDelimiters=array("'", '-')){ - $sReturn = ucwords(strtolower($str)); - foreach ($aDelimiters as $sDelimiter) { - if (strpos($sReturn, $sDelimiter) !== false) { - $sReturn = implode($sDelimiter, array_map('ucfirst', explode($sDelimiter, $sReturn))); - } - } - return $sReturn; - } - - public static function intToSI ($iValue) - { - $prefixes = array(12 => 'T', 9 => 'G', 6 => 'M', 3 => 'k', 0 => ''); - - $m = 0; - foreach ($prefixes as $multiple => $s) { - if ($iValue >= pow(10, $multiple)) { - $m = $multiple; - break; - } - } - - //$decimals = ($m > 0 && $val/pow(10, $m) < 100 ? 1 : 0); - $decimals = ($m === 0 ? 0 : 1); - return array(round($iValue / pow(10, $m), $decimals), $prefixes[$m]); - } - - /** - * Format a number with grouped thousands. - * It is an extended version of number_format() that allow do not specify $decimals. - * - * @param float $fNumber The number being formatted. - * @param string $sDecPoint Sets the separator for the decimal point. - * @param string $sThousandsSep Sets the thousands separator. Only the first character of $thousands_sep is used. - * @param int $iDecimals Sets the number of decimal points. - * @return string A formatted version of $number. - */ - public static function numberFormat ($fNumber, $sDecPoint='.', $sThousandsSep=',', $iDecimals=NULL) - { - if ($iDecimals !== NULL) { - return number_format($fNumber, $iDecimals, $sDecPoint, $sThousandsSep); - } else { - $tmp = explode('.', $fNumber); - $out = number_format($tmp[0], 0, $sDecPoint, $sThousandsSep); - if (isset($tmp[1])) { - $out .= $sDecPoint.$tmp[1]; - } - return $out; - } - } - - /** - * Retourne un couple comprenant d'une part le nombre d'octets contenus dans la plus grande unité informatique - * inférieure à la taille spécifiée, et d'autre part le nom de cette unité. - * - * Par exemple, si $iFileSize vaut 2000, alors le résultat sera : array(1024, 'Kio'). - * - * @param int $iFileSize taille en octets à changer d'unité - * @return array tableau (int, string) comprenant d'une part le nombre d'octets contenus dans la plus grande - * unité inférieure à la taille spécifiée, et d'autre part le nom de cette unité. - */ - public static function getFileSizeUnit ($iFileSize) - { - if ($iFileSize < 1024) { - $iUnit = 1; - $sUnit = 'o'; - } else if ($iFileSize < 1024*1024) { - $iUnit = 1024; - $sUnit = 'Kio'; - } else { - $iUnit = 1024*1024; - $sUnit = 'Mio'; - } - return array($iUnit, $sUnit); - } - - /** - * Retourne un couple comprenant d'une part la taille spécifiée arrondie, - * et d'autre part l'unité dans laquelle la taille a été arrondie. - * - * Le second paramètre, si <> de 0, permet de spécifier une taille de référence pour le calcul de l'unité. - * - * Par exemple : - * (100, 0) => ('100', 'o') - * (100, 2000000) => ('<1', 'Mio') - * (200, 0) => ('2', 'Kio') - * - * @param int $iSize taille à convertir - * @param int $iRefSize référentiel de conversion, si différent de 0 - * @return array un couple comprenant d'une part la taille spécifiée arrondie, - * et d'autre part l'unité dans laquelle la taille a été arrondie. - */ - public static function convertFileSize2String ($iSize, $iRefSize=0) - { - if ($iRefSize === 0) { - $iRefSize = $iSize; - } - list($iUnit, $sUnit) = self::getFileSizeUnit($iRefSize); - - $sFileSize = round($iSize/$iUnit); - if ($sFileSize == 0 && $iSize > 0) { - $sFileSize = '<1'; - } - return array($sFileSize, $sUnit); - } - - public static function strPutCSV ($input, $delimiter = ',', $enclosure = '"') { - // Open a memory "file" for read/write... - $fp = fopen('php://temp', 'r+'); - // ... write the $input array to the "file" using fputcsv()... - fputcsv($fp, $input, $delimiter, $enclosure); - // ... rewind the "file" so we can read what we just wrote... - rewind($fp); - // ... read the entire line into a variable... - $data = fgets($fp); - // ... close the "file"... - fclose($fp); - // ... and return the $data to the caller, with the trailing newline from fgets() removed. - return rtrim( $data, "\n" ); - } -} diff --git a/tests/GAubry/Helpers/HelpersTest.php b/tests/GAubry/Helpers/HelpersTest.php new file mode 100644 index 0000000..cfce02e --- /dev/null +++ b/tests/GAubry/Helpers/HelpersTest.php @@ -0,0 +1,239 @@ + + */ +class HelpersTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @covers \GAubry\Helpers\Helpers::arrayMergeRecursiveDistinct + * @dataProvider dataProvider_testArrayMergeRecursiveDistinct + * + * @param array $aArray1 + * @param array $aArray2 + * @param array $aResult + */ + public function testArrayMergeRecursiveDistinct (array $aArray1, array $aArray2, array $aExpected) + { + $this->assertEquals($aExpected, Helpers::arrayMergeRecursiveDistinct($aArray1, $aArray2)); + } + + /** + * Data provider pour testArrayMergeRecursiveDistinct() + */ + public function dataProvider_testArrayMergeRecursiveDistinct () + { + $aArray1 = array('a' => 'b', 'c' => array('d' => 'e')); + return array( + array(array(), array(), array()), + array($aArray1, array(), $aArray1), + array(array(), $aArray1, $aArray1), + + array(array(1, 2), array(3), array(3)), + array(array(3), array(1, 2), array(1, 2)), + + array(array('a', 'b'), array('c'), array('c')), + array(array('c'), array('a', 'b'), array('a', 'b')), + + array(array(3 => 'a', 'b' => 'c'), array(3 => null), array(3 => null, 'b' => 'c')), + array(array(3 => null), array(3 => 'a', 'b' => 'c'), array(3 => 'a', 'b' => 'c')), + + array(array('a' => 'b'), array('a' => array(1, 2)), array('a' => array(1, 2))), + array(array('a' => array(1, 2)), array('a' => 'b'), array('a' => 'b')), + array(array('a' => array(1, 2)), array('a' => array(3)), array('a' => array(3))), + + array($aArray1, array('a' => 'x'), array('a' => 'x', 'c' => array('d' => 'e'))), + array(array('a' => 'x'), $aArray1, array('a' => 'b', 'c' => array('d' => 'e'))), + + array( + $aArray1, + array('c' => array('d' => 'x'), 'y' => 'z'), + array('a' => 'b', 'c' => array('d' => 'x'), 'y' => 'z') + ), + array( + array('c' => array('d' => 'x'), 'y' => 'z'), + $aArray1, + array('a' => 'b', 'c' => array('d' => 'e'), 'y' => 'z') + ), + ); + } + + /** + * @covers \GAubry\Helpers\Helpers::isAssociativeArray + * @dataProvider dataProvider_testIsAssociativeArray + * + * @param array $aArray + * @param bool $bResult + */ + public function testIsAssociativeArray ($aArray, $bExpected) + { + $this->assertEquals($bExpected, Helpers::isAssociativeArray($aArray)); + } + + /** + * Data provider pour testIsAssociativeArray() + */ + public function dataProvider_testIsAssociativeArray () + { + return array( + array(array(), false), + array(array('a'), false), + array(array('a' => 1), true), + array(array(1, 2), false), + array(array(1, 'a' => 2, 3), true), + ); + } + + /** + * @covers \GAubry\Helpers\Helpers::stripBashColors + * @dataProvider dataProvider_testStripBashColors + * + * @param string $sSource + * @param string $sExpected + */ + public function testStripBashColors ($sSource, $sExpected) + { + $this->assertEquals($sExpected, Helpers::stripBashColors($sSource)); + } + + /** + * Data provider pour testStripBashColors() + */ + public function dataProvider_testStripBashColors () + { + return array( + array('', ''), + array('a', 'a'), + array("\033[0m", ''), + array("\033[1;34m", ''), + array("\033[5;32;47m", ''), + array("\033[1;34mxyz", 'xyz'), + array("xyz\033[1;34m", 'xyz'), + array("x\033[1;34my\033[0mz", 'xyz'), + array("x\x1B[1;34my\x1B[0mz", 'xyz'), + ); + } + + /** + * @covers \GAubry\Helpers\Helpers::flattenArray + * @dataProvider dataProvider_testFlattenArray + * + * @param array $aSource + * @param array $aExpected + */ + public function testFlattenArray (array $aSource, array $aExpected) + { + $this->assertEquals($aExpected, Helpers::flattenArray($aSource)); + } + + /** + * Data provider pour testFlattenArray() + */ + public function dataProvider_testFlattenArray () + { + return array( + array(array(), array()), + array(array(1), array(1)), + array(array('a' => 'b'), array('b')), + array(array(1, 'a' => 'b'), array(1, 'b')), + array(array(array('a' => 'b')), array('b')), + array(array(1, 'a' => array('b' => array('c', 2), 'd')), array(1, 'c', 2, 'd')), + ); + } + + /** + * @covers \GAubry\Helpers\Helpers::intToMultiple + * @dataProvider dataProvider_testIntToMultiple + * + * @param int $iValue + * @param array $aExpected + */ + public function testIntToMultiple ($iValue, $bBinaryPrefix, array $aExpected) + { + $this->assertEquals($aExpected, Helpers::intToMultiple($iValue, $bBinaryPrefix)); + } + + /** + * Data provider pour testIntToMultiple() + */ + public function dataProvider_testIntToMultiple () + { + return array( + array(0, false, array(0, '')), + array(10, false, array(10, '')), + array(1024, false, array(1.024, 'k')), + array(17825792, false, array(17.825792, 'M')), + array(1073741824, false, array(1.073741824, 'G')), + + array(0, true, array(0, '')), + array(10, true, array(10, '')), + array(1024, true, array(1, 'Ki')), + array(17825792, true, array(17, 'Mi')), + array(1073741824, true, array(1, 'Gi')), + ); + } + + /** + * @covers \GAubry\Helpers\Helpers::round + * @dataProvider dataProvider_testRound + * + * @param float $fValue + * @param int $iPrecision + * @param string $sExpected + */ + public function testRound ($fValue, $iPrecision, $sExpected) + { + $this->assertEquals($sExpected, Helpers::round($fValue, $iPrecision)); + } + + /** + * Data provider pour testRound() + */ + public function dataProvider_testRound () + { + return array( + array(0, 0, '0'), + array(1, 3, '1.000'), + array(156.789, 0, '157'), + array(156.789, 2, '156.79'), + array(156.789, 4, '156.7890'), + array(156.789, -1, '160'), + array(156.789, -2, '200'), + array(156.789, -3, '0'), + ); + } + + /** + * @covers \GAubry\Helpers\Helpers::ucwordWithDelimiters + * @dataProvider dataProvider_testUcwordWithDelimiters + * + * @param string $sString + * @param array $aDelimiters + * @param string $sExpected + */ + public function testUcwordWithDelimiters ($sString, array $aDelimiters, $sExpected) + { + $this->assertEquals($sExpected, Helpers::ucwordWithDelimiters($sString, $aDelimiters)); + } + + /** + * Data provider pour testRound() + */ + public function dataProvider_testUcwordWithDelimiters () + { + return array( + array('hello world', array(), 'Hello World'), + array('HELLO world', array(), 'HELLO World'), + array('hel-lo world', array(), 'Hel-lo World'), + array('hel-lo world', array('-'), 'Hel-Lo World'), + array("hel-lo wo'rld", array('-', "'"), "Hel-Lo Wo'Rld"), + ); + } +} diff --git a/tests/GAubry/Tools/ToolsTest.php b/tests/GAubry/Tools/ToolsTest.php deleted file mode 100644 index c4b00ec..0000000 --- a/tests/GAubry/Tools/ToolsTest.php +++ /dev/null @@ -1,68 +0,0 @@ - - */ -class ToolsTest extends \PHPUnit_Framework_TestCase -{ - - /** - * @covers \GAubry\Tools\Tools::getFileSizeUnit - * @dataProvider dataProvider_testGetFileSizeUnit - * @param int $iFileSize taille en octets à changer d'unité - * @param array $aExpected tableau (int, string) comprenant d'une part le nombre d'octets contenus dans la plus grande - * unité inférieure à la taille spécifiée, et d'autre part le nom de cette unité. - */ - public function testGetFileSizeUnit ($iFileSize, $aExpected) - { - $aResult = Tools::getFileSizeUnit($iFileSize); - $this->assertEquals($aExpected, $aResult); - } - - /** - * Data provider pour testGetFileSizeUnit() - */ - public static function dataProvider_testGetFileSizeUnit () - { - return array( - array(0, array(1, 'o')), - array(100, array(1, 'o')), - array(2000, array(1024, 'Kio')), - array(2000000, array(1024*1024, 'Mio')), - ); - } - - /** - * @covers \GAubry\Tools\Tools::convertFileSize2String - * @dataProvider dataProvider_testConvertFileSize2String - * @param int $iSize taille à convertir - * @param int $iRefSize référentiel de conversion, si différent de 0 - * @param array $aExpected un couple comprenant d'une part la taille spécifiée arrondie, - * et d'autre part l'unité dans laquelle la taille a été arrondie. - */ - public function testConvertFileSize2String ($iSize, $iRefSize, $aExpected) - { - $aResult = Tools::convertFileSize2String($iSize, $iRefSize); - $this->assertEquals($aExpected, $aResult); - } - - /** - * Data provider pour testConvertFileSize2String() - */ - public static function dataProvider_testConvertFileSize2String () - { - return array( - array(0, 0, array('0', 'o')), - array(100, 0, array('100', 'o')), - array(100, 2000000, array('<1', 'Mio')), - array(2000, 0, array('2', 'Kio')), - array(2000000, 0, array('2', 'Mio')), - ); - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 77e6cb7..3ad8db8 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,5 +12,4 @@ exit(1); } -$oLoader = require __DIR__ . '/../vendor/autoload.php'; -$oLoader->add('GAubry\Tools\Tests', __DIR__); +require __DIR__ . '/../vendor/autoload.php';