Skip to content

Commit

Permalink
Strings: added after(), before() and pos() (thanks @icaine) [Closes #20]
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Jun 15, 2015
1 parent e7f2fce commit 8c57c61
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 0 deletions.
65 changes: 65 additions & 0 deletions src/Utils/Strings.php
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,71 @@ public static function random($length = 10, $charlist = '0-9a-z')
}


/**
* Returns part of $haystack before $nth occurence of $needle.
* @param string
* @param string
* @param int negative value means searching from the end
* @return string|FALSE returns FALSE if the needle was not found
*/
public static function before($haystack, $needle, $nth = 1)
{
$pos = self::pos($haystack, $needle, $nth);
return $pos === FALSE
? FALSE
: substr($haystack, 0, $pos);
}


/**
* Returns part of $haystack after $nth occurence of $needle.
* @param string
* @param string
* @param int negative value means searching from the end
* @return string|FALSE returns FALSE if the needle was not found
*/
public static function after($haystack, $needle, $nth = 1)
{
$pos = self::pos($haystack, $needle, $nth);
return $pos === FALSE
? FALSE
: (string) substr($haystack, $pos + strlen($needle));
}


/**
* Returns position of $nth occurence of $needle in $haystack.
* @param string
* @param string
* @param int negative value means searching from the end

This comment has been minimized.

Copy link
@JanTvrdik

JanTvrdik Jun 15, 2015

Contributor

Incorrect @return annotation.

* @return string|FALSE returns FALSE if the needle was not found
*/

This comment has been minimized.

Copy link
@Majkl578

Majkl578 Jun 15, 2015

Contributor

Please no abbreviations. :(

This comment has been minimized.

Copy link
@JanTvrdik

JanTvrdik Jun 15, 2015

Contributor

Good point. People are used to strpos but still. What about find, findNth or indexOf?

This comment has been minimized.

Copy link
@JanTvrdik

JanTvrdik Jun 15, 2015

Contributor

Another interesting problem which I have no idea how to solve. This method returns byte-offset however substring accepts codepoint-offset. How do prevent people from passing pos value to substring? I actually think that we should consider making pos private because of that.


Or – we may also consider introducing new class Bytes.


I'm starting to like the idea of new Bytes class. We may move all functions from Strings which work on byte-strings (such as startsWith) there and reduce Strings implementation (without deprecation!) to

public static function startsWith($haystack, $needle)
{
    return Bytes::startsWith($haystack, $needle);
}

Does that make sense to you?

This comment has been minimized.

Copy link
@fprochazka

fprochazka Jun 15, 2015

Contributor

👎 abbreviations :(

This comment has been minimized.

Copy link
@JanTvrdik

JanTvrdik Jun 15, 2015

Contributor

@fprochazka And what name would you choose?

This comment has been minimized.

Copy link
@enumag

enumag Jun 15, 2015

Contributor

@JanTvrdik 👍 for separate class

This comment has been minimized.

Copy link
@fprochazka

fprochazka Jun 16, 2015

Contributor

Something like positionOfNth makes the most sense for me at this point. I'm not sure about better name.

public static function pos($haystack, $needle, $nth = 1)
{
if (!$nth) {
return FALSE;
} elseif ($nth > 0) {
if (strlen($needle) === 0) {
return 0;
}
$pos = 0;
while (FALSE !== ($pos = strpos($haystack, $needle, $pos)) && --$nth) {
$pos++;
}
} else {
$len = strlen($haystack);
if (strlen($needle) === 0) {
return $len;
}
$pos = $len - 1;
while (FALSE !== ($pos = strrpos($haystack, $needle, $pos - $len)) && ++$nth) {
$pos--;
}
}
return $pos;
}


/**
* Splits string by a regular expression.
* @param string
Expand Down
33 changes: 33 additions & 0 deletions tests/Utils/Strings.after().phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* Test: Nette\Utils\Strings::after()
*/

use Nette\Utils\Strings,
Tester\Assert;


require __DIR__ . '/../bootstrap.php';



test(function () { //after
$foo = '0123456789a123456789b123456789c';
Assert::same('123456789a123456789b123456789c', Strings::after($foo, '0', 1));
Assert::same('a123456789b123456789c', Strings::after($foo, '9', 1));
Assert::same('a123456789b123456789c', Strings::after($foo, '789', 1));
Assert::same('0123456789a123456789b123456789c', Strings::after($foo, '', 1));
Assert::same('', Strings::after($foo, '', -1));
Assert::same('', Strings::after($foo, 'c', -1));
Assert::same('c', Strings::after($foo, '9', -1));
Assert::same('c', Strings::after($foo, '789', -1));
Assert::same('c', Strings::after($foo, '9', 3));
Assert::same('c', Strings::after($foo, '789', 3));
Assert::same('a123456789b123456789c', Strings::after($foo, '9', -3));
Assert::same('a123456789b123456789c', Strings::after($foo, '789', -3));
Assert::false(Strings::after($foo, '9', 0));
Assert::false(Strings::after($foo, 'not-in-string'));
Assert::false(Strings::after($foo, 'b', -2));
Assert::false(Strings::after($foo, 'b', 2));
});
32 changes: 32 additions & 0 deletions tests/Utils/Strings.before().phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/**
* Test: Nette\Utils\Strings::before()
*/

use Nette\Utils\Strings,
Tester\Assert;


require __DIR__ . '/../bootstrap.php';



test(function () { //before
$foo = '0123456789a123456789b123456789c';
Assert::same('', Strings::before($foo, '0', 1));
Assert::same('012345678', Strings::before($foo, '9', 1));
Assert::same('0123456', Strings::before($foo, '789', 1));
Assert::same('', Strings::before($foo, '', 1));
Assert::same('0123456789a123456789b123456789c', Strings::before($foo, '', -1));
Assert::same('0123456789a123456789b123456789', Strings::before($foo, 'c', -1));
Assert::same('0123456789a123456789b123456', Strings::before($foo, '789', -1));
Assert::same('0123456789a123456789b12345678', Strings::before($foo, '9', 3));
Assert::same('0123456789a123456789b123456', Strings::before($foo, '789', 3));
Assert::same('012345678', Strings::before($foo, '9', -3));
Assert::same('0123456', Strings::before($foo, '789', -3));
Assert::false(Strings::before($foo, '9', 0));
Assert::false(Strings::before($foo, 'not-in-string'));
Assert::false(Strings::before($foo, 'b', -2));
Assert::false(Strings::before($foo, 'b', 2));
});
33 changes: 33 additions & 0 deletions tests/Utils/Strings.pos().phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* Test: Nette\Utils\Strings::pos()
*/

use Nette\Utils\Strings,
Tester\Assert;


require __DIR__ . '/../bootstrap.php';



test(function () { //after

This comment has been minimized.

Copy link
@JanTvrdik

JanTvrdik Jun 15, 2015

Contributor

Wrong comment. Also seems pretty useless.

$foo = '0123456789a123456789b123456789c';
Assert::same(0, Strings::pos($foo, '0', 1));
Assert::same(9, Strings::pos($foo, '9', 1));
Assert::same(7, Strings::pos($foo, '789', 1));
Assert::same(0, Strings::pos($foo, '', 1));
Assert::same(31, Strings::pos($foo, '', -1));
Assert::same(30, Strings::pos($foo, 'c', -1));
Assert::same(29, Strings::pos($foo, '9', -1));
Assert::same(27, Strings::pos($foo, '789', -1));
Assert::same(29, Strings::pos($foo, '9', 3));
Assert::same(27, Strings::pos($foo, '789', 3));
Assert::same(9, Strings::pos($foo, '9', -3));
Assert::same(7, Strings::pos($foo, '789', -3));
Assert::false(Strings::pos($foo, '9', 0));
Assert::false(Strings::pos($foo, 'not-in-string'));
Assert::false(Strings::pos($foo, 'b', -2));
Assert::false(Strings::pos($foo, 'b', 2));
});

1 comment on commit 8c57c61

@enumag
Copy link
Contributor

@enumag enumag commented on 8c57c61 Jun 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 nice, these will be useful :-)

Please sign in to comment.