[TASK] Improve StringUtility 11/30611/10
authorMarkus Klein <klein.t3@mfc-linz.at>
Fri, 6 Jun 2014 16:00:40 +0000 (18:00 +0200)
committerMarkus Klein <klein.t3@mfc-linz.at>
Fri, 20 Jun 2014 20:45:15 +0000 (22:45 +0200)
This patch adds the methods beginsWith() and endsWith().
Furthermore it deprecates the (Core-wise unused) isLastPartOfString()
method, because we slightly change the implementation of it, which is
now part of endsWith().

Resolves: #59132
Resolves: #45499
Releases: 6.3
Change-Id: Ida21a85a3cb724618a490f7eab753978a1bfaa70
Reviewed-on: https://review.typo3.org/30611
Reviewed-by: Susanne Moog
Tested-by: Susanne Moog
Reviewed-by: Markus Klein
Tested-by: Markus Klein
typo3/sysext/core/Classes/Utility/StringUtility.php
typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php

index 5462b5b..2ab7015 100644 (file)
@@ -30,6 +30,7 @@ namespace TYPO3\CMS\Core\Utility;
  * Class with helper functions for string handling
  *
  * @author Susanne Moog <typo3@susanne-moog.de>
+ * @author Markus Klein <klein.t3@mfc-linz.at>
  */
 class StringUtility {
 
@@ -40,10 +41,13 @@ class StringUtility {
         *
         * @param string $haystack Full string to check
         * @param string $needle Reference string which must be found as the "last part" of the full string
+        * @throws \InvalidArgumentException
         * @return boolean TRUE if $needle was found to be equal to the last part of $str
+        * @deprecated since 6.3 - will be removed two versions later, use beginsWith() instead
         */
        static public function isLastPartOfString($haystack, $needle) {
-                       // Sanitize $haystack and $needle
+               GeneralUtility::logDeprecatedFunction();
+               // Sanitize $haystack and $needle
                if (is_object($haystack) || (string)$haystack != $haystack || strlen($haystack) < 1) {
                        throw new \InvalidArgumentException(
                                '$haystack can not be interpreted as string or has no length',
@@ -61,4 +65,63 @@ class StringUtility {
                return strrpos((string) $haystack, (string) $needle, 0) === $stringLength - $needleLength;
        }
 
+       /**
+        * Returns TRUE if the first part of $str matches the string $partStr
+        *
+        * @param string $haystack Full string to check
+        * @param string $needle Reference string which must be found as the "first part" of the full string
+        * @throws \InvalidArgumentException
+        * @return boolean TRUE if $needle was found to be equal to the first part of $haystack
+        */
+       static public function beginsWith($haystack, $needle) {
+               // Sanitize $haystack and $needle
+               if (is_object($haystack) || $haystack === NULL || (string)$haystack != $haystack) {
+                       throw new \InvalidArgumentException(
+                               '$haystack can not be interpreted as string',
+                               1347135546
+                       );
+               }
+               if (is_object($needle) || (string)$needle != $needle || strlen($needle) < 1) {
+                       throw new \InvalidArgumentException(
+                               '$needle can not be interpreted as string or has zero length',
+                               1347135547
+                       );
+               }
+               $haystack = (string)$haystack;
+               $needle = (string)$needle;
+               return $needle !== '' && strpos($haystack, $needle) === 0;
+       }
+
+       /**
+        * Returns TRUE if $haystack ends with $needle.
+        * The input string is not trimmed before and search is done case sensitive.
+        *
+        * @param string $haystack Full string to check
+        * @param string $needle Reference string which must be found as the "last part" of the full string
+        * @throws \InvalidArgumentException
+        * @return boolean TRUE if $needle was found to be equal to the last part of $haystack
+        */
+       static public function endsWith($haystack, $needle) {
+               // Sanitize $haystack and $needle
+               if (is_object($haystack) || $haystack === NULL || (string)$haystack != $haystack) {
+                       throw new \InvalidArgumentException(
+                               '$haystack can not be interpreted as string',
+                               1347135544
+                       );
+               }
+               if (is_object($needle) || (string)$needle != $needle || strlen($needle) < 1) {
+                       throw new \InvalidArgumentException(
+                               '$needle can not be interpreted as string or has no length',
+                               1347135545
+                       );
+               }
+               $haystackLength = strlen($haystack);
+               $needleLength = strlen($needle);
+               if (!$haystackLength || $needleLength > $haystackLength) {
+                       return FALSE;
+               }
+               $position = strrpos((string)$haystack, (string)$needle);
+               return $position !== FALSE && $position === $haystackLength - $needleLength;
+       }
+
 }
index 0764d19..5f33917 100644 (file)
@@ -23,10 +23,13 @@ namespace TYPO3\CMS\Core\Tests\Unit\Utility;
  *
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
+use TYPO3\CMS\Core\Utility\StringUtility;
+
 /**
  * Testcase for class \TYPO3\CMS\Core\Utility\StringUtility
  *
  * @author Susanne Moog <typo3@susanne-moog.de>
+ * @author Markus Klein <klein.t3@mfc-linz.at>
  */
 class StringUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
@@ -107,4 +110,165 @@ class StringUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        public function isLastPartOfStringReturnsThrowsExceptionWithInvalidArguments($string, $part) {
                $this->assertFalse(\TYPO3\CMS\Core\Utility\StringUtility::isLastPartOfString($string, $part));
        }
+
+       /**
+        * Data provider for endsWithReturnsTrueForMatchingFirstPart
+        *
+        * @return array
+        */
+       public function endsWithReturnsTrueForMatchingLastPartDataProvider() {
+               return array(
+                       'match last part of string' => array('hello world', 'world'),
+                       'match last char of string' => array('hellod world', 'd'),
+                       'match whole string' => array('hello', 'hello'),
+                       'integer is part of string with same number' => array('24', 24),
+                       'string is part of integer with same number' => array(24, '24'),
+                       'integer is part of string ending with same number' => array('please gimme beer, 24', 24)
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider endsWithReturnsTrueForMatchingLastPartDataProvider
+        */
+       public function endsWithReturnsTrueForMatchingLastPart($string, $part) {
+               $this->assertTrue(StringUtility::endsWith($string, $part));
+       }
+
+       /**
+        * Data provider for check endsWithReturnsFalseForNotMatchingLastPart
+        *
+        * @return array
+        */
+       public function endsWithReturnsFalseForNotMatchingLastPartDataProvider() {
+               return array(
+                       'no string match' => array('hello', 'bye'),
+                       'no case sensitive string match' => array('hello world', 'World'),
+                       'string is part but not last part' => array('hello world', 'worl'),
+                       'integer is not part of empty string' => array('', 0),
+                       'longer string is not part of shorter string' => array('a', 'aa'),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider endsWithReturnsFalseForNotMatchingLastPartDataProvider
+        */
+       public function endsWithReturnsFalseForNotMatchingLastPart($string, $part) {
+               $this->assertFalse(StringUtility::endsWith($string, $part));
+       }
+
+       /**
+        * Data provider for endsWithReturnsThrowsExceptionWithInvalidArguments
+        *
+        * @return array
+        */
+       public function endsWithReturnsThrowsExceptionWithInvalidArgumentsDataProvider() {
+               return array(
+                       'array is not part of string' => array('string', array()),
+                       'NULL is not part of string' => array('string', NULL),
+                       'empty string is not part of string' => array('string', ''),
+                       'string is not part of array' => array(array(), 'string'),
+                       'NULL is not part of array' => array(array(), NULL),
+                       'string is not part of NULL' => array(NULL, 'string'),
+                       'array is not part of NULL' => array(NULL, array()),
+                       'integer is not part of NULL' => array(NULL, 0),
+                       'empty string is not part of NULL' => array(NULL, ''),
+                       'NULL is not part of empty string' => array('', NULL),
+                       'FALSE is not part of empty string' => array('', FALSE),
+                       'empty string is not part of FALSE' => array(FALSE, ''),
+                       'empty string is not part of integer' => array(0, ''),
+                       'string is not part of object' => array(new \stdClass(), 'foo'),
+                       'object is not part of string' => array('foo', new \stdClass()),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider endsWithReturnsThrowsExceptionWithInvalidArgumentsDataProvider
+        * @expectedException \InvalidArgumentException
+        */
+       public function endsWithReturnsThrowsExceptionWithInvalidArguments($string, $part) {
+               StringUtility::endsWith($string, $part);
+       }
+
+       /**
+        * Data provider for beginsWithReturnsTrueForMatchingFirstPart
+        *
+        * @return array
+        */
+       public function beginsWithReturnsTrueForMatchingFirstPartDataProvider() {
+               return array(
+                       'match first part of string' => array('hello world', 'hello'),
+                       'match first char of string' => array('hello world', 'h'),
+                       'match whole string' => array('hello', 'hello'),
+                       'integer is part of string with same number' => array('24', 24),
+                       'string is part of integer with same number' => array(24, '24'),
+                       'integer is part of string starting with same number' => array('24, please gimme beer', 24),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider beginsWithReturnsTrueForMatchingFirstPartDataProvider
+        */
+       public function beginsWithReturnsTrueForMatchingFirstPart($string, $part) {
+               $this->assertTrue(StringUtility::beginsWith($string, $part));
+       }
+
+       /**
+        * Data provider for check beginsWithReturnsFalseForNotMatchingFirstPart
+        *
+        * @return array
+        */
+       public function beginsWithReturnsFalseForNotMatchingFirstPartDataProvider() {
+               return array(
+                       'no string match' => array('hello', 'bye'),
+                       'no case sensitive string match' => array('hello world', 'Hello'),
+                   'string in empty string' => array('', 'foo')
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider beginsWithReturnsFalseForNotMatchingFirstPartDataProvider
+        */
+       public function beginsWithReturnsFalseForNotMatchingFirstPart($string, $part) {
+               $this->assertFalse(StringUtility::beginsWith($string, $part));
+       }
+
+       /**
+        * Data provider for beginsWithReturnsThrowsExceptionWithInvalidArguments
+        *
+        * @return array
+        */
+       public function beginsWithReturnsInvalidArgumentDataProvider() {
+               return array(
+                       'array is not part of string' => array('string', array()),
+                       'NULL is not part of string' => array('string', NULL),
+                       'empty string is not part of string' => array('string', ''),
+                       'string is not part of array' => array(array(), 'string'),
+                       'NULL is not part of array' => array(array(), NULL),
+                       'string is not part of NULL' => array(NULL, 'string'),
+                       'array is not part of NULL' => array(NULL, array()),
+                       'integer is not part of NULL' => array(NULL, 0),
+                       'empty string is not part of NULL' => array(NULL, ''),
+                       'NULL is not part of empty string' => array('', NULL),
+                       'FALSE is not part of empty string' => array('', FALSE),
+                       'empty string is not part of FALSE' => array(FALSE, ''),
+                       'empty string is not part of integer' => array(0, ''),
+                       'string is not part of object' => array(new \stdClass(), 'foo'),
+                       'object is not part of string' => array('foo', new \stdClass()),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider beginsWithReturnsInvalidArgumentDataProvider
+        * @expectedException \InvalidArgumentException
+        */
+       public function beginsWithReturnsThrowsExceptionWithInvalidArguments($string, $part) {
+               StringUtility::beginsWith($string, $part);
+       }
+
 }