PHP Classes

File: slice.php

Recommend this page to a friend!
  Classes of Nathaniel Sherwood   Encase PHP Functional Programming   slice.php   Download  
File: slice.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Encase PHP Functional Programming
Make functions work as if they are class functions
Author: By
Last change:
Date: 5 years ago
Size: 2,266 bytes
 

Contents

Class file image Download
<?php
namespace Encase\Functional;

/**
 * Extract a portion of an array, string or \Traversable object.
 * Sliced traversables are returned as arrays.
 *
 * Will try to use native PHP functions where possible.
 *
 * @see https://php.net/manual/en/function.array-slice.php
 * @see https://php.net/manual/en/function.mb-substr.php
 * @see https://php.net/manual/en/function.iterator-to-array.php
 *
 * @param array|string $value
 * @param int|null $start Where to begin extraction.
 * @param int|null $size Where to end extraction.
 * @return mixed A slice of $value.
 */
function slice($value, ?int $start, int $end = null)
{
   
$type = assertType($value, ['\Traversable', 'iterable', 'string'], 'value');

   
$start = $start ?? 0;

   
// Figure out the slice size as PHP funcs expect sizes rather than indexes.
   
if ($end !== null) {
        if (
$start && ($end > 0 || $start < 0) && $end < $start) {
            [
$start, $end] = [$end, $start];
        }

       
$size = $end < 0 ? $end : $end - $start;
    } else {
       
$size = null;
    }

   
// Implement strings using unicode-aware (where possible) substr.
    // TODO: make mb_* a requirement of the library?
   
if ($type === 'string') {
        return \
function_exists('mb_substr') ?
            \
mb_substr($value, $start, $size) :
            \
substr($value, $start, $size);
    }

   
// If the value is traversable, we can extract with a foreach loop.
   
if ($type === '\Traversable') {
       
// Try to use PHP's iterator_to_array for classes implementing
        // \IteratorAggreggate.
       
if ($value instanceof \IteratorAggregate && $start > 0) {
            if (
$size <= 0) {
               
$size = -1;
            }

           
$value = \iterator_to_array(
                new \
LimitIterator($value->getIterator(), $start, $size), true
           
);

           
$start = 0;
        } else {
           
// Fall back to a foreach by building an array out of the iterator. If
            // we have a positive $size we can stop early and return that array.
            // If not, we'll fall back with array_slice with a $start of 0.
           
$output = [];

            foreach (
$value as $key => $value) {
                if (
$start > 0) {
                    --
$start;
                    continue;
                }

               
$output[$key] = $value;

                if (
$size !== null && $size > 0) {
                    --
$size;

                    if (!
$size) {
                        return
$output;
                    }
                }
            }

           
$value = $output;
        }
    }

    return \
array_slice($value, $start, $size, true);
}