strings icon indicating copy to clipboard operation
strings copied to clipboard

[enh] Proposal to add class StringObject

Open rugabarbo opened this issue 5 years ago • 14 comments

I propose to add class StringObject. Then it will be possible to manipulate strings in object-oriented style:

$baseWord = new StringObject('soft', 'UTF-8');

$projectName = $baseWord
   ->preprend('yii')
   ->addWord('project')
   ->upperCaseEveryWord();

$projectNameWordsCount = $projectName->wordsCount();

Then we can make class StringHelper a wrapper over class StringObject. For example:

class StringHelper
{
    public static function createObject(string $string, string $encoding = null): StringObject
    {
        return new StringObject($string, $encoding);
    }

    public static function wordsCount(string $string, string $encoding = null): int
    {
        return static::createObject($string, $encoding)->wordsCount();
    }
}

rugabarbo avatar Jun 15 '19 12:06 rugabarbo

You can use it https://github.com/fe3dback/str

loveorigami avatar Jun 15 '19 12:06 loveorigami

@loveorigami I know about this lib. Thanks. But I am talking about the future of this library.

There are many other good string processing libraries. But they have one problem - the authors stop supporting them. For example, there is an excellent library Stringy – but project is dead because author stopped supporting it. There's no code commits since 2017.

If Yii team isn't going to support and develop this library, then it should be replaced now with some kind of external solution. If the team is going, then I suggest an object-oriented design.

rugabarbo avatar Jun 15 '19 13:06 rugabarbo

BTW library https://github.com/fe3dback/str is also rarely updated (only 3 commits for 2019) and still has no documentation and has no implementation of a static wrapper. I would not choose it for a long-term project.

rugabarbo avatar Jun 15 '19 13:06 rugabarbo

What's the benefit in wrapping strings into objects?

samdark avatar Jun 15 '19 21:06 samdark

What's the benefit in wrapping strings into objects?

  1. An object has a state. This allows you to set the encoding only once.
  2. Fluent interface is more convenient when you need a series of calls.

Just compare:

a) Using object:

$sentence = StringHelper::createObject($input, 'UTF-8')
    ->trim()
    ->upperCaseFirst()
    ->ensureLastSymbol('.');

b) Using static helper:

$encoding = 'UTF-8';

$buffer = StringHelper::trim($input, $encoding);
$buffer = StringHelper::upperCaseFirst($buffer, $encoding);
$buffer = StringHelper::ensureLastSymbol($buffer, '.', $encoding);

$sentence = $buffer;

On the other hand static methods are useful when a callable parameter is required:

public function rules()
{
    return [
        [['title', 'short_description'], 'filter', 'filter' => StringHelper::class . '::collapseWhitespaces'],
        [['title', 'short_description'], 'trim'],
        [['title', 'short_description'], 'required'],
        [['title', 'short_description'], 'string', 'max' => 128],
    ];
}

Here it would be very inconvenient to use a string object.


Therefore, it is convenient to have both an object interface for strings and a static wrapper in the same library.

rugabarbo avatar Jun 15 '19 23:06 rugabarbo

@yiisoft/core-developers, @yiisoft/reviewers opinions?

samdark avatar Jun 16 '19 13:06 samdark

Any benchmarks to measure overhead?

rob006 avatar Jun 16 '19 19:06 rob006

Any benchmarks to measure overhead?

@rob006 what kind of overhead do you mean? In such libraries, the main performance losses are the result of poor implementation, but not the result of using objects.

Implementation is a separate problem. This can be bad in both object-oriented style and static methods.

rugabarbo avatar Jun 17 '19 10:06 rugabarbo

@rob006 what kind of overhead do you mean? In such libraries, the main performance losses are the result of poor implementation, but not the result of using objects.

I believe that creating objects and additional function calls will result noticeable overhead compared to simple operations on scalars. Especially if you want to use it internally in this way:

    public static function wordsCount(string $string, string $encoding = null): int
    {
        return static::createObject($string, $encoding)->wordsCount();
    }

rob006 avatar Jun 17 '19 10:06 rob006

Do you mean memory overhead or performance overhead?

rugabarbo avatar Jun 17 '19 11:06 rugabarbo

Performance (CPU).

rob006 avatar Jun 17 '19 11:06 rob006

In order not to create a large number of objects during a series of static calls, we can implement it a little differently:

class StringHelper
{
    public static function wordsCount(string $string, string $encoding = null): int
    {
        // Simple operations on scalars here

        return $count;
    }
}

class StringObject
{
    protected $string;
    protected $encoding;

    public function __construct(string $string, string $encoding = null)
    {
        $this->string = $string;
        $this->encoding = $encoding;
    }

    public static function create(string $string, string $encoding = null): StringObject
    {
        return new static($string, $encoding);
    }

    public function wordsCount(): int
    {
        return StringHelper::wordsCount($this->string, $this->encoding);
    }
}

If you are worried about the performance of static calls, then this design will solve the problem. So, performance depends on the concrete implementation.

rugabarbo avatar Jun 17 '19 11:06 rugabarbo

Hi, I merged performance improvements from the str lib into a maintained fork of Stringy + fixes from the community. Maybe you can find there some inspirations?! ☺ https://github.com/voku/Stringy

voku avatar Jun 26 '19 06:06 voku

Can be implemented later as a wrapper for StringHelper.

samdark avatar Aug 26 '20 16:08 samdark