luyadev/yii-helpers

View on GitHub
src/helpers/ArrayHelper.php

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
<?php

namespace luya\yii\helpers;

use yii\helpers\BaseArrayHelper;

/**
 * Helper methods when dealing with Arrays.
 *
 * Extends the {{yii\helpers\ArrayHelper}} class by some useful functions like:
 *
 * + {{luya\yii\helpers\ArrayHelper::toObject()}}
 * + {{luya\yii\helpers\ArrayHelper::arrayUnshiftAssoc()}}
 * + {{luya\yii\helpers\ArrayHelper::typeCast()}}
 * + {{luya\yii\helpers\ArrayHelper::search()}}
 * + {{luya\yii\helpers\ArrayHelper::searchColumn()}}
 * + {{luya\yii\helpers\ArrayHelper::searchColumns()}}
 * + {{luya\yii\helpers\ArrayHelper::generateRange()}}
 *
 * @author Basil Suter <basil@nadar.io>
 * @since 1.0.0
 */
class ArrayHelper extends BaseArrayHelper
{
    /**
     * @var array An array with sensitive keys which are used if no keys will be passed to {{luya\yii\helpers\ArrayHelper::coverSensitiveValues()}}.
     */
    public static $sensitiveDefaultKeys = ['password', 'pwd', 'pass', 'passwort', 'pw', 'token', 'hash', 'authorization', 'auth'];

    /**
     * Create an object from an array.
     *
     * @param array $array
     * @return object
     */
    public static function toObject(array $array)
    {
        return json_decode(json_encode($array), false, 512);
    }

    /**
     * Cover sensitive values from a given list of keys.
     *
     * The main purpose is to remove passwords transferred to API when existing in POST, GET or SESSION.
     *
     * Example:
     *
     * ```php
     * $data = ArrayHelper::coverSensitiveValues(['username' => 'foo', 'password' => 'bar'], ['password']];
     *
     * var_dump($data); // array('username' => 'foo', 'password' => '***');
     * ```
     *
     * @param array $data The input data to cover given sensitive key values. `['username' => 'foo', 'password' => 'bar']`.
     * @param array $keys The keys which can contain sensitive data inside the $data array. `['password', 'pwd', 'pass']` if no keys provided the {{luya\yii\helpers\ArrayHelper::$sensitiveDefaultKeys}} is used.
     */
    public static function coverSensitiveValues(array $data, array $keys = [])
    {
        if (empty($keys)) {
            $keys = self::$sensitiveDefaultKeys;
        }

        $clean = [];
        foreach ($keys as $key) {
            $kw = strtolower($key);
            foreach ($data as $k => $v) {
                if (is_array($v)) {
                    $clean[$k] = static::coverSensitiveValues($v, $keys);
                } elseif (is_scalar($v) && ($kw == strtolower($k) || StringHelper::startsWith(strtolower($k), $kw))) {
                    $v = str_repeat("*", strlen($v));
                    $clean[$k] = $v;
                }
            }
        }

        // the latter overrides the former
        return array_replace($data, $clean);
    }

    /**
     * Prepend an associative array item as first entry for a given array.
     *
     * Adds the given key with value as first entry to $arr
     *
     * @param array $arr The array where the value should be prepend
     * @param string $key The new array key
     * @param mixed $val The value for the new key
     * @return array
     */
    public static function arrayUnshiftAssoc(&$arr, $key, $val)
    {
        $arr = array_reverse($arr, true);
        $arr[$key] = $val;
        return array_reverse($arr, true);
    }

    /**
     * TypeCast values from a mixed array source. numeric values will be casted as integer.
     *
     * This method is often used to convert correct JSON response arrays
     *
     * @param array $array The array which should be type casted
     * @return array An array with type casted values
     */
    public static function typeCast(array $array)
    {
        $return = [];

        foreach ($array as $k => $v) {
            if (is_numeric($v)) {
                $return[$k] = StringHelper::typeCastNumeric($v);
            } elseif (is_array($v)) {
                $return[$k] = self::typeCast($v);
            } else {
                $return[$k] = $v;
            }
        }

        return $return;
    }

    /**
     * Search through all keys inside of an array, any occurrence will return the rest of the array.
     *
     * ```php
     * $data  = [
     *     ['name' => 'Foo Bar', 'id' => 1],
     *     ['name' => 'Bar Baz', 'id' => 2],
     * ];
     * ```
     *
     * Assuming the above array the expression `ArrayHelper::search($data, 1)` would return:
     *
     * ```php
     * $data  = [
     *     ['name' => 'Foo Bar', 'id' => 1],
     * ];
     * ```
     *
     * Searching for the string `Bar` would return the original array if bar would be found in both.
     *
     * In order to search only in certain keys defined $keys attribute:
     *
     * ```php
     * ArrayHelper::search($data, 'Foo', false, ['name']);
     * ```
     *
     * The above example will search only in the array key `name` for the `Foo` expression.
     *
     * @param array $array The multidimensional array keys.
     * @param string $searchText The text you where search inside the rows.
     * @param boolean $sensitive Whether to use strict sensitive search (`true`) or case insensitive search (`false`).
     * @param array $keys A list of array keys which should be taken to search in, if empty or not provided it will lookup all array keys by default.
     * @return array The modified array depending on the search result hits.
     */
    public static function search(array $array, $searchText, $sensitive = false, array $keys = [])
    {
        $function = $sensitive ? 'strpos' : 'stripos';
        return array_filter($array, function ($item) use ($searchText, $function, $keys) {
            $response = false;
            foreach ($item as $key => $value) {
                if ($response) {
                    continue;
                }

                if (!empty($keys) && !in_array($key, $keys)) {
                    continue;
                }

                if ($function($value, "$searchText") !== false) {
                    $response = true;
                }
            }
            return $response;
        });
    }

    /**
     * Search for a column value inside a multidimensional array and return the array with the found key.
     *
     * Compared to `searchColumns()` this function return will return the first found result.
     *
     * ```php
     * $array = [
     *     ['name' => 'luya', 'userId' => 1],
     *     ['name' => 'nadar', 'userId' => 2],
     * ];
     *
     * $result = ArrayHelper::searchColumn($array, 'name', 'nadar');
     *
     * // output:
     * // array ('name' => 'nadar', 'userId' => 2);
     * ```
     *
     * > This will not work with associative keys
     *
     * @param array $array The array with the multimensional array values.
     * @param string $column The column to lookup and compare with the $search string.
     * @param string $search The string to search inside the provided column.
     * @return array|boolean
     */
    public static function searchColumn(array $array, $column, $search)
    {
        $array = array_values($array); // align array keys
        $columns = array_column($array, $column);
        $key = array_search($search, $columns);
        return ($key !== false) ? $array[$key] : false;
    }

    /**
     * Search for columns with the given search value, returns the full array with all valid items.
     *
     * Compared to `searchColumn()` this function return will return all found results.
     *
     * > This function is not case-sensitive, which means `FOO` will match `Foo`, `foo` and `FOO`.
     *
     * ```php
     * $array = [
     *     ['name' => 'luya', 'userId' => 1],
     *     ['name' => 'nadar', 'userId' => 1],
     * ];
     *
     * $result = ArrayHelper::searchColumns($array, 'userId', '1');
     *
     * // output:
     * // array (
     * //     array ('name' => 'luya', 'userId' => 1),
     * //     array ('name' => 'nadar', 'userId' => 1)
     * // );
     * ```
     *
     * @param array $array The multidimensional array input
     * @param string $column The column to compare with $search string
     * @param mixed $search The search string to compare with the column value.
     * @return array Returns an array with all valid elements.
     */
    public static function searchColumns(array $array, $column, $search)
    {
        $keys = array_filter($array, function ($var) use ($column, $search) {
            return strcasecmp($search, (string) $var[$column]) == 0 ? true : false;
        });

        return $keys;
    }

    /**
     * Generate an array from a range with an appending optional text.
     *
     * This is commonly used when generate dropdowns in forms to select a number of something.
     *
     * When $text is an array, the first key is the singular value to use, the second is the pluralized value.
     *
     * ```php
     * $range = ArrayHelper::generateRange(1, 3, 'ticket');
     * // array (1 => "1 ticket", 2 => "2 ticket", 3 => "3 ticket")
     * ```
     *
     * Using the pluralized texts:
     *
     * ```php
     * $range = ArrayHelper::generateRange(1, 3, ['ticket', 'tickets']);
     * // array (1 => "1 ticket", 2 => "2 tickets", 3 => "3 tickets")
     * ```
     *
     * PHP's `range()` function is used to generate the array range.
     *
     * @param string|integer $from The range starts from
     * @param string|integer $to The range ends
     * @param string|array $text Optinal text to append to each element. If an array is given the first value is used
     * for the singular value, the second will be used for the pluralized values.
     * @return array An array where the key is the number and value the number with optional text.
     */
    public static function generateRange($from, $to, $text = null)
    {
        $range = range($from, $to);
        $array = array_combine($range, $range);

        if ($text) {
            array_walk($array, function (&$item, $key) use ($text) {
                if (is_array($text)) {
                    list($singular, $plural) = $text;
                    if ($key == 1) {
                        $item = "{$key} {$singular}";
                    } else {
                        $item = "{$key} {$plural}";
                    }
                } else {
                    $item = "{$key} {$text}";
                }
            });
        }

        return $array;
    }

    /**
     * Helper method to generate an array with the same keys and values.
     *
     * ```php
     * $data = ArrayHelper::combine(['foo', 'bar']);
     *
     * // generates
     * ['foo' => 'foo', 'bar' => 'bar'];
     * ```
     *
     * @param array $array The array to combine.
     * @return array
     */
    public static function combine(array $array)
    {
        return array_combine($array, $array);
    }
}