Skip to content

Instantly share code, notes, and snippets.

@DaveRandom
Last active December 14, 2015 21:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaveRandom/c23d394cb4f1af211b94 to your computer and use it in GitHub Desktop.
Save DaveRandom/c23d394cb4f1af211b94 to your computer and use it in GitHub Desktop.
Class for resolving standard PHP bitwise expressions using integers and/or E_* error constants to integers. Should work with any value allowed in php.ini
<?php
/**
* Class for resolving standard PHP bitwise expressions using integers
* and/or E_* error constants to integers. Should work with any value
* allowed in php.ini
*
* PHP version 5.3
*
* @author Chris Wright <info@daverandom.com>
* @copyright Copyright (c) 2013 Chris Wright
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 1.0
*/
/**
* Class for resolving standard PHP bitwise expressions using integers
* and/or E_* error constants to integers. Should work with any value
* allowed in php.ini
*
* @author Chris Wright <info@daverandom.com>
*/
class ErrorExpressionResolver
{
/**
* Recusively resolve nested expressions to a flat expression
*
* @param string $subject The subject expression to resolve
*
* @return int The value the expression represents
*
* @throws \InvalidArgumentException When the subject expression is invalid
*/
private function resolveGroups($subject)
{
return preg_replace_callback(
'/~?[ \t]*\((?:(?>[^()]+)|(?R))*\)/',
array($this, 'groupCallback'),
trim($subject)
);
}
/**
* Callback for resolving a single nested expression
*
* @param array $matches A PCRE match array for the matched expression
*
* @return string The value the expression represents, converted to a string
*
* @throws \InvalidArgumentException When the subject expression is invalid
*/
private function groupCallback($matches)
{
$isNegated = $matches[0][0] === '~';
if ($isNegated) {
$matches[0] = ltrim($matches[0], "\t\r\n\x00 ~");
}
if ($matches[0][0] === '(') {
$matches[0] = substr(rtrim($matches[0]), 1, -1);
}
$result = $this->resolve($matches[0]);
if ($isNegated) {
$result = ~((int) $result);
}
return (string) $result;
}
/**
* Resolve a single string operand to an integer
*
* @param string $operand The operand
*
* @return int The value the operand represents
*
* @throws \InvalidArgumentException When the operand cannot be resolved
*/
private function resolveOperand($operand)
{
$operand = trim($operand);
$isNegated = $operand[0] === '~';
if ($isNegated) {
$operand = ltrim($operand, "\t\r\n\x00 ~");
}
if (preg_match('/^-?\d+$/', $operand)) {
$operand = (int) $operand;
} else if (substr($operand, 0, 2) === 'E_' && defined($operand)) {
$operand = (int) constant($operand);
} else {
throw new \InvalidArgumentException('Invalid operand: ' . $operand);
}
if ($isNegated) {
$operand = ~$operand;
}
return $operand;
}
/**
* Perform a bitwise operation on two operands
*
* @param int $left The operand on the left side of the operation
* @param int $operator The operator used in the operation
* @param int $right The operand on the right side of the operation
*
* @return int The result of the operation
*
* @throws \InvalidArgumentException When the operator in invalid
*/
private function doOperation($left, $operator, $right)
{
switch (trim($operator)) {
case '|':
$result = $left | $right;
break;
case '&':
$result = $left & $right;
break;
case '^':
$result = $left ^ $right;
break;
default:
throw new \InvalidArgumentException('Unsupported operator: ' . $operator);
}
return $result;
}
/**
* Evaluate a flat expression represented as a string
*
* @param string $subject The subject expression to evaluate
*
* @return int The result of the operation
*
* @throws \InvalidArgumentException When any of the operands or operators is invalid
*/
private function evaluateFlatExpression($subject)
{
$result = 0;
$operator = '|';
$parts = preg_split(
'/[\t ]*(\S+)[\t ]*/',
$subject,
-1,
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
);
for ($i = 0; isset($parts[$i]); $i += 2) {
$operand = $this->resolveOperand($parts[$i]);
$result = $this->doOperation($result, $operator, $operand);
if (isset($parts[$i + 1])) {
$operator = $parts[$i + 1];
}
}
return $result;
}
/**
* Resolve an expression to an integer
*
* @param string $subject The subject expression to resolve
*
* @return int The value the expression represents
*
* @throws \InvalidArgumentException When the subject expression is invalid
*/
public function resolve($subject)
{
$flat = $this->resolveGroups($subject);
$result = $this->evaluateFlatExpression($flat);
return $result;
}
}
$subject = 'E_ALL & ~(E_NOTICE | E_WARNING | E_STRICT | E_CORE_WARNING | E_COMPILE_WARNING | E_USER_WARNING | E_DEPRECATED)';
$resolver = new ErrorExpressionResolver;
var_dump($resolver->resolve($subject));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment