PHP Classes

File: _functions.php

Recommend this page to a friend!
  Classes of Payam Naderi  >  Std  >  _functions.php  >  Download  
File: _functions.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Std
General purpose Web development classes
Author: By
Last change: code cleanup
implement insert into array pos
Date: 4 years ago
Size: 25,497 bytes
 

Contents

Class file image Download
<?php
namespace
{
    !defined('DS')   and define('DS', DIRECTORY_SEPARATOR);
    !defined('VOID') and define('VOID', "\0");

    /**
     * Return Unmodified Argument
     *
     * usage:
     *
     *    __(new Classes())->callMethods();
     *
     * @param mixed $var
     * @return mixed
     */
    function __($var)
    {
        return $var;
    }
}

namespace Poirot\Std\Invokable
{
    use Poirot\Std\Interfaces\Pact\ipInvokableCallback;


    /**
     * Resolve Arguments Matched With Callable Arguments
     *
     * @param callable           $callable
     * @param array|\Traversable $parameters Params to match with function arguments
     *
     * @return \Closure
     */
    function resolveCallableWithArgs(/*callable*/$callable, $parameters)
    {
        if ($parameters instanceof \Traversable)
            $parameters = \Poirot\Std\cast($parameters)->toArray();


        $reflection       = reflectCallable($callable);
        $matchedArguments = resolveArgsForReflection($reflection, $parameters);

        if (!empty($parameters) && empty($matchedArguments))
            // In Case That Fun Has No Any Argument.
            // exp. func() { $args = func_get_args() ..
            $matchedArguments = $parameters;

        $callbackResolved = function() use ($callable, $matchedArguments) {
            return call_user_func_array($callable, $matchedArguments);
        };

        return $callbackResolved;
    }

    /**
     * Resolve Arguments Matched With Reflection Method/Function
     *
     * @param \ReflectionMethod|\ReflectionFunction $reflectFunc
     * @param array|\Traversable                    $givenArgs  Params to match with function arguments
     * @param bool                                  $inDepth     Resolve argument in depth from parameters; if false just
     *                                                           resolve arguments by name
     *
     * @return array Of Matching Arguments
     */
    function resolveArgsForReflection(/*callable*/$reflectFunc, $givenArgs, $inDepth = true)
    {
        if ( !($reflectFunc instanceof \ReflectionFunction || $reflectFunc instanceof \ReflectionMethod) )
            throw new \InvalidArgumentException(sprintf(
                '(%s) is not reflection.'
                , \Poirot\Std\flatten($reflectFunc)
            ));


        $mappedArgs = array(); $positionalArgs = array();
        foreach ($givenArgs as $key => $val) {
            if (is_string($key)) {
                // Create Map Of "field_name" to "fieldName" and "FieldName" to Resolve To Callable
                $res = (string) \Poirot\Std\cast($key)->camelCase();
                if (! isset($givenArgs[$res]) )
                    $mappedArgs[strtolower($res)] = $val;

                $mappedArgs[strtolower($key)] = $val;
            }

            $mappedArgs[$key] = $val;

            if (is_int($key))
                $positionalArgs[] = $val;
        }

        $matchedArguments = array();
        foreach ($reflectFunc->getParameters() as $reflectArgument)
        {
            /** @var \ReflectionParameter $reflectArgument */
            $argValue = $notSet = uniqid(); // maybe null value is default

            if ( $reflectArgument->isDefaultValueAvailable() )
                $argValue = $reflectArgument->getDefaultValue();

            $argName = strtolower($reflectArgument->getName());
            if ( array_key_exists($argName, $mappedArgs) ) {
                ## resolve argument value match with name given in parameters list
                $argValue = $mappedArgs[$argName];
                unset($mappedArgs[$argName]);

            } elseif ($inDepth) {
                ## in depth argument resolver
                $av = $notSet;
                foreach ( $givenArgs as $k => $v ) {
                    if ( ( $class = $reflectArgument->getClass() ) && is_object($v) && $class->isInstance($v) )
                        $av = $v;

                    if ( $reflectArgument->isArray() && is_array($v) )
                        $av = $v;

                    if ( $reflectArgument->isCallable() && is_callable($v) )
                        $av = $v;

                    if ( is_int($k) )
                        // Placement Argument
                        $av = $v;

                    if ($av !== $notSet) {
                        unset($givenArgs[$k]);
                        break;
                    }
                }

                ($av === $notSet) ?: $argValue = $av;
            }

            if ($argValue === $notSet)
                throw new \InvalidArgumentException(sprintf(
                    'Callable (%s) has no match found on parameter (%s) from (%s) list.'
                    , ($reflectFunc instanceof \ReflectionMethod)
                    ? $reflectFunc->getDeclaringClass()->getName().'::'.$reflectFunc->getName()
                    : $reflectFunc->getName()
                    , $reflectArgument->getName()
                    , \Poirot\Std\flatten($givenArgs)
                ));

            $matchedArguments[$reflectArgument->getName()] = $argValue;
        }

        return $matchedArguments;
    }

    /**
     * Factory Reflection From Given Callable
     *
     * $function
     *   'function_name' | \closure
     *   'classname::method'
     *   [className_orObject, 'method_name']
     *
     * @param $callable
     *
     * @throws \ReflectionException
     * @return \ReflectionFunction|\ReflectionMethod
     */
    function reflectCallable($callable)
    {
        if (!is_callable($callable))
            throw new \InvalidArgumentException(sprintf(
                'Argument provided is not callable; given: (%s).'
                , \Poirot\Std\flatten($callable)
            ));


        if ($callable instanceof ipInvokableCallback)
            $callable = $callable->getCallable();

        if (is_array($callable))
            ## [className_orObject, 'method_name']
            $reflection = new \ReflectionMethod($callable[0], $callable[1]);

        if (is_string($callable)) {
            if (strpos($callable, '::'))
                ## 'classname::method'
                $reflection = new \ReflectionMethod($callable);
            else
                ## 'function_name'
                $reflection = new \ReflectionFunction($callable);
        }

        if (method_exists($callable, '__invoke')) {
            ## Closure and Invokable
            if ($callable instanceof \Closure)
                $reflection = new \ReflectionFunction($callable);
            else
                $reflection = new \ReflectionMethod($callable, '__invoke');
        }

        if (!isset($reflection))
            throw new \ReflectionException;

        return $reflection;
    }
}

namespace Poirot\Std\Lexer
{
    /**
     * Tokenize Parse String Definition Into Parts
     *
     * String in form of:
     *  '[:subdomain{{static}}.]localhost.:tld{{\s+}}'
     *  : variable {delimiter}
     *    delimiter .. localhost\.(?P<tld>\s+)
     *  [optional]
     *
     * TODO: optional in optional;
     *
     * @param string $criteria
     * @param string $expectedLiteral
     *
     * @return array
     */
    function parseCriteria($criteria, $expectedLiteral = null)
    {
        $TOKENS  = preg_quote('\:~<>');

        ($expectedLiteral !== null) ?: $expectedLiteral = '/@+-.';
        $LITERAL = preg_quote($expectedLiteral).'A-Za-z0-9_';

        $currentPos = 0;
        $length     = strlen($criteria);

        $parts      = array();
        $levelParts = array(&$parts);
        $level      = 0;

        while ($currentPos < $length)
        {
            ## the tokens are .:~<>
            preg_match("(\G(?P<_literal_>[$LITERAL]*)(?P<_token_>[$TOKENS]|$))"
                , $criteria
                , $matches
                , 0
                , $currentPos
            );

            if (empty($matches)) break;

            $currentPos += strlen($matches[0]);

            if (!empty($matches['_literal_']))
                $levelParts[$level][] = array('_literal_' => $matches['_literal_']);

            # Deal With Token:
            if (!isset($matches['_token_']) || $matches['_token_'] == '')
                continue;

            $Token = $matches['_token_'];
            if ($Token === '~') {
                // ^ match any character that is not in list
                $pmatch = preg_match("/~(?P<_delimiter_>[^~]+)~/"
                    , $criteria
                    , $matches
                    , 0
                    , 0
                );
                if (! $pmatch )
                    throw new \RuntimeException('Miss usage of ~ ~ token.');

                $val['_regex_'] = $matches['_delimiter_'];
                $levelParts[$level][] = $val;

                $currentPos += strlen($matches[0])+2/*{}*/;
            }
            elseif ($Token === ':') {
                // TODO better expression
                // currently match everything between {{ \w+}}/:identifier_type{{\w+ }}
                // /validate/resend/:validation_code{{\w+}}/:identifier_type{{\w+}}
                // ^ match any character that is not in list
                $pmatch = preg_match("(\G(?P<_name_>[^$TOKENS]+)(?:~(?P<_delimiter_>[^~]+)~)?:?)"
                    , $criteria
                    , $matches
                    , 0
                    , $currentPos
                );

                if (! $pmatch && !(isset($matches['_name_']) && isset($matches['_delimiter_'])) )
                    throw new \RuntimeException(
                        'Found empty parameter name or delimiter on (%s).'
                        , $criteria
                    );

                $parameter = $matches['_name_'];
                $val       = array('_parameter_' => $parameter);
                if (isset($matches['_delimiter_']))
                    $val[$parameter] = $matches['_delimiter_'];

                $levelParts[$level][] = $val;
                $currentPos += strlen($matches[0]);
            }

            elseif ($Token === '\\') {
                // Consider next character as Literal
                // localhost\::port
                $nextChar = $currentPos += 1;
                $levelParts[$level][]   = array('_literal_' => $criteria[$nextChar]);
            }

            elseif ($Token === '<') {
                if (!isset($va)) {
                    $va = array();
                    $n = 0;
                }

                $n++;
                $va[$n] = array();
                $levelParts[$level][]   = array('_optional_' => &$va[$n]);
                $level++;
                $levelParts[$level] = &$va[$n];
            }

            elseif ($Token === '>') {
                unset($levelParts[$level]);
                $level--;

                if ($level < 0)
                    throw new \RuntimeException('Found closing bracket without matching opening bracket');
            } else
                // Recognized unused token return immanently
                $levelParts[$level][]   = array('_token_' => $Token);

        } // end while

        if ($level > 0)
            throw new \RuntimeException('Found unbalanced brackets');

        return $parts;
    }

    /**
     * Build the matching regex from parsed parts.
     *
     * @param array $parts
     * @param bool $matchExact
     *
     * @return string
     */
    function buildRegexFromParsed(array $parts, $matchExact = null)
    {
        $regex = '';

        // [0 => ['_literal_' => 'localhost'], 1=>['_optional' => ..] ..]
        foreach ($parts as $parsed) {
            $definition_name  = key($parsed);
            $definition_value = $parsed[$definition_name];
            // $parsed can also have extra parsed data options
            // _parameter_ String(3) => tld \
            // tld String(4)         => .com
            switch ($definition_name) {
                case '_regex_':
                    $regex .= $definition_value;
                    break;
                case '_token_':
                case '_literal_':
                    $regex .= preg_quote($definition_value);
                    break;

                case '_parameter_':
                    $groupName = '?P<' . $definition_value . '>';

                    if (isset($parsed[$definition_value])) {
                        // Delimiter: localhost:port{{\d+}}
                        $regex .= '(' . $groupName . $parsed[$definition_value] . ')';
                    } else{
                        $regex .= '(' . $groupName . '[^.]+)';
                    }

                    break;

                case '_optional_':
                    $regex .= '(?:' . buildRegexFromParsed($definition_value, null) . ')?';
                    break;
            }
        }

        if ($matchExact !== null) {
            $regex = ( (bool) $matchExact )
                ? "(^{$regex}$)" ## exact match
                // TODO /me match on /members request with matchWhole Option false
                //      so i add trailing slash but not tested completely
                : "(^{$regex})"; ## only start with criteria "/pages[/other/paths]"
        }

        return $regex;
    }

    /**
     * Build String Representation From Given Parts and Params
     *
     * @param array              $parts
     * @param array|\Traversable $params
     *
     * @return string
     */
    function buildStringFromParsed(array $parts, $params = array())
    {
        if ($params instanceof \Traversable)
            $params = \Poirot\Std\cast($params)->toArray();

        if (!is_array($params))
            throw new \InvalidArgumentException(sprintf(
                'Parameters must be an array or Traversable; given: (%s).'
                , \Poirot\Std\flatten($params)
            ));


        // regard to recursive function call
        $isOptional = false;
        $args = func_get_args();
        if ($args && isset($args[2]))
            $isOptional = $args[2];

        # Check For Presented Values in Optional Segment
        // consider this "/[@:username{{\w+}}][-:userid{{\w+}}]"
        // if username not presented as params injected then first part include @ as literal
        // not rendered.
        // optional part only render when all parameter is present
        if ($isOptional) {
            foreach ($parts as $parsed) {
                if (!isset($parsed['_parameter_']))
                    continue;

                ## need parameter
                $neededParameterName = $parsed['_parameter_'];
                if (!(isset($params[$neededParameterName]) && $params[$neededParameterName] !== '') )
                    return '';
            }
        }


        $return    = '';
        // [0 => ['_literal_' => 'localhost'], 1=>['_optional' => ..] ..]
        foreach ($parts as $parsed) {
            $definition_name  = key($parsed);
            $definition_value = $parsed[$definition_name];
            // $parsed can also have extra parsed data options
            // _parameter_ String(3) => tld \
            // tld String(4)         => .com
            switch ($definition_name)
            {
                case '_literal_':
                    $return .= $definition_value;
                    break;

                case '_parameter_':
                    if (!isset($params[$definition_value])) {
                        if ($isOptional)  return '';

                        throw new \InvalidArgumentException(sprintf(
                            'Missing parameter (%s).'
                            , $definition_value
                        ));
                    }

                    $return .= $params[$definition_value];
                    break;

                case '_optional_':
                    $optionalPart = buildStringFromParsed($definition_value, $params, true);
                    if ($optionalPart !== '')
                        $return .= $optionalPart;

                    break;
            }
        }

        return $return;
    }
}

namespace Poirot\Std
{
    use Closure;
    use ReflectionFunction;

    use Poirot\Std\Type\StdArray;
    use Poirot\Std\Type\StdString;
    use Poirot\Std\Type\StdTravers;

    const CODE_NUMBERS       = 2;
    const CODE_STRINGS_LOWER = 4;
    const CODE_STRINGS_UPPER = 8;
    const CODE_STRINGS       = CODE_STRINGS_LOWER | CODE_STRINGS_UPPER;


    /**
     * Cast Given Value Into SplTypes
     * SplTypes Contains Some Utility For That Specific Type
     *
     * ! when you want to force cast to string is necessary to
     *   use type casting cast((string) 10)
     *
     * @param mixed $type
     *
     * @throws \UnexpectedValueException
     * @return StdString|StdArray|StdTravers|\SplType
     */
    function cast($type)
    {
        switch(1) {
            case is_array($type):
                $return = new StdArray($type);
                break;
            case ($type instanceof \Traversable):
                $return = new StdTravers($type);
                break;
            case isStringify($type):
                // Stringify has low priority than \Traversable
                // TODO and object may has both \Traversable or Stringify ...
                $return = new StdString($type);
                break;

            default: throw new \UnexpectedValueException(sprintf(
                'Type (%s) is unexpected.', gettype($type)
            ));
        }

        return $return;
    }

    /**
     * Insert Into Array Pos
     *
     * @param $array
     * @param $element
     * @param $offset
     *
     * @return int
     */
    function insertIntoPosArray(&$array, $element, $offset)
    {
        if ($offset == 0)
            return array_unshift($array, $element);

        if ( $offset + 1 >= count($array) )
            return array_push($array, $element);


        // [1, 2, x, 4, 5, 6] ---> before [1, 2], after [4, 5, 6]
        $beforeOffsetPart = array_slice($array, 0, $offset);
        $afterOffsetPart  = array_slice($array, $offset);
        # insert element in offset
        $beforeOffsetPart = $beforeOffsetPart + [$offset => $element];
        # glue them back
        $array = array_merge($beforeOffsetPart , $afterOffsetPart);
        arsort($array);
    }

    /**
     * Catch Callable Exception
     *
     * @param callable $fun
     * @param callable $catch mixed function(\Exception $exception)
     *
     * @return mixed value returned by $fun or $catch
     * @throws \Exception
     */
    function catchIt(/*callable*/ $fun, /*callable*/ $catch = null)
    {
        try {
            $return = call_user_func($fun);
        } catch (\Exception $e) {
            if ($catch)
                return call_user_func($catch, $e);

            throw $e;
        }

        return $return;
    }

    /**
     * Retry Callable
     *
     * @param Closure $f
     * @param int     $maxTries
     * @param int     $sleep
     *
     * @return mixed
     * @throws \Exception
     */
    function reTry(\Closure $f, $maxTries = 5, $sleep = 0)
    {
        $attempt = 0;

        do {
            try {
                // Pop Payload form Queue
                return $f();

            } catch (\Exception $e) {

                if ($sleep) sleep($sleep);
                $attempt++;
                continue;
            }

        } while ($attempt >= $maxTries);

        throw $e;
    }

    /**
     * Is Given Mime Match With Mime-Type Lists
     *
     * @param array  $mimesList
     * @param string $against
     *
     * @return bool|null
     */
    function isMimeMatchInList(array $mimesList, $against)
    {
        $exMimeType = explode('/', $against);

        $flag = null; // mean we have no list
        foreach ($mimesList as $mimeDef)
        {
            foreach (explode('/', $mimeDef) as $i => $v) {
                if ($v == '*')
                    // Skip This mimeType Definition Part, try next ...
                    continue;

                $flag = false; // mean we have a list

                if (isset($exMimeType[$i]) && strtolower($v) === strtolower($exMimeType[$i]))
                    return $against; // mean we have given mime in list
            }
        }

        return $flag;
    };

    /**
     * Timer Diff
     *
     * @param $timeStart
     *
     * @return string
     */
    function timerDiff($timeStart)
    {
        return number_format(microtime(true) - $timeStart, 3);
    }

    /**
     * // TODO $contains is string contains chars. to use by shuffler
     * Generate Random Strings
     *
     * @param int $length
     * @param int $contains
     *
     * @return string
     */
    function generateShuffleCode($length = 8, $contains = CODE_NUMBERS | CODE_STRINGS)
    {
        $characters = null;

        if (($contains & CODE_NUMBERS) == CODE_NUMBERS)
            $characters .= '0123456789';

        if (($contains & CODE_STRINGS_LOWER) == CODE_STRINGS_LOWER)
            $characters .= 'abcdefghijklmnopqrstuvwxyz';

        if (($contains & CODE_STRINGS_UPPER) == CODE_STRINGS_UPPER)
            $characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

        if ($characters === null)
            throw new \InvalidArgumentException('Invalid Contains Argument Provided; Does Not Match Any Condition.');


        $randomString = '';
        for ($i = 0; $i < $length; $i++)
            $randomString .= $characters[rand(0, strlen($characters) - 1)];

        return $randomString;
    }

    /**
     * Generate a unique identifier
     *
     * @param int $length
     *
     * @return string
     * @throws \Exception
     */
    function generateUniqueIdentifier($length = 40)
    {
        try {
            // Converting bytes to hex will always double length. Hence, we can reduce
            // the amount of bytes by half to produce the correct length.
            return bin2hex(random_bytes($length / 2));
        } catch (\TypeError $e) {
            throw new \Exception('Server Error While Creating Unique Identifier.');
        } catch (\Error $e) {
            throw new \Exception('Server Error While Creating Unique Identifier.');
        } catch (\Exception $e) {
            // If you get this message, the CSPRNG failed hard.
            throw new \Exception('Server Error While Creating Unique Identifier.');
        }
    }

    function makeReadableFileSize($size, $precision = 2)
    {
        static $units = array('B','KB','MB','GB','TB','PB','EB','ZB','YB');
        $step = 1024;
        $i = 0;
        while (($size / $step) > 0.9) {
            $size = $size / $step;
            $i++;
        }

        return round($size, $precision).' '.$units[$i];
    }

    /**
     * With null|false Data Return Default Value
     * Elsewhere Return Data
     *
     * @param null|false|mixed $data
     * @param mixed            $default
     *
     * @return mixed
     */
    function emptyCoalesce($data, $default = null)
    {
        return ($data === null || $data === false) ? $default : $data;
    }

    /**
     * Swap Value Of Two Variable
     *
     * @param mixed $a
     * @param mixed $b
     *
     * @return void
     */
    function swap(&$a, &$b)
    {
        list($a, $b) = array($b, $a);
    }

    /**
     * Check Variable/Object Is String
     *
     * @param mixed $var
     *
     * @return bool
     */
    function isStringify($var)
    {
        return ( (!is_array($var)) && (
                (!is_object($var) && @settype($var, 'string') !== false) ||
                (is_object($var)  && method_exists($var, '__toString' ))
            ));
    }

    /**
     * Convert Object To Array
     *
     * @param \stdClass|object $data
     *
     * @return array
     */
    function toArrayObject($data)
    {
        if (is_object($data))
            $data = get_object_vars($data);

        if (is_array($data))
            return array_map(__FUNCTION__, $data);
        else
            return $data;
    }

    /**
     * Represent Stringify From Variable
     *
     * @param mixed $var
     *
     * @return string
     * @throws \Exception
     */
    function toStrVar($var)
    {
        $r = null;

        if (is_bool($var))
            $r = ($var) ? 'True' : 'False';
        elseif (is_null($var))
            $r = 'null';
        elseif (isStringify($var))
            $r = (string) $var;
        else
            throw new \Exception(sprintf('Variable (%s) cant convert to string.'));

        return $r;
    }

    /**
     * Flatten Value
     *
     * @param mixed $value
     *
     * @return string
     */
    function flatten($value)
    {
        if ($value instanceof Closure) {
            $closureReflection = new ReflectionFunction($value);
            $value = sprintf(
                '(Closure at %s:%s)',
                $closureReflection->getFileName(),
                $closureReflection->getStartLine()
            );
        } elseif (is_object($value)) {
            $value = sprintf('%s:object(%s)', spl_object_hash($value), get_class($value));
        } elseif (is_resource($value)) {
            $value = sprintf('resource(%s-%s)', get_resource_type($value), $value);
        } elseif (is_array($value)) {
            foreach($value as $k => &$v)
                $v = flatten($v);

            $value = 'Array: ['.implode(', ', $value).']';
        } elseif (is_scalar($value)) {
            $value = sprintf('%s(%s)',gettype($value), $value);
        } elseif ($value === null)
            $value = 'NULL';

        return $value;
    }
}
For more information send a message to info at phpclasses dot org.