I got these 2 functions that throw DEPRECATED notices on php 7.2
function session_unregister($var) {
global $session;
for (reset($session->vars); list($i)=each($session->vars);) {
if ($session->vars[$i] == trim($var)) {
unset($session->vars[$i]);
break;
}
}
}
function session_is_registered($var) {
global $session;
for (reset($session->vars); list($i)=each($session->vars);) {
if ($session->vars[$i] == trim($var)) {
return true;
}
}
return false;
}
I tried to modify them to be compatible with the new php, using foreach like so:
Changing:
for (reset($session->vars); list($i)=each($session->vars);) {
To:
foreach (array_keys($session->vars) as $i) {
But I am not 100% sure if that is the correct identical adaptation. Any comments?
You don't need array keys:
foreach ($session->vars as $key=>$sesion_var) {
if ($sesion_var == trim($var)) {
return true;
}
}
or to be more consistent to rest of your code:
foreach ($session->vars as $i=>$sesion_var) {
if ($session->vars[$i] == trim($var)) {
return true;
}
}
or maybe you should be using array_search() instead of that code block:
if(array_search(trim($var), $session->vars)) return true;
function session_unregister(string $var): void
{
global $session;
$var = trim($var);
if (isset($session->vars[$var])) {
unset($session->vars[$var]);
}
}
function session_is_registered(string $var): bool
{
global $session;
return isset($session->vars[trim($var)]);
}
However if you rewrite the app, I would recommend to get rid of that global too and pass the session as argument to each function.
I need opinion about using session with php. I'm useing session to store data. For instance - configuration:
First I'm loading data from config.ini to $_SESSION['config']
Then I'm using custom session class to get specific data from $_SESSION['config'][$key];
This is config function:
public static function load_config($process_sections = FALSE)
{
$array = parse_ini_file(CONFIG . DS . 'config.ini', $process_sections);
if ($array)
{
$_SESSION['configuration'] = $array;
return TRUE;
}
else
{
$_SESSION['configuration'] = array();
return FALSE;
}
}
function config($key, $default = NULL)
{
if (isset($_SESSION['configuration']))
{
if (isset($_SESSION['configuration'][$key]))
{
return $_SESSION['configuration'][$key];
}
else
{
return $default;
}
}
}
That same is with user object. I'm getting user data not from DB, but API, and storing it in $_SESSION['user']. When user object is constructs, I'm attributing all properties just from $_SESSION['user'][...], for instance:
public function __construct()
{
parent::__construct();
$this->initUser();
}
private function initUser()
{
if (Session::is('user'))
{
return $this->setUserData(Session::get('user'));
}
else
{
return FALSE;
}
}
private function setUserData($data)
{
foreach ($data as $key => $value)
{
if(property_exists($this->_name, $key))
{
$this->{$key} = $value;
}
}
return TRUE;
}
Properties are defined in class. I'm doing it just on time, when object is constructing. Is that right practice? It's working very good for me but I doubt if my method overolads server.
I have a system that was designed to do a kind of cascading - get the sequence of methods called upon success of the previous condition.
The example is the below code, which I presume it's not a best practice for doing this, so would be great if I could get some suggestions to refactor this, probably using a design pattern or a different than this system.
<?php
class Model
{
public function isOk()
{
return true;
}
}
class OtherClass
{
public function isOk()
{
return true;
}
}
class AnotherClass
{
public function verifies()
{
return true;
}
}
class Sequence
{
public function fire()
{
$model = new Model();
if($model->isOk()) {
$otherclass = new OtherClass();
if($otherclass->isOk()) {
$anotherclass = new AnotherClass();
if($anotherclass->verifies()) {
echo "We're done with the sequence.";
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
}
$sequence = new Sequence();
echo $sequence->fire();
?>
I would avoid deep nesting of if/else statements to enhance the readability. One way is to use early return:
class Test1
{
public function isOk()
{
echo 'Test1';
return true;
}
}
class Test2
{
public function isOk()
{
echo 'Test2';
return true;
}
}
class Sequence
{
public function fire()
{
$test1 = new Test1();
if (!$test1->isOk()) {
return false;
}
$test2 = new Test2();
if (!$test2->isOk()) {
return false;
}
echo "We're done with the sequence.";
return true;
}
}
If you need it more dynamically you could use call_user_func or call_user_func_array.
class Sequence
{
protected $sequence = array(
array('Test1', 'isOk'),
array('Test2', 'isOk'),
);
public function fire()
{
foreach ($this->sequence as $callback) {
if (!call_user_func(array(new $callback[0], $callback[1]))) {
return false;
}
}
echo "We're done with the sequence.";
return true;
}
}
I want to use PHP to calculate simple algebraic expressions like, 8*(5+1), entered via an <input> tag by a normal user (which means, normal notation: no syntax changes like Multiply(8, Add(5, 1))). Also, it has to show all steps, but that's not hard. The problem, right now, is calculating the value of the expressions.
Note: this is what I thought so far, which is quite inefficient but it's a provisory solution.
Just replace strings where possible: in our example, recognize the string 5+1 and replace it with 6. Then, loop again, replace (6) with 6, loop again, and replace 8*6 with 48.
The code for multiplying, for example, should look like this:
for ($a=1; $a < 1000; $a++) {
for ($b=1; $b < 1000; $b++) {
string_replace($a . '*' . $b, $a*$b, $string);
}
}
Depending on your needs, I would suggest looking into the Shunting Yard Algorithm. It's pretty easy to implement, and works quite well.
Here's an example I whipped up a while ago: GIST.
Here's the code copy/pasted into one block:
Expression Definitions:
class Parenthesis extends TerminalExpression {
protected $precidence = 7;
public function operate(Stack $stack) {
}
public function getPrecidence() {
return $this->precidence;
}
public function isNoOp() {
return true;
}
public function isParenthesis() {
return true;
}
public function isOpen() {
return $this->value == '(';
}
}
class Number extends TerminalExpression {
public function operate(Stack $stack) {
return $this->value;
}
}
abstract class Operator extends TerminalExpression {
protected $precidence = 0;
protected $leftAssoc = true;
public function getPrecidence() {
return $this->precidence;
}
public function isLeftAssoc() {
return $this->leftAssoc;
}
public function isOperator() {
return true;
}
}
class Addition extends Operator {
protected $precidence = 4;
public function operate(Stack $stack) {
return $stack->pop()->operate($stack) + $stack->pop()->operate($stack);
}
}
class Subtraction extends Operator {
protected $precidence = 4;
public function operate(Stack $stack) {
$left = $stack->pop()->operate($stack);
$right = $stack->pop()->operate($stack);
return $right - $left;
}
}
class Multiplication extends Operator {
protected $precidence = 5;
public function operate(Stack $stack) {
return $stack->pop()->operate($stack) * $stack->pop()->operate($stack);
}
}
class Division extends Operator {
protected $precidence = 5;
public function operate(Stack $stack) {
$left = $stack->pop()->operate($stack);
$right = $stack->pop()->operate($stack);
return $right / $left;
}
}
class Power extends Operator {
protected $precidence=6;
public function operate(Stack $stack) {
$left = $stack->pop()->operate($stack);
$right = $stack->pop()->operate($stack);
return pow($right, $left);
}
}
abstract class TerminalExpression {
protected $value = '';
public function __construct($value) {
$this->value = $value;
}
public static function factory($value) {
if (is_object($value) && $value instanceof TerminalExpression) {
return $value;
} elseif (is_numeric($value)) {
return new Number($value);
} elseif ($value == '+') {
return new Addition($value);
} elseif ($value == '-') {
return new Subtraction($value);
} elseif ($value == '*') {
return new Multiplication($value);
} elseif ($value == '/') {
return new Division($value);
} elseif ($value == '^') {
return new Power($value);
} elseif (in_array($value, array('(', ')'))) {
return new Parenthesis($value);
}
throw new Exception('Undefined Value ' . $value);
}
abstract public function operate(Stack $stack);
public function isOperator() {
return false;
}
public function isParenthesis() {
return false;
}
public function isNoOp() {
return false;
}
public function render() {
return $this->value;
}
}
The stack (really simple implementation):
class Stack {
protected $data = array();
public function push($element) {
$this->data[] = $element;
}
public function poke() {
return end($this->data);
}
public function pop() {
return array_pop($this->data);
}
}
And finally, the executor class:
class Math {
protected $variables = array();
public function evaluate($string) {
$stack = $this->parse($string);
return $this->run($stack);
}
public function parse($string) {
$tokens = $this->tokenize($string);
$output = new Stack();
$operators = new Stack();
foreach ($tokens as $token) {
$token = $this->extractVariables($token);
$expression = TerminalExpression::factory($token);
if ($expression->isOperator()) {
$this->parseOperator($expression, $output, $operators);
} elseif ($expression->isParenthesis()) {
$this->parseParenthesis($expression, $output, $operators);
} else {
$output->push($expression);
}
}
while (($op = $operators->pop())) {
if ($op->isParenthesis()) {
throw new RuntimeException('Mismatched Parenthesis');
}
$output->push($op);
}
return $output;
}
public function registerVariable($name, $value) {
$this->variables[$name] = $value;
}
public function run(Stack $stack) {
while (($operator = $stack->pop()) && $operator->isOperator()) {
$value = $operator->operate($stack);
if (!is_null($value)) {
$stack->push(TerminalExpression::factory($value));
}
}
return $operator ? $operator->render() : $this->render($stack);
}
protected function extractVariables($token) {
if ($token[0] == '$') {
$key = substr($token, 1);
return isset($this->variables[$key]) ? $this->variables[$key] : 0;
}
return $token;
}
protected function render(Stack $stack) {
$output = '';
while (($el = $stack->pop())) {
$output .= $el->render();
}
if ($output) {
return $output;
}
throw new RuntimeException('Could not render output');
}
protected function parseParenthesis(TerminalExpression $expression, Stack $output, Stack $operators) {
if ($expression->isOpen()) {
$operators->push($expression);
} else {
$clean = false;
while (($end = $operators->pop())) {
if ($end->isParenthesis()) {
$clean = true;
break;
} else {
$output->push($end);
}
}
if (!$clean) {
throw new RuntimeException('Mismatched Parenthesis');
}
}
}
protected function parseOperator(TerminalExpression $expression, Stack $output, Stack $operators) {
$end = $operators->poke();
if (!$end) {
$operators->push($expression);
} elseif ($end->isOperator()) {
do {
if ($expression->isLeftAssoc() && $expression->getPrecidence() <= $end->getPrecidence()) {
$output->push($operators->pop());
} elseif (!$expression->isLeftAssoc() && $expression->getPrecidence() < $end->getPrecidence()) {
$output->push($operators->pop());
} else {
break;
}
} while (($end = $operators->poke()) && $end->isOperator());
$operators->push($expression);
} else {
$operators->push($expression);
}
}
protected function tokenize($string) {
$parts = preg_split('((\d+|\+|-|\(|\)|\*|/)|\s+)', $string, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$parts = array_map('trim', $parts);
return $parts;
}
}
It works by first tokenizing the input (based on word boundary, and tokens). Then, it runs the Shunting Yard algorithm on it to convert the input into a RPN (Reverse Polish Notation) stack. Then, it's just a matter of executing the stack. Here's a quick example:
$math = new Math();
$answer = $math->evaluate('(2 + 3) * 4');
var_dump($answer);
// int(20)
$answer = $math->evaluate('1 + 2 * ((3 + 4) * 5 + 6)');
var_dump($answer);
// int(83)
$answer = $math->evaluate('(1 + 2) * (3 + 4) * (5 + 6)');
var_dump($answer);
// int(231)
$math->registerVariable('a', 4);
$answer = $math->evaluate('($a + 3) * 4');
var_dump($answer);
// int(28)
$math->registerVariable('a', 5);
$answer = $math->evaluate('($a + $a) * 4');
var_dump($answer);
// int(40)
Now, this example is significantly more complex than you may need. The reason is that it also handles grouping and operator precedence. But it's a decent example of a running algorithm that doesn't use EVAL and supports variables...
There is a Math Parser class called bcParserPHP that might be of interest.
Seems fairly simple to use and pretty powerful.
Example code from their site:
$parser = new MathParser();
$parser->setVariable('X', 5);
$parser->setVariable('Y', 2);
$parser->setExpression('COS(X)+SIN(Y)/2');
echo $parser->getValue();
Unfortunately, it's a commercial product; I don't know if that would stop you using it or not (guess it depends on the price and on your needs).
A non-commercial alternative might be this one: http://www.phpclasses.org/package/2695-PHP-Safely-evaluate-mathematical-expressions.html
Note that this class uses eval() internally, which I would avoid doing if possible.
Failing that, writing your own language parser would be the ideal solution, but not really sensible to do that in PHP.
I'd start by stripping the input of anything which shouldn't be in the expression (assuming you just want to allow add, subtract, multiply, divide, and no variables):
$expr = preg_replace('/[^0-9+*\/-]/', '', $expr);
and then, once I'm confident nothing dangerous remains in the user input, simply pass the itthrough eval() to evaluate the expression:
$result = eval("return $expr;");
No need to reinvent the wheel.
Edited to incorporate Kolink's corrections. Thanks!
So I'm trying to create a tree structure in PHP. I don't know if that's possible and I'm not all that great with PHP so this is difficult for me.
The code I have so far is (important stuff only, extra code has been cut out):
abstract class tree_node {
protected $_child_refs = array();
abstract public function add_child($arg);
public function count() {
return count($this->_child_refs);
}
public function get_deepest_children() {
if ($this->count() === 0) {
return $this;
} else {
foreach ($this->_child_refs as $child_ref) {
$deepest[] = $child_ref->get_deepest_children();
}
}
}
abstract public function __construct();
}
class data_node extends tree_node {
private $_data = "";
public function add_child($data) {
$new_child = new data_node($data);
$this->_child_refs[] = $new_child;
}
public function __construct($data) {
$this->_data = $data;
}
}
$foo = new data_node("foo");
$foo->add_child("bar");
var_dump($foo->get_deepest_children());
This code should return a data_node with "bar" as the data but instead I get NULL. What's wrong with "return $this"? Is that not the proper way to return an instance of a class itself?
Also, feel free to critique this code/tell me I'm doing this completely wrong. I want to keep tree functions separate from functions specific to the data stored in the tree, which is why I split it up into two classes, but if you think that's a bad idea tell me.
This:
public function get_deepest_children() {
if ($this->count() === 0) {
return $this;
} else {
foreach ($this->_child_refs as $child_ref) {
$deepest[] = $child_ref->get_deepest_children();
}
}
}
Should be something like this:
public function get_deepest_children() {
if ($this->count() === 0) {
return array($this);
}
$deepest = array();
foreach ($this->_child_refs as $child_ref) {
$deepest = array_merge($deepest,$child_ref->get_deepest_children());
}
return $deepest;
}
Your get_deepest_children isn't always returning the same type of value. In the base case it will return a node object, in the other cases it extends the $deepest list but doesn't return any value.
<?php
public function get_deepest_children() {
if ($this->count() === 0) {
return $this;
} else {
foreach ($this->_child_refs as $child_ref) {
$deepest[] = $child_ref->get_deepest_children();
}
}
}
I would modify it like this:
<?php
public function get_deepest_children() {
if ($this->count() === 0) {
return Array($this);
} else {
$deepest = Array();
foreach ($this->_child_refs as $child_ref) {
$deepest = array_merge($deepest, $child_ref->get_deepest_children());
}
return $deepest;
}
}
You should be able to see how it now always it returns an array of all of the deepest nodes (usually called leaf nodes) below/including the current one. This way we can array_merge the consecutive results to get the final ones.