<?php
namespace Laravel\SerializableClosure\Support;
defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', -4);
defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', -5);
defined('T_FN') || define('T_FN', -6);
defined('T_NULLSAFE_OBJECT_OPERATOR') || define('T_NULLSAFE_OBJECT_OPERATOR', -7);
use Closure;
use ReflectionFunction;
class ReflectionClosure extends ReflectionFunction
{
protected $code;
protected $tokens;
protected $hashedName;
protected $useVariables;
protected $isStaticClosure;
protected $isScopeRequired;
protected $isBindingRequired;
protected $isShortClosure;
protected static $files = [];
protected static $classes = [];
protected static $functions = [];
protected static $constants = [];
protected static $structures = [];
/**
* Creates a new reflection closure instance.
*
* @param \Closure $closure
* @param string|null $code
* @return void
*/
public function __construct(Closure $closure, $code = null)
{
parent::__construct($closure);
}
/**
* Checks if the closure is "static".
*
* @return bool
*/
public function isStatic(): bool
{
if ($this->isStaticClosure === null) {
$this->isStaticClosure = strtolower(substr($this->getCode(), 0, 6)) === 'static';
}
return $this->isStaticClosure;
}
/**
* Checks if the closure is a "short closure".
*
* @return bool
*/
public function isShortClosure()
{
if ($this->isShortClosure === null) {
$code = $this->getCode();
if ($this->isStatic()) {
$code = substr($code, 6);
}
$this->isShortClosure = strtolower(substr(trim($code), 0, 2)) === 'fn';
}
return $this->isShortClosure;
}
/**
* Get the closure's code.
*
* @return string
*/
public function getCode()
{
if ($this->code !== null) {
return $this->code;
}
$fileName = $this->getFileName();
$line = $this->getStartLine() - 1;
$className = null;
if (null !== $className = $this->getClosureScopeClass()) {
$className = '\\'.trim($className->getName(), '\\');
}
$builtin_types = self::getBuiltinTypes();
$class_keywords = ['self', 'static', 'parent'];
$ns = $this->getClosureNamespaceName();
$nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\'.$ns);
$_file = var_export($fileName, true);
$_dir = var_export(dirname($fileName), true);
$_namespace = var_export($ns, true);
$_class = var_export(trim($className ?: '', '\\'), true);
$_function = $ns.($ns == '' ? '' : '\\').'{closure}';
$_method = ($className == '' ? '' : trim($className, '\\').'::').$_function;
$_function = var_export($_function, true);
$_method = var_export($_method, true);
$_trait = null;
$tokens = $this->getTokens();
$state = $lastState = 'start';
$inside_structure = false;
$isFirstClassCallable = false;
$isShortClosure = false;
$inside_structure_mark = 0;
$open = 0;
$code = '';
$id_start = $id_start_ci = $id_name = $context = '';
$classes = $functions = $constants = null;
$use = [];
$lineAdd = 0;
$isUsingScope = false;
$isUsingThisObject = false;
for ($i = 0, $l = count($tokens); $i < $l; $i++) {
$token = $tokens[$i];
switch ($state) {
case 'start':
if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) {
$code .= $token[1];
$state = $token[0] === T_FUNCTION ? 'function' : 'static';
} elseif ($token[0] === T_FN) {
$isShortClosure = true;
$code .= $token[1];
$state = 'closure_args';
} elseif ($token[0] === T_PUBLIC || $token[0] === T_PROTECTED || $token[0] === T_PRIVATE) {
$code = '';
$isFirstClassCallable = true;
}
break;
case 'static':
if ($token[0] === T_WHITESPACE || $token[0] === T_COMMENT || $token[0] === T_FUNCTION) {
$code .= $token[1];
if ($token[0] === T_FUNCTION) {
$state = 'function';
}
} elseif ($token[0] === T_FN) {
$isShortClosure = true;
$code .= $token[1];
$state = 'closure_args';
} else {
$code = '';
$state = 'start';
}
break;
case 'function':
switch ($token[0]) {
case T_STRING:
if ($isFirstClassCallable) {
$state = 'closure_args';
break;
}
$code = '';
$state = 'named_function';
break;
case '(':
$code .= '(';
$state = 'closure_args';
break;
default:
$code .= is_array($token) ? $token[1] : $token;
}
break;
case 'named_function':
if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) {
$code = $token[1];
$state = $token[0] === T_FUNCTION ? 'function' : 'static';
} elseif ($token[0] === T_FN) {
$isShortClosure = true;
$code .= $token[1];
$state = 'closure_args';
}
break;
case 'closure_args':
switch ($token[0]) {
case T_NAME_QUALIFIED:
[$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]);
$context = 'args';
$state = 'id_name';
$lastState = 'closure_args';
break;
case T_NS_SEPARATOR:
case T_STRING:
$id_start = $token[1];
$id_start_ci = strtolower($id_start);
$id_name = '';
$context = 'args';
$state = 'id_name';
$lastState = 'closure_args';
break;
case T_USE:
$code .= $token[1];
$state = 'use';
break;
case T_DOUBLE_ARROW:
$code .= $token[1];
if ($isShortClosure) {
$state = 'closure';
}
break;
case ':':
$code .= ':';
$state = 'return';
break;
case '{':
$code .= '{';
$state = 'closure';
$open++;
break;
default:
$code .= is_array($token) ? $token[1] : $token;
}
break;
case 'use':
switch ($token[0]) {
case T_VARIABLE:
$use[] = substr($token[1], 1);
$code .= $token[1];
break;
case '{':
$code .= '{';
$state = 'closure';
$open++;
break;
case ':':
$code .= ':';
$state = 'return';
break;
default:
$code .= is_array($token) ? $token[1] : $token;
break;
}
break;
case 'return':
switch ($token[0]) {
case T_WHITESPACE:
case T_COMMENT:
case T_DOC_COMMENT:
$code .= $token[1];
break;
case T_NS_SEPARATOR:
case T_STRING:
$id_start = $token[1];
$id_start_ci = strtolower($id_start);
$id_name = '';
$context = 'return_type';
$state = 'id_name';
$lastState = 'return';
break 2;
case T_NAME_QUALIFIED:
[$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]);
$context = 'return_type';
$state = 'id_name';
$lastState = 'return';
break 2;
case T_DOUBLE_ARROW:
$code .= $token[1];
if ($isShortClosure) {
$state = 'closure';
}
break;
case '{':
$code .= '{';
$state = 'closure';
$open++;
break;
default:
$code .= is_array($token) ? $token[1] : $token;
break;
}
break;
case 'closure':
switch ($token[0]) {
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
case '{':
$code .= is_array($token) ? $token[1] : $token;
$open++;
break;
case '}':
$code .= '}';
if (--$open === 0 && ! $isShortClosure) {
break 3;
} elseif ($inside_structure) {
$inside_structure = ! ($open === $inside_structure_mark);
}
break;
case '(':
case '[':
$code .= $token[0];
if ($isShortClosure) {
$open++;
}
break;
case ')':
case ']':
if ($isShortClosure) {
if ($open === 0) {
break 3;
}
$open--;
}
$code .= $token[0];
break;
case ',':
case ';':
if ($isShortClosure && $open === 0) {
break 3;
}
$code .= $token[0];
break;
case T_LINE:
$code .= $token[2] - $line + $lineAdd;
break;
case T_FILE:
$code .= $_file;
break;
case T_DIR:
$code .= $_dir;
break;
case T_NS_C:
$code .= $_namespace;
break;
case T_CLASS_C:
$code .= $inside_structure ? $token[1] : $_class;
break;
case T_FUNC_C:
$code .= $inside_structure ? $token[1] : $_function;
break;
case T_METHOD_C:
$code .= $inside_structure ? $token[1] : $_method;
break;
case T_COMMENT:
if (substr($token[1], 0, 8) === '#trackme') {
$timestamp = time();
$code .= '/**'.PHP_EOL;
$code .= '* Date : '.date(DATE_W3C, $timestamp).PHP_EOL;
$code .= '* Timestamp : '.$timestamp.PHP_EOL;
$code .= '* Line : '.($line + 1).PHP_EOL;
$code .= '* File : '.$_file.PHP_EOL.'*/'.PHP_EOL;
$lineAdd += 5;
} else {
$code .= $token[1];
}
break;
case T_VARIABLE:
if ($token[1] == '$this' && ! $inside_structure) {
$isUsingThisObject = true;
}
$code .= $token[1];
break;
case T_STATIC:
case T_NS_SEPARATOR:
case T_STRING:
$id_start = $token[1];
$id_start_ci = strtolower($id_start);
$id_name = '';
$context = 'root';
$state = 'id_name';
$lastState = 'closure';
break 2;
case T_NAME_QUALIFIED:
[$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]);
$context = 'root';
$state = 'id_name';
$lastState = 'closure';
break 2;
case T_NEW:
$code .= $token[1];
$context = 'new';
$state = 'id_start';
$lastState = 'closure';
break 2;
case T_USE:
$code .= $token[1];
$context = 'use';
$state = 'id_start';
$lastState = 'closure';
break;
case T_INSTANCEOF:
case T_INSTEADOF:
$code .= $token[1];
$context = 'instanceof';
$state = 'id_start';
$lastState = 'closure';
break;
case T_OBJECT_OPERATOR:
case T_NULLSAFE_OBJECT_OPERATOR:
case T_DOUBLE_COLON:
$code .= $token[1];
$lastState = 'closure';
$state = 'ignore_next';
break;
case T_FUNCTION:
$code .= $token[1];
$state = 'closure_args';
if (! $inside_structure) {
$inside_structure = true;
$inside_structure_mark = $open;
}
break;
case T_TRAIT_C:
if ($_trait === null) {
$startLine = $this->getStartLine();
$endLine = $this->getEndLine();
$structures = $this->getStructures();
$_trait = '';
foreach ($structures as &$struct) {
if ($struct['type'] === 'trait' &&
$struct['start'] <= $startLine &&
$struct['end'] >= $endLine
) {
$_trait = ($ns == '' ? '' : $ns.'\\').$struct['name'];
break;
}
}
$_trait = var_export($_trait, true);
}
$code .= $_trait;
break;
default:
$code .= is_array($token) ? $token[1] : $token;
}
break;
case 'ignore_next':
switch ($token[0]) {
case T_WHITESPACE:
case T_COMMENT:
case T_DOC_COMMENT:
$code .= $token[1];
break;
case T_CLASS:
case T_NEW:
case T_STATIC:
case T_VARIABLE:
case T_STRING:
case T_CLASS_C:
case T_FILE:
case T_DIR:
case T_METHOD_C:
case T_FUNC_C:
case T_FUNCTION:
case T_INSTANCEOF:
case T_LINE:
case T_NS_C:
case T_TRAIT_C:
case T_USE:
$code .= $token[1];
$state = $lastState;
break;
default:
$state = $lastState;
$i--;
}
break;
case 'id_start':
switch ($token[0]) {
case T_WHITESPACE:
case T_COMMENT:
case T_DOC_COMMENT:
$code .= $token[1];
break;
case T_NS_SEPARATOR:
case T_NAME_FULLY_QUALIFIED:
case T_STRING:
case T_STATIC:
$id_start = $token[1];
$id_start_ci = strtolower($id_start);
$id_name = '';
$state = 'id_name';
break 2;
case T_NAME_QUALIFIED:
[$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]);
$state = 'id_name';
break 2;
case T_VARIABLE:
$code .= $token[1];
$state = $lastState;
break;
case T_CLASS:
$code .= $token[1];
$state = 'anonymous';
break;
default:
$i--; //reprocess last
$state = 'id_name';
}
break;
case 'id_name':
switch ($token[0]) {
case $token[0] === ':' && $context !== 'instanceof':
if ($lastState === 'closure' && $context === 'root') {
$state = 'closure';
$code .= $id_start.$token;
}
break;
case T_NAME_QUALIFIED:
case T_NS_SEPARATOR:
case T_STRING:
case T_WHITESPACE:
case T_COMMENT:
case T_DOC_COMMENT:
$id_name .= $token[1];
break;
case '(':
if ($isShortClosure) {
$open++;
}
if ($context === 'new' || false !== strpos($id_name, '\\')) {
if ($id_start_ci === 'self' || $id_start_ci === 'static') {
if (! $inside_structure) {
$isUsingScope = true;
}
} elseif ($id_start !== '\\' && ! in_array($id_start_ci, $class_keywords)) {
if ($classes === null) {
$classes = $this->getClasses();
}
if (isset($classes[$id_start_ci])) {
$id_start = $classes[$id_start_ci];
}
if ($id_start[0] !== '\\') {
$id_start = $nsf.'\\'.$id_start;
}
}
} else {
if ($id_start !== '\\') {
if ($functions === null) {
$functions = $this->getFunctions();
}
if (isset($functions[$id_start_ci])) {
$id_start = $functions[$id_start_ci];
} elseif ($nsf !== '\\' && function_exists($nsf.'\\'.$id_start)) {
$id_start = $nsf.'\\'.$id_start;
// Cache it to functions array
$functions[$id_start_ci] = $id_start;
}
}
}
$code .= $id_start.$id_name.'(';
$state = $lastState;
break;
case T_VARIABLE:
case T_DOUBLE_COLON:
if ($id_start !== '\\') {
if ($id_start_ci === 'self' || $id_start_ci === 'parent') {
if (! $inside_structure) {
$isUsingScope = true;
}
} elseif ($id_start_ci === 'static') {
if (! $inside_structure) {
$isUsingScope = $token[0] === T_DOUBLE_COLON;
}
} elseif (! (\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))) {
if ($classes === null) {
$classes = $this->getClasses();
}
if (isset($classes[$id_start_ci])) {
$id_start = $classes[$id_start_ci];
}
if ($id_start[0] !== '\\') {
$id_start = $nsf.'\\'.$id_start;
}
}
}
$code .= $id_start.$id_name.$token[1];
$state = $token[0] === T_DOUBLE_COLON ? 'ignore_next' : $lastState;
break;
default:
if ($id_start !== '\\' && ! defined($id_start)) {
if ($constants === null) {
$constants = $this->getConstants();
}
if (isset($constants[$id_start])) {
$id_start = $constants[$id_start];
} elseif ($context === 'new') {
if (in_array($id_start_ci, $class_keywords)) {
if (! $inside_structure) {
$isUsingScope = true;
}
} else {
if ($classes === null) {
$classes = $this->getClasses();
}
if (isset($classes[$id_start_ci])) {
$id_start = $classes[$id_start_ci];
}
if ($id_start[0] !== '\\') {
$id_start = $nsf.'\\'.$id_start;
}
}
} elseif ($context === 'use' ||
$context === 'instanceof' ||
$context === 'args' ||
$context === 'return_type' ||
$context === 'extends' ||
$context === 'root'
) {
if (in_array($id_start_ci, $class_keywords)) {
if (! $inside_structure && ! $id_start_ci === 'static') {
$isUsingScope = true;
}
} elseif (! (\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))) {
if ($classes === null) {
$classes = $this->getClasses();
}
if (isset($classes[$id_start_ci])) {
$id_start = $classes[$id_start_ci];
}
if ($id_start[0] !== '\\') {
$id_start = $nsf.'\\'.$id_start;
}
}
}
}
$code .= $id_start.$id_name;
$state = $lastState;
$i--; //reprocess last token
}
break;
case 'anonymous':
switch ($token[0]) {
case T_NAME_QUALIFIED:
[$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]);
$state = 'id_name';
$lastState = 'anonymous';
break 2;
case T_NS_SEPARATOR:
case T_STRING:
$id_start = $token[1];
$id_start_ci = strtolower($id_start);
$id_name = '';
$state = 'id_name';
$context = 'extends';
$lastState = 'anonymous';
break;
case '{':
$state = 'closure';
if (! $inside_structure) {
$inside_structure = true;
$inside_structure_mark = $open;
}
$i--;
break;
default:
$code .= is_array($token) ? $token[1] : $token;
}
break;
}
}
if ($isShortClosure) {
$this->useVariables = $this->getStaticVariables();
} else {
$this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
}
$this->isShortClosure = $isShortClosure;
$this->isBindingRequired = $isUsingThisObject;
$this->isScopeRequired = $isUsingScope;
if (PHP_VERSION_ID >= 80100) {
$attributesCode = array_map(function ($attribute) {
$arguments = $attribute->getArguments();
$name = $attribute->getName();
$arguments = implode(', ', array_map(function ($argument, $key) {
$argument = sprintf("'%s'", str_replace("'", "\\'", $argument));
if (is_string($key)) {
$argument = sprintf('%s: %s', $key, $argument);
}
return $argument;
}, $arguments, array_keys($arguments)));
return "#[$name($arguments)]";
}, $this->getAttributes());
if (! empty($attributesCode)) {
$code = implode("\n", array_merge($attributesCode, [$code]));
}
}
$this->code = $code;
return $this->code;
}
/**
* Get PHP native built in types.
*
* @return array
*/
protected static function getBuiltinTypes()
{
// PHP 8.1
if (PHP_VERSION_ID >= 80100) {
return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object', 'mixed', 'false', 'null', 'never'];
}
// PHP 8
if (\PHP_MAJOR_VERSION === 8) {
return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object', 'mixed', 'false', 'null'];
}
// PHP 7
switch (\PHP_MINOR_VERSION) {
case 0:
return ['array', 'callable', 'string', 'int', 'bool', 'float'];
case 1:
return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void'];
default:
return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object'];
}
}
/**
* Gets the use variables by the closure.
*
* @return array
*/
public function getUseVariables()
{
if ($this->useVariables !== null) {
return $this->useVariables;
}
$tokens = $this->getTokens();
$use = [];
$state = 'start';
foreach ($tokens as &$token) {
$is_array = is_array($token);
switch ($state) {
case 'start':
if ($is_array && $token[0] === T_USE) {
$state = 'use';
}
break;
case 'use':
if ($is_array) {
if ($token[0] === T_VARIABLE) {
$use[] = substr($token[1], 1);
}
} elseif ($token == ')') {
break 2;
}
break;
}
}
$this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
return $this->useVariables;
}
/**
* Checks if binding is required.
*
* @return bool
*/
public function isBindingRequired()
{
if ($this->isBindingRequired === null) {
$this->getCode();
}
return $this->isBindingRequired;
}
/**
* Checks if access to the scope is required.
*
* @return bool
*/
public function isScopeRequired()
{
if ($this->isScopeRequired === null) {
$this->getCode();
}
return $this->isScopeRequired;
}
/**
* The the hash of the current file name.
*
* @return string
*/
protected function getHashedFileName()
{
if ($this->hashedName === null) {
$this->hashedName = sha1($this->getFileName());
}
return $this->hashedName;
}
/**
* Get the file tokens.
*
* @return array
*/
protected function getFileTokens()
{
$key = $this->getHashedFileName();
if (! isset(static::$files[$key])) {
static::$files[$key] = token_get_all(file_get_contents($this->getFileName()));
}
return static::$files[$key];
}
/**
* Get the tokens.
*
* @return array
*/
protected function getTokens()
{
if ($this->tokens === null) {
$tokens = $this->getFileTokens();
$startLine = $this->getStartLine();
$endLine = $this->getEndLine();
$results = [];
$start = false;
foreach ($tokens as &$token) {
if (! is_array($token)) {
if ($start) {
$results[] = $token;
}
continue;
}
$line = $token[2];
if ($line <= $endLine) {
if ($line >= $startLine) {
$start = true;
$results[] = $token;
}
continue;
}
break;
}
$this->tokens = $results;
}
return $this->tokens;
}
/**
* Get the classes.
*
* @return array
*/
protected function getClasses()
{
$key = $this->getHashedFileName();
if (! isset(static::$classes[$key])) {
$this->fetchItems();
}
return static::$classes[$key];
}
/**
* Get the functions.
*
* @return array
*/
protected function getFunctions()
{
$key = $this->getHashedFileName();
if (! isset(static::$functions[$key])) {
$this->fetchItems();
}
return static::$functions[$key];
}
/**
* Gets the constants.
*
* @return array
*/
protected function getConstants()
{
$key = $this->getHashedFileName();
if (! isset(static::$constants[$key])) {
$this->fetchItems();
}
return static::$constants[$key];
}
/**
* Get the structures.
*
* @return array
*/
protected function getStructures()
{
$key = $this->getHashedFileName();
if (! isset(static::$structures[$key])) {
$this->fetchItems();
}
return static::$structures[$key];
}
/**
* Fetch the items.
*
* @return void.
*/
protected function fetchItems()
{
$key = $this->getHashedFileName();
$classes = [];
$functions = [];
$constants = [];
$structures = [];
$tokens = $this->getFileTokens();
$open = 0;
$state = 'start';
$lastState = '';
$prefix = '';
$name = '';
$alias = '';
$isFunc = $isConst = false;
$startLine = $endLine = 0;
$structType = $structName = '';
$structIgnore = false;
foreach ($tokens as $token) {
switch ($state) {
case 'start':
switch ($token[0]) {
case T_CLASS:
case T_INTERFACE:
case T_TRAIT:
$state = 'before_structure';
$startLine = $token[2];
$structType = $token[0] == T_CLASS
? 'class'
: ($token[0] == T_INTERFACE ? 'interface' : 'trait');
break;
case T_USE:
$state = 'use';
$prefix = $name = $alias = '';
$isFunc = $isConst = false;
break;
case T_FUNCTION:
$state = 'structure';
$structIgnore = true;
break;
case T_NEW:
$state = 'new';
break;
case T_OBJECT_OPERATOR:
case T_DOUBLE_COLON:
$state = 'invoke';
break;
}
break;
case 'use':
switch ($token[0]) {
case T_FUNCTION:
$isFunc = true;
break;
case T_CONST:
$isConst = true;
break;
case T_NS_SEPARATOR:
$name .= $token[1];
break;
case T_STRING:
$name .= $token[1];
$alias = $token[1];
break;
case T_NAME_QUALIFIED:
$name .= $token[1];
$pieces = explode('\\', $token[1]);
$alias = end($pieces);
break;
case T_AS:
$lastState = 'use';
$state = 'alias';
break;
case '{':
$prefix = $name;
$name = $alias = '';
$state = 'use-group';
break;
case ',':
case ';':
if ($name === '' || $name[0] !== '\\') {
$name = '\\'.$name;
}
if ($alias !== '') {
if ($isFunc) {
$functions[strtolower($alias)] = $name;
} elseif ($isConst) {
$constants[$alias] = $name;
} else {
$classes[strtolower($alias)] = $name;
}
}
$name = $alias = '';
$state = $token === ';' ? 'start' : 'use';
break;
}
break;
case 'use-group':
switch ($token[0]) {
case T_NS_SEPARATOR:
$name .= $token[1];
break;
case T_NAME_QUALIFIED:
$name .= $token[1];
$pieces = explode('\\', $token[1]);
$alias = end($pieces);
break;
case T_STRING:
$name .= $token[1];
$alias = $token[1];
break;
case T_AS:
$lastState = 'use-group';
$state = 'alias';
break;
case ',':
case '}':
if ($prefix === '' || $prefix[0] !== '\\') {
$prefix = '\\'.$prefix;
}
if ($alias !== '') {
if ($isFunc) {
$functions[strtolower($alias)] = $prefix.$name;
} elseif ($isConst) {
$constants[$alias] = $prefix.$name;
} else {
$classes[strtolower($alias)] = $prefix.$name;
}
}
$name = $alias = '';
$state = $token === '}' ? 'use' : 'use-group';
break;
}
break;
case 'alias':
if ($token[0] === T_STRING) {
$alias = $token[1];
$state = $lastState;
}
break;
case 'new':
switch ($token[0]) {
case T_WHITESPACE:
case T_COMMENT:
case T_DOC_COMMENT:
break 2;
case T_CLASS:
$state = 'structure';
$structIgnore = true;
break;
default:
$state = 'start';
}
break;
case 'invoke':
switch ($token[0]) {
case T_WHITESPACE:
case T_COMMENT:
case T_DOC_COMMENT:
break 2;
default:
$state = 'start';
}
break;
case 'before_structure':
if ($token[0] == T_STRING) {
$structName = $token[1];
$state = 'structure';
}
break;
case 'structure':
switch ($token[0]) {
case '{':
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
$open++;
break;
case '}':
if (--$open == 0) {
if (! $structIgnore) {
$structures[] = [
'type' => $structType,
'name' => $structName,
'start' => $startLine,
'end' => $endLine,
];
}
$structIgnore = false;
$state = 'start';
}
break;
default:
if (is_array($token)) {
$endLine = $token[2];
}
}
break;
}
}
static::$classes[$key] = $classes;
static::$functions[$key] = $functions;
static::$constants[$key] = $constants;
static::$structures[$key] = $structures;
}
/**
* Returns the namespace associated to the closure.
*
* @return string
*/
protected function getClosureNamespaceName()
{
$ns = $this->getNamespaceName();
// First class callables...
if ($this->getName() !== '{closure}' && empty($ns) && ! is_null($this->getClosureScopeClass())) {
$ns = $this->getClosureScopeClass()->getNamespaceName();
}
return $ns;
}
/**
* Parse the given token.
*
* @param string $token
* @return array
*/
protected function parseNameQualified($token)
{
$pieces = explode('\\', $token);
$id_start = array_shift($pieces);
$id_start_ci = strtolower($id_start);
$id_name = '\\'.implode('\\', $pieces);
return [$id_start, $id_start_ci, $id_name];
}
}
At NFC Pay, we strive to provide a seamless and satisfactory experience with our services. This Refund Policy outlines the circumstances under which refunds may be issued for transactions made through our platform. Please read this policy carefully to understand your rights regarding refunds.
1. Eligibility for Refunds
Refunds may be considered under the following circumstances:
2. Non-Refundable Situations
Refunds will generally not be issued in the following situations:
3. Refund Process
To request a refund, please follow these steps:
4. Refund Exceptions
Certain transactions may be subject to specific terms and conditions, including non-refundable fees or charges. Please review the terms associated with each transaction carefully, as some fees may not be eligible for refunds.
5. Modifications to the Refund Policy
NFC Pay reserves the right to modify this Refund Policy at any time. Changes will be communicated through updates on our website and app, and the effective date will be updated accordingly. We encourage you to review this policy periodically to stay informed about our refund practices.
By using NFC Pay, you agree to this Refund Policy and understand the terms under which refunds may be issued. Our goal is to ensure a fair and transparent refund process, providing you with confidence and peace of mind when using our services.