From 6250dfc3af06b3d41ea03f7ab595835df69b3058 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Sat, 18 May 2013 17:25:35 +0200 Subject: [PATCH 1/9] [twgit] Init feature 'feature-array-merge-recursive'. From 150e32ab2ff2cde778161cc837b8cadae558b2ce Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Sat, 18 May 2013 17:37:44 +0200 Subject: [PATCH 2/9] Add arrayMergeRecursiveDistinct() and isAssociativeArray() --- phpunit-dist.xml | 2 +- src/GAubry/Tools/Tools.php | 64 +++++++++++++++++++++++++- tests/GAubry/Tools/ToolsTest.php | 79 ++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 2 deletions(-) diff --git a/phpunit-dist.xml b/phpunit-dist.xml index 570b6a1..583ccf6 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" diff --git a/src/GAubry/Tools/Tools.php b/src/GAubry/Tools/Tools.php index 740b24d..1c83025 100644 --- a/src/GAubry/Tools/Tools.php +++ b/src/GAubry/Tools/Tools.php @@ -31,7 +31,7 @@ public static function flatten (array $array) { * @param string $s * @return string la chaîne spécifiée, en l'encodant en UTF8 seulement si celle-ci ne l'était pas déjà. */ - public static function utf8_encode ($s) + public static function utf8Encode ($s) { return (utf8_encode(utf8_decode($s)) == $s ? $s : utf8_encode($s)); } @@ -193,4 +193,66 @@ public static function strPutCSV ($input, $delimiter = ',', $enclosure = '"') { // ... and return the $data to the caller, with the trailing newline from fgets() removed. return rtrim( $data, "\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 sur sous-tableaux indexés : + * Avant : + * array_merge_recursive_distinct(array('a', 'b'), array('c')) => array('c', 'b') + * Maintenant : + * => 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 + */ + 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; + } + + /** + * Retourne true ssi le tableau spécifié est associatif. + * Retourne false si le tableau est vide. + * 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 le tableau est associatif + */ + public static function isAssociativeArray (array $aArray) { + foreach (array_keys($aArray) as $key) { + if ( ! is_int($key)) { + return true; + } + } + return false; + } } diff --git a/tests/GAubry/Tools/ToolsTest.php b/tests/GAubry/Tools/ToolsTest.php index c4b00ec..9147a55 100644 --- a/tests/GAubry/Tools/ToolsTest.php +++ b/tests/GAubry/Tools/ToolsTest.php @@ -65,4 +65,83 @@ public static function dataProvider_testConvertFileSize2String () array(2000000, 0, array('2', 'Mio')), ); } + + /** + * @covers \GAubry\Tools\Tools::arrayMergeRecursiveDistinct + * @dataProvider dataProvider_testArrayMergeRecursiveDistinct + * + * @param array $aArray1 + * @param array $aArray2 + * @param array $aResult + */ + public function testArrayMergeRecursiveDistinct (array $aArray1, array $aArray2, array $aResult) + { + $this->assertEquals(Tools::arrayMergeRecursiveDistinct($aArray1, $aArray2), $aResult); + } + + /** + * Data provider pour testArrayMergeRecursiveDistinct() + */ + public static 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\Tools\Tools::isAssociativeArray + * @dataProvider dataProvider_testIsAssociativeArray + * + * @param unknown $aArray + * @param unknown $bResult + */ + public function testIsAssociativeArray ($aArray, $bResult) + { + $this->assertEquals(Tools::isAssociativeArray($aArray),$bResult); + } + + /** + * Data provider pour testIsAssociativeArray() + */ + public static 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), + ); + } } From 8467546fd1c3126c3716d2781809837fba4f2a00 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Sun, 19 May 2013 17:45:15 +0200 Subject: [PATCH 3/9] Add some unit tests. --- src/GAubry/Tools/Tools.php | 162 +++++++++++------------ tests/GAubry/Tools/ToolsTest.php | 216 ++++++++++++++++++++++--------- 2 files changed, 229 insertions(+), 149 deletions(-) diff --git a/src/GAubry/Tools/Tools.php b/src/GAubry/Tools/Tools.php index 1c83025..2894195 100644 --- a/src/GAubry/Tools/Tools.php +++ b/src/GAubry/Tools/Tools.php @@ -12,24 +12,23 @@ private function __construct() } /** - * Transforme un tableau multidimensionnel en un tableau à une seule dimension, - * en ramenant toutes les feuilles au premier niveau. + * Flatten a multidimensional array (keys are ignored). * * @param array $array * @return array tableau à une seule dimension. * @see http://stackoverflow.com/a/1320156/1813519 */ - public static function flatten (array $array) { - $return = array(); - array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; }); - return $return; + public static function flattenArray (array $aArray) { + $aFlattened = array(); + array_walk_recursive($aArray, function($a) use (&$aFlattened) {$aFlattened[] = $a;}); + return $aFlattened; } /** - * Retourne la chaîne spécifiée, en l'encodant en UTF8 seulement si celle-ci ne l'était pas déjà. + * Returns the UTF-8 translation of the specified string, only if not already in UTF-8. * * @param string $s - * @return string la chaîne spécifiée, en l'encodant en UTF8 seulement si celle-ci ne l'était pas déjà. + * @return string the UTF-8 translation of the specified string, only if not already in UTF-8. */ public static function utf8Encode ($s) { @@ -37,12 +36,13 @@ public static function utf8Encode ($s) } /** - * Exécute la commande shell spécifiée et retourne la sortie découpée par ligne dans un tableau. - * En cas d'erreur shell (code d'erreur <> 0), lance une exception incluant le message d'erreur. + * 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 - * @return array tableau indexé du flux de sortie shell découpé par ligne - * @throws RuntimeException en cas d'erreur shell + * @param string $sCmd shell command + * @return array array filled with every line of output from the command + * @throws RuntimeException if shell error */ public static function exec ($sCmd, $sOutputPath='') { @@ -61,20 +61,43 @@ public static function exec ($sCmd, $sOutputPath='') 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})?)?[m|K]/', '', $sMsg); + return preg_replace('/\x1B\[([0-9]{1,2}(;[0-9]{1,2}){0,2})?[m|K]/', '', $sMsg); } - // Allow negative precision. + /** + * Rounds specified value with precision $iPrecision as native round() function, + * but keep trailing zero. + * + * @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)); } - public static function ucwordWithDelimiters ($str, array $aDelimiters=array("'", '-')){ - $sReturn = ucwords(strtolower($str)); + /** + * 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))); @@ -83,21 +106,34 @@ public static function ucwordWithDelimiters ($str, array $aDelimiters=array("'", return $sReturn; } - public static function intToSI ($iValue) + /** + * 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) { - $prefixes = array(12 => 'T', 9 => 'G', 6 => 'M', 3 => 'k', 0 => ''); + 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 ($prefixes as $multiple => $s) { - if ($iValue >= pow(10, $multiple)) { - $m = $multiple; + foreach (array_keys($aPrefixes) as $iMultiple) { + if ($iValue >= pow($iBase, $iMultiple)) { + $m = $iMultiple; 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]); + return array($iValue / pow($iBase, $m), $aPrefixes[$m]); } /** @@ -125,73 +161,24 @@ public static function numberFormat ($fNumber, $sDecPoint='.', $sThousandsSep=', } /** - * 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'). + * 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 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é. + * @param array $aInput + * @param string $sDelimiter + * @param string $sEnclosure + * @return string specified array converted into CSV format string */ - 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 = '"') { + public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"') { // 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); + fputcsv($fp, $aInput, $sDelimiter, $sEnclosure); // ... 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"... + $sData = fgets($fp); fclose($fp); // ... and return the $data to the caller, with the trailing newline from fgets() removed. - return rtrim( $data, "\n" ); + return rtrim($sData, "\n"); } /** @@ -240,12 +227,13 @@ public static function arrayMergeRecursiveDistinct (array $aArray1, array $aArra } /** - * Retourne true ssi le tableau spécifié est associatif. - * Retourne false si le tableau est vide. + * 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 le tableau est associatif + * @return bool true ssi iff the specified array is associative */ public static function isAssociativeArray (array $aArray) { foreach (array_keys($aArray) as $key) { diff --git a/tests/GAubry/Tools/ToolsTest.php b/tests/GAubry/Tools/ToolsTest.php index 9147a55..40e44d6 100644 --- a/tests/GAubry/Tools/ToolsTest.php +++ b/tests/GAubry/Tools/ToolsTest.php @@ -12,60 +12,6 @@ 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')), - ); - } - /** * @covers \GAubry\Tools\Tools::arrayMergeRecursiveDistinct * @dataProvider dataProvider_testArrayMergeRecursiveDistinct @@ -74,15 +20,15 @@ public static function dataProvider_testConvertFileSize2String () * @param array $aArray2 * @param array $aResult */ - public function testArrayMergeRecursiveDistinct (array $aArray1, array $aArray2, array $aResult) + public function testArrayMergeRecursiveDistinct (array $aArray1, array $aArray2, array $aExpected) { - $this->assertEquals(Tools::arrayMergeRecursiveDistinct($aArray1, $aArray2), $aResult); + $this->assertEquals($aExpected, Tools::arrayMergeRecursiveDistinct($aArray1, $aArray2)); } /** * Data provider pour testArrayMergeRecursiveDistinct() */ - public static function dataProvider_testArrayMergeRecursiveDistinct () + public function dataProvider_testArrayMergeRecursiveDistinct () { $aArray1 = array('a' => 'b', 'c' => array('d' => 'e')); return array( @@ -123,18 +69,18 @@ public static function dataProvider_testArrayMergeRecursiveDistinct () * @covers \GAubry\Tools\Tools::isAssociativeArray * @dataProvider dataProvider_testIsAssociativeArray * - * @param unknown $aArray - * @param unknown $bResult + * @param array $aArray + * @param bool $bResult */ - public function testIsAssociativeArray ($aArray, $bResult) + public function testIsAssociativeArray ($aArray, $bExpected) { - $this->assertEquals(Tools::isAssociativeArray($aArray),$bResult); + $this->assertEquals($bExpected, Tools::isAssociativeArray($aArray)); } /** * Data provider pour testIsAssociativeArray() */ - public static function dataProvider_testIsAssociativeArray () + public function dataProvider_testIsAssociativeArray () { return array( array(array(), false), @@ -144,4 +90,150 @@ public static function dataProvider_testIsAssociativeArray () array(array(1, 'a' => 2, 3), true), ); } + + /** + * @covers \GAubry\Tools\Tools::stripBashColors + * @dataProvider dataProvider_testStripBashColors + * + * @param string $sSource + * @param string $sExpected + */ + public function testStripBashColors ($sSource, $sExpected) + { + $this->assertEquals($sExpected, Tools::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\Tools\Tools::flattenArray + * @dataProvider dataProvider_testFlattenArray + * + * @param array $aSource + * @param array $aExpected + */ + public function testFlattenArray (array $aSource, array $aExpected) + { + $this->assertEquals($aExpected, Tools::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, array('a' => array('b', 2), 'c')), array(1, 'b', 2, 'c')), + ); + } + + /** + * @covers \GAubry\Tools\Tools::intToMultiple + * @dataProvider dataProvider_testIntToMultiple + * + * @param int $iValue + * @param array $aExpected + */ + public function testIntToMultiple ($iValue, $bBinaryPrefix, array $aExpected) + { + $this->assertEquals($aExpected, Tools::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\Tools\Tools::round + * @dataProvider dataProvider_testRound + * + * @param float $fValue + * @param int $iPrecision + * @param string $sExpected + */ + public function testRound ($fValue, $iPrecision, $sExpected) + { + $this->assertEquals($sExpected, Tools::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\Tools\Tools::ucwordWithDelimiters + * @dataProvider dataProvider_testUcwordWithDelimiters + * + * @param string $sString + * @param array $aDelimiters + * @param string $sExpected + */ + public function testUcwordWithDelimiters ($sString, array $aDelimiters, $sExpected) + { + $this->assertEquals($sExpected, Tools::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"), + ); + } } From e53b4d29eaf72d8d18475ddcf30522c299a1ac49 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Sun, 19 May 2013 18:38:35 +0200 Subject: [PATCH 4/9] rename Tools -> Helpers --- composer.json | 6 ++-- phpunit-dist.xml | 4 +-- .../{Tools/Tools.php => Helpers/Helpers.php} | 7 ++-- .../ToolsTest.php => Helpers/HelpersTest.php} | 34 +++++++++---------- tests/bootstrap.php | 3 +- 5 files changed, 27 insertions(+), 27 deletions(-) rename src/GAubry/{Tools/Tools.php => Helpers/Helpers.php} (97%) rename tests/GAubry/{Tools/ToolsTest.php => Helpers/HelpersTest.php} (83%) diff --git a/composer.json b/composer.json index f9795f6..c510859 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "geoffroy-aubry/tools", + "name": "geoffroy-aubry/helpers", "description": "bla bla", "keywords": ["bla"], "type": "library", @@ -7,13 +7,13 @@ "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 583ccf6..9708f90 100644 --- a/phpunit-dist.xml +++ b/phpunit-dist.xml @@ -27,7 +27,7 @@ - + tests @@ -35,7 +35,7 @@ array('c', 'b') + * arrayMergeRecursiveDistinct(array('a', 'b'), array('c')) => array('c', 'b') * Maintenant : * => array('c') * @@ -209,6 +209,7 @@ public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"') * @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; diff --git a/tests/GAubry/Tools/ToolsTest.php b/tests/GAubry/Helpers/HelpersTest.php similarity index 83% rename from tests/GAubry/Tools/ToolsTest.php rename to tests/GAubry/Helpers/HelpersTest.php index 40e44d6..1c9f3a6 100644 --- a/tests/GAubry/Tools/ToolsTest.php +++ b/tests/GAubry/Helpers/HelpersTest.php @@ -1,19 +1,19 @@ */ -class ToolsTest extends \PHPUnit_Framework_TestCase +class HelpersTest extends \PHPUnit_Framework_TestCase { /** - * @covers \GAubry\Tools\Tools::arrayMergeRecursiveDistinct + * @covers \GAubry\Helpers\Helpers::arrayMergeRecursiveDistinct * @dataProvider dataProvider_testArrayMergeRecursiveDistinct * * @param array $aArray1 @@ -22,7 +22,7 @@ class ToolsTest extends \PHPUnit_Framework_TestCase */ public function testArrayMergeRecursiveDistinct (array $aArray1, array $aArray2, array $aExpected) { - $this->assertEquals($aExpected, Tools::arrayMergeRecursiveDistinct($aArray1, $aArray2)); + $this->assertEquals($aExpected, Helpers::arrayMergeRecursiveDistinct($aArray1, $aArray2)); } /** @@ -66,7 +66,7 @@ public function dataProvider_testArrayMergeRecursiveDistinct () } /** - * @covers \GAubry\Tools\Tools::isAssociativeArray + * @covers \GAubry\Helpers\Helpers::isAssociativeArray * @dataProvider dataProvider_testIsAssociativeArray * * @param array $aArray @@ -74,7 +74,7 @@ public function dataProvider_testArrayMergeRecursiveDistinct () */ public function testIsAssociativeArray ($aArray, $bExpected) { - $this->assertEquals($bExpected, Tools::isAssociativeArray($aArray)); + $this->assertEquals($bExpected, Helpers::isAssociativeArray($aArray)); } /** @@ -92,7 +92,7 @@ public function dataProvider_testIsAssociativeArray () } /** - * @covers \GAubry\Tools\Tools::stripBashColors + * @covers \GAubry\Helpers\Helpers::stripBashColors * @dataProvider dataProvider_testStripBashColors * * @param string $sSource @@ -100,7 +100,7 @@ public function dataProvider_testIsAssociativeArray () */ public function testStripBashColors ($sSource, $sExpected) { - $this->assertEquals($sExpected, Tools::stripBashColors($sSource)); + $this->assertEquals($sExpected, Helpers::stripBashColors($sSource)); } /** @@ -122,7 +122,7 @@ public function dataProvider_testStripBashColors () } /** - * @covers \GAubry\Tools\Tools::flattenArray + * @covers \GAubry\Helpers\Helpers::flattenArray * @dataProvider dataProvider_testFlattenArray * * @param array $aSource @@ -130,7 +130,7 @@ public function dataProvider_testStripBashColors () */ public function testFlattenArray (array $aSource, array $aExpected) { - $this->assertEquals($aExpected, Tools::flattenArray($aSource)); + $this->assertEquals($aExpected, Helpers::flattenArray($aSource)); } /** @@ -149,7 +149,7 @@ public function dataProvider_testFlattenArray () } /** - * @covers \GAubry\Tools\Tools::intToMultiple + * @covers \GAubry\Helpers\Helpers::intToMultiple * @dataProvider dataProvider_testIntToMultiple * * @param int $iValue @@ -157,7 +157,7 @@ public function dataProvider_testFlattenArray () */ public function testIntToMultiple ($iValue, $bBinaryPrefix, array $aExpected) { - $this->assertEquals($aExpected, Tools::intToMultiple($iValue, $bBinaryPrefix)); + $this->assertEquals($aExpected, Helpers::intToMultiple($iValue, $bBinaryPrefix)); } /** @@ -181,7 +181,7 @@ public function dataProvider_testIntToMultiple () } /** - * @covers \GAubry\Tools\Tools::round + * @covers \GAubry\Helpers\Helpers::round * @dataProvider dataProvider_testRound * * @param float $fValue @@ -190,7 +190,7 @@ public function dataProvider_testIntToMultiple () */ public function testRound ($fValue, $iPrecision, $sExpected) { - $this->assertEquals($sExpected, Tools::round($fValue, $iPrecision)); + $this->assertEquals($sExpected, Helpers::round($fValue, $iPrecision)); } /** @@ -211,7 +211,7 @@ public function dataProvider_testRound () } /** - * @covers \GAubry\Tools\Tools::ucwordWithDelimiters + * @covers \GAubry\Helpers\Helpers::ucwordWithDelimiters * @dataProvider dataProvider_testUcwordWithDelimiters * * @param string $sString @@ -220,7 +220,7 @@ public function dataProvider_testRound () */ public function testUcwordWithDelimiters ($sString, array $aDelimiters, $sExpected) { - $this->assertEquals($sExpected, Tools::ucwordWithDelimiters($sString, $aDelimiters)); + $this->assertEquals($sExpected, Helpers::ucwordWithDelimiters($sString, $aDelimiters)); } /** 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'; From 6577e5087102bf99199a9a82f83792c915b86168 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Sun, 19 May 2013 23:50:29 +0200 Subject: [PATCH 5/9] Add gihtub doc. --- .gitattributes | 44 ++++ .travis.yml | 13 ++ CHANGELOG.md | 6 + LICENSE | 165 ++++++++++++++ README.md | 260 ++++++++++++++++++++++ composer.json | 4 +- src/GAubry/Helpers/Helpers.php | 44 ++-- tests/GAubry/Helpers/HelpersTest.php | 320 +++++++++++++-------------- 8 files changed, 676 insertions(+), 180 deletions(-) create mode 100644 .gitattributes create mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md 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..a0a18df --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +# See: http://about.travis-ci.org/docs/user/build-configuration/ + +language: php +php: + - 5.3 + +before_script: + - cp phpunit-dist.php phpunit.php +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..ae82110 --- /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 c510859..e4e1d31 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "geoffroy-aubry/helpers", - "description": "bla bla", - "keywords": ["bla"], + "description": "Some helpers used in several personal packages.", + "keywords": ["helpers"], "type": "library", "license": "LGPL-3.0+", "authors": [ diff --git a/src/GAubry/Helpers/Helpers.php b/src/GAubry/Helpers/Helpers.php index 3df819b..1dbfe7c 100644 --- a/src/GAubry/Helpers/Helpers.php +++ b/src/GAubry/Helpers/Helpers.php @@ -3,7 +3,13 @@ namespace GAubry\Helpers; /** - * Outils divers et variés... + * Some helpers used in several personal packages. + * + * Copyright (c) 2013 Geoffroy Aubry + * 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 { @@ -14,8 +20,8 @@ private function __construct() /** * Flatten a multidimensional array (keys are ignored). * - * @param array $array - * @return array tableau à une seule dimension. + * @param array $aArray + * @return array a one dimensional array. * @see http://stackoverflow.com/a/1320156/1813519 */ public static function flattenArray (array $aArray) { @@ -41,6 +47,7 @@ public static function utf8Encode ($s) * 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 */ @@ -73,8 +80,7 @@ public static function stripBashColors ($sMsg) } /** - * Rounds specified value with precision $iPrecision as native round() function, - * but keep trailing zero. + * 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) @@ -107,7 +113,7 @@ public static function ucwordWithDelimiters ($sString, array $aDelimiters=array( } /** - * Returns specified value in the most appropriate unit with that unit. + * 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 @@ -138,7 +144,7 @@ public static function intToMultiple ($iValue, $bBinaryPrefix=false) /** * Format a number with grouped thousands. - * It is an extended version of number_format() that allow do not specify $decimals. + * 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. @@ -146,9 +152,9 @@ public static function intToMultiple ($iValue, $bBinaryPrefix=false) * @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) + public static function numberFormat ($fNumber, $sDecPoint='.', $sThousandsSep=',', $iDecimals=null) { - if ($iDecimals !== NULL) { + if ($iDecimals !== null) { return number_format($fNumber, $iDecimals, $sDecPoint, $sThousandsSep); } else { $tmp = explode('.', $fNumber); @@ -169,7 +175,8 @@ public static function numberFormat ($fNumber, $sDecPoint='.', $sThousandsSep=', * @param string $sEnclosure * @return string specified array converted into CSV format string */ - public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"') { + public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"') + { // Open a memory "file" for read/write... $fp = fopen('php://temp', 'r+'); fputcsv($fp, $aInput, $sDelimiter, $sEnclosure); @@ -188,20 +195,20 @@ public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"') * this happens (documented behavior): * * array_merge_recursive(array('key' => 'org value'), array('key' => 'new value')); - * => array('key' => array('org value', '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')); + * ⇒ array('key' => array('new value')); * - * EVO sur sous-tableaux indexés : - * Avant : - * arrayMergeRecursiveDistinct(array('a', 'b'), array('c')) => array('c', 'b') - * Maintenant : - * => array('c') + * EVO on indexed arrays: + * Before: + * arrayMergeRecursiveDistinct(array('a', 'b'), array('c')) ⇒ array('c', 'b') + * Now: + * ⇒ array('c') * * @param array $aArray1 * @param array $aArray2 @@ -211,7 +218,8 @@ public static function strPutCSV ($aInput, $sDelimiter = ',', $sEnclosure = '"') * @author Geoffroy Aubry * @see http://fr2.php.net/manual/en/function.array-merge-recursive.php#89684 */ - public static function arrayMergeRecursiveDistinct (array $aArray1, array $aArray2) { + public static function arrayMergeRecursiveDistinct (array $aArray1, array $aArray2) + { $aMerged = $aArray1; if (self::isAssociativeArray($aMerged)) { foreach ($aArray2 as $key => &$value) { diff --git a/tests/GAubry/Helpers/HelpersTest.php b/tests/GAubry/Helpers/HelpersTest.php index 1c9f3a6..cfce02e 100644 --- a/tests/GAubry/Helpers/HelpersTest.php +++ b/tests/GAubry/Helpers/HelpersTest.php @@ -1,138 +1,138 @@ - - */ -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)); - } - + + */ +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(), $aArray1, $aArray1), + array(array(1, 2), array(3), array(3)), - array(array(3), array(1, 2), array(1, 2)), + 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('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(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('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') + 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)); - } - + } + + /** + * @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( + { + 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('', ''), + ); + } + + /** + * @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[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)); - } - + ); + } + + /** + * @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() */ @@ -144,22 +144,22 @@ public function dataProvider_testFlattenArray () array(array('a' => 'b'), array('b')), array(array(1, 'a' => 'b'), array(1, 'b')), array(array(array('a' => 'b')), array('b')), - array(array(1, array('a' => array('b', 2), 'c')), array(1, 'b', 2, 'c')), + 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)); - } - + } + + /** + * @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() */ @@ -170,29 +170,29 @@ public function dataProvider_testIntToMultiple () 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(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')), + 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)); - } - + } + + /** + * @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() */ @@ -200,7 +200,7 @@ public function dataProvider_testRound () { return array( array(0, 0, '0'), - array(1, 3, '1.000'), + array(1, 3, '1.000'), array(156.789, 0, '157'), array(156.789, 2, '156.79'), array(156.789, 4, '156.7890'), @@ -208,32 +208,32 @@ public function dataProvider_testRound () 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)); - } - + } + + /** + * @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('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"), ); - } -} + } +} From 16be973a74b2efb7fcd75ae9b044c1c20f3d2020 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Sun, 19 May 2013 23:51:44 +0200 Subject: [PATCH 6/9] [twgit] Init release 'release-1.1.0'. From ee386d71a29775efa216967fc42a344c1e207386 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Mon, 20 May 2013 00:07:25 +0200 Subject: [PATCH 7/9] fix paths of README.md and tests travis.ci --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ae82110..c8e8cd2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Helpers -[![Build Status](https://secure.travis-ci.org/geoffroy-aubry/helpers.png?branch=stable)](http://travis-ci.org/geoffroy-aubry/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. @@ -243,10 +243,10 @@ 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. +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. +See [CHANGELOG](https://github.com/geoffroy-aubry/Helpers/blob/stable/CHANGELOG.md) file for details. ## Running tests To run the test suite, simply: From ab4d453dde285a6b2b43d5fd22aeb64655fe0a42 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Mon, 20 May 2013 00:10:57 +0200 Subject: [PATCH 8/9] fix for travis.ci --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a0a18df..b61e56f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ php: - 5.3 before_script: - - cp phpunit-dist.php phpunit.php + - cp phpunit-dist.xml phpunit.xml script: phpunit --configuration phpunit.xml notifications: From fe55160050b5e044f26a2bf879937a3edaad8968 Mon Sep 17 00:00:00 2001 From: Geoffroy Aubry Date: Mon, 20 May 2013 00:18:26 +0200 Subject: [PATCH 9/9] fix: composer with travis.ci --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index b61e56f..778247a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: before_script: - cp phpunit-dist.xml phpunit.xml + - composer install script: phpunit --configuration phpunit.xml notifications: