Standard Data Types

SPL data types wrappers and utilities to dealing with array, strings, traversable, ...

Introduction

Standard Data Types Libraries is wrapper around existence PHP data types which adding more convenient methods and useful helpers to them. these libraries are used as part of core functionalities of Poirot Framework. The component are distributed under the "Poirot/Std" namespace and has a git repository, and, for the php components, a light autoloader and composer support. All Poirot components following the PSR-4 convention for namespace related to directory structure and so, can be loaded using any modern framework autoloader. 100% Test Coverage and fully testable by PHPUnit Test.

In Case You need any support or question use the links below:

Slack: getpoirot.slack.com https://join.slack.com/t/getpoirot/shared_invite/zt-dvfhkoes-h45XoKlyxamfhaHm6G4uSA

Arrays

Additionally to the rich set of PHP array functions, there is a collection of convenience helper methods array helper provides extra methods allowing you to deal with arrays more efficiently.

Method name convention: Any method from StdArray which prefixed with get like getKeys, getLast, ... has no effect on the current object and will return new instance of object include the expected result.

Instantiate and Construct

StdArray can be instantiated around existing array or from an initial empty array:

$arrOne   = new StdArray();
$arrTwo   = new StdArray([1, 2, 3, 4]);
$arrThree = new StdArray(new \ArrayIterator([1, 2, 3, 4]));

// Through factory static method:
$arr = StdArray::of([1, 2, 3, 4]);

StdArray can built around different data sources such as Traversable, Object, json string, reference memory address to array variable.

All accepted different data types can be constructed through construct method and none-strict mode by passing second argument to constructor.

$initialValue = (object) ['field' => 'value', 'otherField' => 'other-value']

// can be constructed on none-strict mode
$stdArr = new StdArray($initialValue, false);

Converting To Array

Traversable:

$initialValue = function() {
  yield field => 'value';
  yield otherField => 'other-value';
}();

$stdArr = StdArray::ofTraversable($initialValue);

stdClass object:

$initialValue = new \stdClass();
$initialValue->field = 'value';
$initialValue->otherField = 'other-value';

$stdArr = StdArray::ofObject($initialValue);

class object:

$initialValue = new class {
  public $field = 'value';
  public $otherField = 'other-value';
};

$stdArr = StdArray::ofObject($initialValue);

json string:

$initialValue = '{
  "field": "value",
  "otherField": "other-value"
}';

$stdArr = StdArray::ofJson($initialValue);

memory address reference:

$initialValue = [1, 2, 3, 4];
$stdArray = StdArray::ofReference($initialValue);
$stdArray->append(5);

print_r($initialValue); // [1, 2, 3, 4, 5]

It's Iterable

StdArray is iterable object and items are accessible through \Iteratorinterface.

$stdArray = new StdArray(['a', 'b', 'c', 'd']);
while ($stdArray->valid()) {
    echo $stdArray->key() . '=' . $stdArray->current() . PHP_EOL;
    $stdArray->next();
}

It's Array Accessible

The object items are accessible just like a regular array, the only difference is when key is undefined instead of trigger E_NOTICE error of Array key does not exist, it will just return null as a value.

$stdArray = new StdArray(['a', 'b', 'c', 'd']);

// when key dosen't exists
var_dump( $stdArray[100] );        # null
var_dump( isset($stdArray[100]) ); # false

echo $stdArray[1];                 # b

$stdArray['field'] = 'value';
echo $stdArray['field'];           # value

It's Countable

$initialValue = [1, 2, 3, 4];
$stdArray = new StdArray($initialValue);
count($stdArray); // 4

Array keys accessible by reference

$stdArray = new StdArray([1, 2, 3, 4]);
$stdArray['new-item'] = 'value';

$t = &$stdArray['new-item'];
$t = 'new-value';
echo $stdArray['new-item']; // new-value

// Internal array:
$stdArray = new StdArray([1, 2, 3, 4]);

$internalArray = &$stdArray->asInternalArray;
array_pop($internalArray);

// [1, 2, 3]
var_dump(
  $stdArray->asInternalArray
); 

lookup - Search array elements by query path

The lookup method search array to matching against given query path expression and will return \Generator traversable of matching elements or exact value of an element based on the given query path expressiontype. Any expression which does not return nodes will return null. These path expressions look very much like the path expressions you use with traditional computer file systems:

Fetch one by nested hierarchy lookup:

Selects from the root by hierarchy descendant.

$arr = [
    'books' => [
        [
            'title' => 'Professional PHP Design Patterns',
            'author' => [
                'firstname' => 'Aaron',
                'lastname' => 'Saray',
            ],
        ],
    ],
];

$result = StdArray::of($arr)->lookup('/books/0/title');
// Professional PHP Design Patterns

$result = StdArray::of($arr)->lookup('/books/0/not_exist_field');
// null

First slash given to expression can be removed, so this query will do the same:

$result = StdArray::of($arr)->lookup('books/0/title');

Fetch all by nested hierarchy lookup:

By adding * beside the element in query, simply expected path to match with more than one element or make the result to \Generator iterable object.

$result = StdArray::of($arr)->lookup('/books/0/*title');
foreach ($result as $k => $v) {
   print $k .': ' . $v;
}

// title: Professional PHP Design Patterns

Regex lookup:

Regex matching expression can be defined between ~ characters:

$arr = [
    'books' => [
        [
            'title' => 'Professional PHP Design Patterns',
            'author' => [
                'firstname' => 'Aaron',
                'lastname' => 'Saray',
            ],
        ],
    ],
];

## First element key which match with given regex
#
$result = StdArray::of($arr)->lookup('/books/0/author/~.+name$~');
// Aaron

## All elements which matches with given regex
#
$result = StdArray::of($arr)->lookup('/books/0/author/*~.+name$~');
// or
$result = StdArray::of($arr)->lookup('/books/0/author/*~firstname|lastname~');
foreach ($result as $k => $v) {
   print $k .': ' . $v . PHP_EOL;
}
// firstname: Aaron
// lastname: Saray

Match lookup in specified depth only:

The depth are counting from 1 for the next level of hierarchy elements inside root. check this out:

$arr = [
    'Python' => [
        // Depth 1
        'name' => 'Python',
        "designed_by" => "Guido van Rossum",
        "details" => [
            // Depth 2
            'name' => 'python language',
            'typing_discipline' => [
                'tags' => ['Duck', 'dynamic', 'gradual'],
            ],
        ],
    ],
    'PHP' => [
        // Depth 1
        'name' => 'PHP',
        "designed_by" => "Rasmus Lerdorf",
        "details" => [
            // Depth 2
            'name' => 'php language',
            "typing_discipline" => [
                'tags' => ['Dynamic', 'weak'],
            ],
        ],
    ],
];

## First element in depth level ONE matching with given expression
#
$result = StdArray::of($arr)->lookup('/>/name');
// Python

## All elements in depth level TWO matching with given expression
#
$result = StdArray::of($arr)->lookup('/>2/*name');
foreach ($result as $k => $v) {
   print $k .': ' . $v . PHP_EOL;
}
// name: python language
// name: php language

Recursive lookup - Match lookup expression within any depth:

The result of // expression is always an \Generator traversable.

$arr = [
    'Python' => [
        'name' => 'Python',
        'details' => [
            'name' => 'python language',
            'typing_discipline' => [
                'tags' => ['Duck', 'dynamic', 'gradual'],
            ],
        ],
    ],
    'PHP' => [
        'name' => 'PHP',
        'details' => [
            'name' => 'php language',
            'typing_discipline' => [
                'tags' => ['Dynamic', 'weak'],
            ],
        ],
    ],
];

$result = StdArray::of($arr)->lookup('//name');
foreach ($result as $k => $v) {
    print $k .': ' . $v . PHP_EOL;
}

// name: Python
// name: python language
// name: PHP
// name: php language

Considerations about lookup resulting in same keys:

As the result of query lookup is a Generator some times we have duplicated key in the result so using some functions like iterator_to_array will not give us the expected result. check this sample: as you see here we have two similar key in the iterator each came from the nested element on main array. when use query take this into consideration.

## Fetch first tag in every item
#
$result = StdArray::of($arr)->lookup('//tags/>/*0')
foreach ($result as $k => $v) {
  print $k .': ' . $v . PHP_EOL;
}
// 0: dynamic 0: dynamic

Reindex lookup result:

By re-index expression token the result will get re-indexed on numeric values started from zero, it could be a solution the the same key on the traversable result which also mentioned. here are the examples: to re-index : should be comes after /

$result = StdArray::of($arr)->lookup('//tags/>/*0/:');
iterator_to_array($result);
// ['dynamic', 'dynamic']

Samples on Combination of variant query expressions:

$arr = [
    'Python' => [
        'name' => 'Python',
        'details' => [
            'name' => 'python language',
            'typing_discipline' => [
                'tags' => ['dynamic', 'duck', 'gradual'],
            ],
        ],
    ],
    'PHP' => [
        'name' => 'PHP',
        'details' => [
            'name' => 'php language',
            'typing_discipline' => [
                'tags' => ['dynamic', 'weak'],
            ],
        ],
    ],
];

## Fetch any tags inside PHP element
#
$result = StdArray::of($arr)->lookup('~PHP~//tags');
foreach ($result as $k => $v) {
    print $k .': ' . implode(', ', $v) . PHP_EOL;
}
// tags: Dynamic, weak


## Fetch All available tags
#
$result = StdArray::of($arr)->lookup('//tags/:');
foreach ($result as $v) {
    print implode(', ', $v) . PHP_EOL;
}
// dynamic, duck, gradual
// dynamic, weak


## Fetch Re-Indexed result of First(0) and Third(2) tag from each element
#
$result = StdArray::of($arr)->lookup('//tags/>/*~0|2~/:');
iterator_to_array($result);
// ['dynamic', 'gradual', 'dynamic']

$result = StdArray::of($arr)->lookup('//tags/:/>/*~0|2~/:')
// ['dynamic', 'gradual', 'dynamic']

lookup - Access by reference

The lookup method can access elements by their memory reference address.

$stdArray = new StdArray([
    [
        'user' => 'user@domain.com',
        'name' => 'name a',
        'data' => ['id' => '1234'],
    ],
    [
        'user' => 'user2@domain.com',
        'name' => 'name b',
        'data' => ['id' => '5678'],
    ],
]);

$firstUserByReference = &$stdArray->lookup('/0/user');
$firstUserByReference = 'changed_user@domain.com';

$names = &$stdArray->lookup('/>/*name');
foreach ($names as &$name)
    $name = strtoupper($name);

$ids = &$stdArray->lookup('//id/:');
foreach ($ids as $i => &$id)
    $id = (int) $id;

/*
[
  [
    'user' => 'changed_user@domain.com',
    'name' => 'NAME A',
    'data' => ['id' => 1234],
  ],
  [
    'user' => 'user2@domain.com',
    'name' => 'NAME B',
    'data' => ['id' => 5678],
  ],
];
*/

filter - Filter Array Items

This method will affect current elements inside array object, check out the following code. We'll use the filter helper to capitalise each string element with strtoupper and remove all none string and empty elements.

$initialValue = ['a', 'b', 'c', '', 0];
$stdArray     = new StdArray($initialValue);

$stdArray->filter(function($value) {
    if (!is_string($value) || $value === '')
        /** @var IteratorWrapper $this */
        $this->skipCurrentIteration();

    if (is_string($value))
        $value = strtoupper($value);

    return $value;
});

// ['A', 'B', 'C']
print_r(
  $stdArray->asInternalArray
);

Array keys can change as well with filter method:

$initialValue = ['a', 'b', 'c'];
$stdArray     = new StdArray($initialValue);

$stdArray->filter(function($value, &$key) {
    // 65 is ascii code for 'A' char
    $key = chr(65 + $key);
    
    // actual value always should be returned by filter
    return $value;
});

// ['A' => 'a', 'B' => 'b', 'C' => 'c']
print_r(
   $stdArray->asInternalArray
);

Further reading

As filter method use IteratorWrapper internally to provide the functionalities for filter elements to know better about all available features see IteratorWrapper documentation for more.

append - Append to end of array

The append method add a given value to the end of array, if given array key as a second argument exists will replaced by new value and will moved to the end of array elements.

$stdArray = StdArray::of([1986 => 'a', 1983 => 'b']);

$stdArray->append('c');
// [1986 => a, 1983 => b, 1987 => c]

$stdArray->append(1, 'one');
// [1986 => a, 1983 => b, 1987 => c, 'one' => 1]

$stdArray->append('d');
// [1986 => a, 1983 => b, 1987 => c, 'one' => 1, 1988 => 'd']

$stdArray->append('e', 1986);
// [1983 => b, 1987 => c, 'one' => 1, 1988 => 'd', 1986 => 'e']

prepend - Prepend to beginning of array

The prepend method add and element to the beginning of array and re-index array keys. if given array key as a second argument exists will replaced by new value and will moved to the beginning of array elements.

$stdArray = StdArray::of([1986 => 'b', 1983 => 'c']);

$stdArray->prepend('a');
// [0 => 'a', 1 => 'b', 2 => 'c']

$stdArray->prepend(1, 'one');
// ['one' => 1, 0 => 'a', 1 => 'b', 2 => 'c']

$stdArray->prepend('x');
// [0 => 'x', 'one' => 1, 1 => 'a', 2 => 'b', 3 => 'c']

$stdArray->prepend(1, 'one');
// ['one' => 1, 0 => 'x', 1 => 'a', 2 => 'b', 3 => 'c']

insert - Insert an element to array

The insert method will append an element to the array if not any specific key given as second argument or set value of the element if key exists. When key is not given the value will appended to array:

$stdArray = StdArray::of([])->insert('a');
// [0 => 'a']

$stdArray = $stdArray->insert('b');
// [0 => 'a', 1 => 'b']

When we specify key the default behaviour is to replace value with new one:

$stdArray = StdArray::of([0 => 'a', 1 => 'zz']);
$stdArray->insert(1, 'b');
// [0 => 'a', 1 => 'b']

By comparison callable as third arguments it's possible to define the insert behaviour when given key is exists in array:

$comprator = function ($givenValue, $currValue, $key) {
    if ($givenValue === $currValue)
        return StdArray::InsertDoSkip;
    elseif ($key == 'words')
        return StdArray::InsertDoPush;

    // otherwise when no insert instruction returned 
    // the default behaviour is to replace value
    // which is considered as StdArray::InsertDoReplace
};

$stdArray = StdArray::of(['words' => 'a', 'time' => 1585909377]);
$stdArray
  ->insert('b', 'words', $comprator)
  ->insert(time(), 'time', $comprator);
  
 // ['words' => ['a', 'b'], 'time' => 1585909389]

merge - merges the given array recursively

The merge method gives flexibility on how to merges the given array into the current array by introducing merge strategies. By default when element has an integer key it will be appended to array.

$stdArray = StdArray::of([ 0 => 'foo'])
   ->merge([ 0 => 'bar']);
   
// [0 => foo, 1 => bar]

The way that merge behaves can be changed with providing merge strategy as a second argument: Merge Strategy is a callable or integer value of compare result which instruct the merge how to behave when same element with key exists, the returned number is -1, 0, 1 which actually has equality constant defined in the class in order as MergeDoReplace, MergeDoMerging, MergeDoPush. The code below changes the merge behaviour in this way when there is an existing integer key try to merge their value together and for the rest default strategy is called.

$a = [ 0 => 'foo'];
$b = [ 0 => 'bar'];

$stdArray = StdArray::of($a)
    ->merge($b, function ($value, $currValue, $key) {
        if (is_int($key))
            return StdArray::MergeDoMerging;

        return StdArray\DefaultMergeStrategy::call($value, $currValue, $key);
    });
    
// [0 => ['foo', 'bar']]

If the array not consist of other different mix of structures which is not important how it's gonna merge, the codes above can be simplified to this:

$stdArray = StdArray::of($a)
    ->merge($b, StdArray::MergeDoMerging);

Other examples to see how merge works in default:

$stdArray = StdArray::of([ 0 => ['foo']])
    ->merge([ 0 => 'bar']);
// [['foo'], 'bar']

$stdArray = StdArray::of([ 0 => 'foo'])
    ->merge([ 0 => ['bar']]);
// ['foo', ['bar']]

$stdArray = StdArray::of(['key' => 'foo'])
    ->merge(['key' => 'bar']);
// ['key' => 'bar']

$stdArray = StdArray::of(['key' => ['foo']])
    ->merge(['key' => 'bar']);
// ['key' => ['foo', 'bar']]

$stdArray = StdArray::of(['key' => 'foo'])
    ->merge(['key' => ['bar']]);
// ['key' => ['bar', 'foo']]


$a = [ 'key' => [['foo', 'a' => null], 'a' => 1], 'foo'];
$b = [ 'key' => [[['bar'], 'a' => 1, 'b' => 2], 'b' => 2], 'bar'];

$stdArray = StdArray::of($a)
    ->merge($b);
    
/*
[
    'key' => [
        [
            'foo',
            'a' => 1,
            ['bar'],
            'b' => 2,
        ],
        'a' => 1,
        'b' => 2,
    ],
    'foo',
    'bar',
]
*/

Another merge strategy example:

If any field has a same value it's not gonna merge otherwise the values getting merged for the rest. In this sample 'value' field has same value in both array and will not get merged.

$a = ['field' => [
  'type' => 'invalid-type',
  'message' => 'Error message 1.',
  'value' => 'invalid_value',
]];

$b = ['field' => [
  'type' => 'not-in-range',
  'message' => 'Error message 2.',
  'value' => 'invalid_value',
]];

$messages = StdArray::of($a)
   ->merge($b, function ($value, $currVal) {
        if ($value === $currVal)
           return StdArray::MergeDoReplace;
        return StdArray::MergeDoMerging;
})->asInternalArray;

/*
[
    'field' => [
        'type' => [
            'invalid-type',
            'not-in-range',
        ],
        'message' => [
            'Error message 1.',
            'Error message 2.',
        ],
        'value' => 'invalid_value',
    ],
]
*/

except - Remove array element(s) by key(s)

The except method will remove all given key(s) element from array if key exists.

$initialValue = [1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3];
$stdArray = StdArray::of($initialValue)
    ->except(...[1, 3]);

// ['a' => 0, 'b' => 1, 'd' => 3]

keep - Keep only given key(s) in array

The keep method only keeps the given keys in array if they exists and rest of array will be cleared from unwanted keys.

$initialValue = [1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3];
$stdArray = StdArray::of($initialValue)
    ->keep(...[1, 3]);

// [1 => 'b', 3 => 'd'];

keepIntersection - Keep values that are present in all the arguments.

Computes the intersection of arrays, compares data by a callback function.

$initialValue = ["a" => "green", "b" => "brown", "c" => "blue", "red"];
$stdArray = StdArray::of($initialValue)
    ->keepIntersection(["a" => "GREEN", "B" => "brown", "yellow", "red"], 
      function($a, $b) {
        return \strcasecmp($a, $b);
      });

// ['a' => 'green', 'b' => 'brown', 0 => 'red']

getFirst - Get first array element

The getFirst method return first array element of array which the result is another StdArray object with the single element from beginning of current array. if current array is empty the result will be an empty array.

$initialValue = [1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3];
$firstElement = StdArray::of($initialValue)
    ->getFirst();

echo $firstElement->key() . ' = ' . $firstElement->value();
// 1 = b

getFirstAndRemove - Pop an element off the beginning of array

The getFirstAndRemove shift element off from beginning of array by removing the element from array and return new StdArray object containing the element.

This method will reset the array pointer of the input array to the beginning.

$stdArray = StdArray::of([1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3]);
$firstElement = $stdArray
    ->getFirstAndRemove();

echo $firstElement->key() . ' = ' . $firstElement->value();
// 1 = b

$stdArray->reset();
echo $stdArray->key() . ' = ' . $stdArray->value();
// 3 = d

getLast - Get last array element

The getLast method return last array element of array which the result is another StdArray object with the single element from end of current array. if current array is empty the result will be an empty array.

$initialValue = [1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3];
$lastElement = StdArray::of($initialValue)
    ->getLast();

echo $lastElement->key() . ' = ' . $lastElement->value();
// d = 3

getLastAndRemove - Pop an element off the end of array

The getLastAndRemove pop element off from end of array by removing the element from array and return new StdArray object containing the element.

This method will reset the array pointer of the input array to the beginning.

$stdArray = StdArray::of([1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3]);
$lastElement = $stdArray
    ->getLastAndRemove();

echo $lastElement->key() . ' = ' . $lastElement->value();
// d = 3

echo $stdArray->key() . ' = ' . $stdArray->value();
// 1 = b

getKeys - List Array keys

The getKeys method returns an instance of StdArray with values contains all the keys from current array.

$stdArray = StdArray::of([1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3]);
$arrayKeys = $stdArray->getKeys();
// [1, 3, 'a', 'b', 'd']

getValues - List Array values

The getValues method returns an instance of StdArray contains all the values from current array indexed from zero.

$stdArray = StdArray::of([1 => 'b', 3 => 'd', 'a' => 0, 'b' => 1, 'd' => 3]);
$arrayValues = $stdArray->getValues();
// ['b', 'd', 0, 1, 3]

getColumn - representing a single column from array

The getColumn method retrieves all of the values for a given key(column), it possible to specify how the resulting values from column to be keyed by defining another key as second argument.

$rows = [
  ['id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'],
  ['id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'],
];

$stdArray = StdArray::of($rows)->getColumn('title');
// ['Foo', 'Bar']

$stdArray = StdArray::of($rows)->getColumn('title', 'id');
// [3 => 'Foo', 5 => 'Bar']

$stdArray = StdArray::of($rows)->getColumn(null, 'id');
// [
//   3 => ['id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'],
//   5 => ['id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'],
// ]

getFlatten - Get flatten face of multi-dimensional array

The getFlatten method flattens a multi-dimensional array into a single level array that uses given notation to indicate depth and return new StdArray object containing the element.

$nestedArray = [
    'key1' => 'value1',
    'key2' => [
        'subkey' => 'subkeyval'
    ],
    'key3' => 'value3',
    'key4' => [
        'subkey4' => [
            'subsubkey4' => 'subsubkeyval4',
            'subsubkey5' => 'subsubkeyval5',
        ],
        'subkey5' => 'subkeyval5'
    ]
];

$flattenArr = StdArray::of($nestedArray)
    ->getFlatten('.');
    
/*
array (
  'key1' => 'value1',
  'key2.subkey' => 'subkeyval',
  'key3' => 'value3',
  'key4.subkey4.subsubkey4' => 'subsubkeyval4',
  'key4.subkey4.subsubkey5' => 'subsubkeyval5',
  'key4.subkey5' => 'subkeyval5',
)
*/
$flattenArr = StdArray::of($nestedArray)
    ->getFlatten('[%s]');

/*
array (
  'key1' => 'value1',
  'key2[subkey]' => 'subkeyval',
  'key3' => 'value3',
  'key4[subkey4][subsubkey4]' => 'subsubkeyval4',
  'key4[subkey4][subsubkey5]' => 'subsubkeyval5',
  'key4[subkey5]' => 'subkeyval5',
)
*/

reindex - ReIndex array keys

The reindex method will index array values from zero based on the current position of the item, if the key is an integer then take none-integer keys sort keys and concat them to the end of array as final result.

$stdArray = StdArray::of([1 => 'b', 3 => 'd', 'a' => 0, 'd' => 3, 'b' => 1]);
$arrayKeys = $stdArray->reindex();
// [0 => b, 1 => d, 'a' => 0, 'b' => 1, 'd' => 3]

clean - Clean elements with empty values

The clean method Clean all elements with falsy values from the current array.

$stdArray = StdArray::of([null, false, true, -9, 
  'foo' => false, 'foo', 'lall' => [], null => '^']);
  
$stdArray->clean();
// [2 => 1, 3 => -9, 4 => foo, null => ^]

isEqual - Check whether origin array is equal to given value

The isEqual method check equality of current array with the given value can be any type which can constructed by StdArray

$stdArray = new StdArray([1, 2, 3, 4])
    ->isEqualTo([1, 2, 3, 4]);
    
// true

isEmpty - Check if array is empty

The isEmpty method returns true if the collection is empty; otherwise, false is returned.

StdArray::of([])->isEmpty();
// true

isAssoc - Check if array is Associative array

$initialValue = ['field' => 'value', 'otherField' => 'other-value'];
$stdArray = new StdArray($initialValue);

$stdArray->isAssoc();
// true

Enums

An enumeration type, "enum" for short, is a data type to categorise named values. Enums can be used instead of hard coded strings to represent, for example, the status of a blog post in a structured and typed way. For example, a Post object supposed to have three different values for status of the post, it could be one of three strings: draft, published or archived.

class Post
{
    function setStatus(PostStatus $status): void
    {
        $this->status = $status(); // Invoke StdEnum to get value
    }
}

With StdEnum the enumeration type can be defined as follow:

class PostStatus extends StdEnum
{
    const Draft = 'draft';
    const Published = 'published';
    const Archived = 'archived';
}

And Named values can be accessed by calling as static methods:

$post->setStatus(PostStatus::Draft());

Declaration

To define new enumeration type it's needed to just extend the StdEnum and define needed named values as public constant inside the class.

The StdEnum will automatically recognize the possible values from defined constants and will proxy all static method calls to related constant name to build new instance with value equal to const.

To get IDE support on autocompletion on the class the Docblock notation can be defined but it's not necessary but highly recommended.

/**
 * @method static EnumFixture Draft();
 * @method static EnumFixture Published();
 * @method static EnumFixture Archived();
 */
class PostStatus extends StdEnum
{
    const Draft = 'draft';
    const Published = 'published';
    const Archived = 'archived';
    
    // 'draft' as a default value
    function __construct($initial_value = self::Draft)
    {
        parent::__construct($initial_value);
    }
}

Constructing object

Instantiating object is possible by new syntax and by calling statically the value name like ::NamedConst.

$postStatus = PostStatus::Archived();
// which is equal to
$postStatus = new PostStatus(PostStatus::Archived);

When the named value is not valid to class the \UnexpectedValueException will thrown.

new PostStatus('invalid');
// Exception: \UnexpectedValueException

PostStatus::UNDEFINED();
// Exception: \UnexpectedValueException

Get Value of enum object

The StdEnum is invokable and by calling the object like functions will return the value which is holding.

$postStatus = PostStatus::Draft();
if ($postStatus() === PostStatus::Draft) {
   echo 'Post is on Draft.';
}

// Post is on Draft.

An example on How it's used in classes which are needed these values can be found here.

Equality check

The isEqualTo method will tests whether enum instances are equal (returns true if enum values are equal, false otherwise).

$archiveStatus = PostStatus::Archived();

// true
$archivedStatus->isEqualTo(new PostStatus(PostStatus::Archived));
$archivedStatus->isEqualTo(PostStatus::Archived());

// false
$archivedStatus->isEqualTo(PostStatus::Draft());

Check on classes which extends from base enum class

The equality check on different object can be true only for the classes which extended from base enum class with same value. otherwise result check for different objects with the same value is always false.

class ExtendedPostStatus extends PostStatus
{
    const Banned = 'banned';
}


$archiveStatus = ExtendedPostStatus::Archived();
$archivedStatus->isEqualTo(PostStatus::Archived());
// true

List all possible enums

Return an array of enum name to the Enum object initialized with the internal value of related constant.

$enums = PostStatus::listEnums();

/*
[
   'Draft' => (PostStatus) 'draft',
   // ..
]
*/

Strings

The StdString is easy to use and easy to bundle library include convenience methods to handle strings with different character set encoding support. This library will also auto-detect server environment and will use the available installed php-extensions and pick them as a fallback to do the task.

The StdString is immutable object which means any modification method to the current object will apply changes on the new instance of the object and the current one will stay same without any changes.

It's a wrapper around existence several extensions or libraries like mbstring, iconv, intl, ... in addition any custom user implementation can be registered as a fallback to the library through the available interfaces.

If Symfony Polyfills for mbstring is available (required in composer) or classes are loaded or autoloadable by PHP it might be selected as a fallback by StdString if needed.(https://github.com/symfony/polyfill)

Register custom wrapper

Any custom implementation string wrapper can be registered to StdString by implementing iStringWrapper interface and then simply can be registered.

Registered wrapper will take the higher priority when StdString taking a wrapper from registry basically depends on the implemented methods it would take the job.

StdString::registerWrapper(CustomTestStringWrapper::class);

// Do the string manuplation job ....

StdString::unregisterWrapper(CustomTestStringWrapper::class);

Usage sample

echo StdString::of('iñtërnâtiônàlizætiøn')->upperFirstChar();
// Iñtërnâtiônàlizætiøn

echo (string) StdString::of('mĄkA')->lower();
// mąka

echo StdString::of('/foo/bar/baz')
    ->stripPrefix('/')
    ->upperFirstChar();
// Foo/bar/baz

$str = StdString::of('¬fòô bàř¬');
if ($str->isStartedWith('¬fòô ')) {
    echo 'String has started with the expected prefix.';
}

Instantiate and Construct

When string object constructed the object should know about the encoding charset of string, the default charset is UTF-8, depend on the string representing content any other encoding can be chosen.

The Encodings class contains all available charsets which can be used to get more consistency when using different charset on code base.

StdString\Encodings::Charset['UTF-32'];
StdString\Encodings::CharsetSingleByte['ISO-8859-1'];

Values given as initial value to the class will casting to string type if it's not string. On Any object which can't cast to string \UnexpectedValueException will thrown.

$stdString = new StdString('Hello!');
$stdString = new StdString($toStrigableObject);

$stdString = new StdString(3.14, StdString::CharsetDefault);
// '3.14'

$stdString = new StdString(-1234, StdString::CharsetDefault);
// '-1234'

$stdString = new StdString(true, StdString::CharsetDefault);
// 'true'

It's serializable

The serialized string is a 3 byte sequence in the beginning of the string which indicate the encoding charset of the string and the rest is the origin string byte sequences means the whole original string. For instance the serialized string 中文空白 with encoding UTF-16 represented like this: \x00\x25 中文空白 the string part is also should be considered as byte sequences but here just to make it more understandable the string is used.

Serialized string is safe to store in DB and other storages and can be searchable and can be converted and used byStdString object with same encoding again.

$stdStr    = StdString::of('中文空白', StdString\Encodings::Charset['UTF-32']);
$newStdStr = StdString::ofSerializedByteOrder(
    $stdStr->asSerializedByteOrder()
);

echo $newStdStr->encoding;
// UTF-32

It's array like accessible

Characters within strings may be only accessed by specifying the zero-based offset of the desired character after the string using square array brackets, as in $str[0].

Instead of default PHP behaviour of accessing elements which is consider the string as series of 8bit data for each element, here each element represent a character in string. in multibyte encodings one character may consist more than 8bit.

$str = '中文空白';
$stdString = StdString::of($str);

echo $stdString[1];
// 文

var_dump(isset($str[6]));       // true
var_dump(isset($stdString[4])); // false

It's countable

$stdString = StdString::of('中文空白');
$stdString->length();
// or
count($stdString);
// 4

Get string encoding

To know which charset encoding object is instantiated with there are two readonly property which hold the same value.

$stdStr = new StdString('', StdString\Encodings::CharsetSingleByte['ASCII']);
$stdStr->charset;
$stdStr->encoding;
// ASCII

lower

Convert all alphabetic characters within string to lowercase.

echo StdString::of('foO Bar BAZ')->lower();
// foo bar baz

echo StdString::of('mĄkA')->lower();
// mąka

lowerFirstChar

Makes string's first char lowercase, if that character is alphabetic.

echo StdString::of('Foo bar')->lowerFirstChar();
// foo bar

echo StdString::of('Öäü')->lowerFirstChar();
// 'öäü'

upper

Convert all alphabetic characters within string to uppercase.

echo StdString::of('foO Bar BAZ')->upper();
// FOO BAR BAZ

echo StdString::of('Umlaute äöü')->upper();
// UMLAUTE ÄÖÜ

upperFirstChar

Makes string's first char uppercase, if that character is alphabetic.

echo StdString::of('foo bar')->upperFirstChar();
// Foo bar

echo StdString::of('öäü')->upperFirstChar();
// Öäü

camelCase

Note that whitespaces will be removed from the string. only _ character will be considered as a separator char and will be used in creating camelCase result of string other like numbers will be considered as part of words.

echo StdString::of('camelCase')->camelCase();
// camelCase

echo StdString::of('camel_case')->camelCase();
// camelCase

echo StdString::of('  camel  case')->camelCase();
// camelCase

echo StdString::of('Camel2case')->camelCase();
// camel2case

echo StdString::of('Camel2_case')->camelCase();
// camel2Case

echo StdString::of('ΣamelCase')->camelCase();
// σamelCase

The delimiter can given to the method and that will be remain if string at the beginning or end has it.

echo StdString::of('__Camel_case__')->camelCase('_');
// __cascalCase__

pascalCase

echo StdString::of('pascalCase')->pascalCase();
// PascalCase

echo StdString::of('pascal__case')->pascalCase();
// PascalCase

echo StdString::of('  pascal  case')->pascalCase();
// PascalCase

echo StdString::of('pascal2case')->pascalCase();
// Pascal2case

echo StdString::of('pascal2_case')->pascalCase();
// Pascal2Case

echo StdString::of('σascalCase')->pascalCase();
// ΣascalCase

The delimiter can given to the method and that will be remain if string at the beginning or end has it.

echo StdString::of('__σascal_case__')->pascalCase('_');
// __ΣascalCase__

snakeCase

echo StdString::of('SnakeCase')->snakeCase();
// snake_case

echo StdString::of('Snake#Case')->snakeCase();
// snake#_case

echo StdString::of('snake  case')->snakeCase();
// snake_case

echo StdString::of('snake2Case')->snakeCase();
// snake2_case

echo StdString::of('ΣτανιλCase')->snakeCase();
// στανιλ_case

The delimiter can given to the method and that will be remain if string at the beginning or end has it.

echo StdString::of('__snakeCase__')->snakeCase('_');
// __snake_case__

subString - Return part of a string

Returns the portion of string specified by the start and length parameters. to see about all possible values to the parameters check here.

$stdString = StdString::of('abcde');
echo $stdString->subString(1, 2);  // bc
echo $stdString->subString(-2);    // de
echo $stdString->subString(-3, 2); // cd

$stdString = StdString::of('中文空白');
echo $stdString->subString(1, 2);  // 文空
echo $stdString->subString(-2);    // 空白

stripPrefix - remove string from beginning

Remove given string if only it's found at the beginning of the original string.

$stdString = StdString::of('/foo/bar/baz');
echo $stdString->stripPrefix('/');      // foo/bar/baz
echo $stdString->stripPrefix('/foo/');  // bar/baz

$stdString = StdString::of('آهایی دنیا سلام');
echo $stdString->stripPrefix('آهایی');  
// دنیا سلام

Second argument to the method will strip any whitespace from beginning of array before strip given string when it has true value. which is default value.

echo StdString::of('  /foo/bar/')->stripPrefix();
// '/foo/bar/'

stripPostfix - remove string from end

Remove given string if only it's found at the end of the original string.

$stdString = StdString::of('foo/bar/baz/');
echo $stdString->stripPostfix('/');      // foo/bar/baz
echo $stdString->stripPostfix('/baz/');  // foo/bar

$stdString = StdString::of('آهایی دنیا سلام');
echo $stdString->stripPostfix('سلام');  
// آهایی دنیا

Second argument to the method will strip any whitespace from end of array before strip given string when it has true value. which is default value.

echo StdString::of('/foo/bar/  ')->stripPostfix();
// '/foo/bar/'

hasStartedWith - Get prefixed string

Get the given string as a result if the origin string is started with that otherwise return empty string. Method will accept variadic argument of string values and will return the one which match first with the origin string prefix.

$stdStr = StdString::of('foobarbaz');
$prefix = $str->hasStartedWith('bo', 'fo');

echo $prefix->__toString(); // fo 

isStartedWith - Check prefix of string

Method will accept variadic argument of string values and will check if the string has one of the given values in the beginning.

$stdStr = StdString::of('foobarbaz');
$str->hasStartedWith('fo');
// true

hasContains - Has string contains a substring

Determine existence of a substring in origin string, if it has the substring will be returned as the result otherwise empty string will be returned.

$stdString = new StdString('щука 漢字');
echo $stdString->hasContains('щука');
// щука

isContains - Check string contains a substring

Check if string contains given substring and return boolean value true if has substring otherwise return false.

$stdString = new StdString('щука 漢字');
$stdString->isContains('щука');
// true

concatSafeImplode - Safe join strings

Join string elements from array with a delimiter glue, joining will be safe to not include extra glue character if concat string has already contain the delimiter glue.

$stdInitial = StdString::of('/a/');
$stdString  = $stdInitial->concatSafeImplode(['b/', 'c', 'd/'], '/');

echo $stdString;
// /a/b/c/d/

normalizeExtraNewLines - Remove extra line breaks

Remove extra new lines and line breaks from string.

$stdString = new StdString("aaжи\r\n\r\n\r\n\r\nвπά\n\n\n\n\n우리をあöä\r\n");
echo $stdString->normalizeExtraNewLines();
// aaжи\r\n\r\nвπά\n\n우리をあöä\r\n

normalizeWhitespaces - Strip and remove whitespaces

$stdString = new StdString("\tΟ \r\n   συγγραφέας  ");
echo $stdString->normalizeWhitespaces();
// Ο συγγραφέας

Last updated

Was this helpful?