3v4l.org

run code in 300+ PHP versions simultaneously
<?php /** * Parent class for custom model classes. * * This class was implemented like part of Observer pattern * https://en.wikipedia.org/wiki/Observer_pattern * http://php.net/manual/en/class.splsubject.php */ class Model implements SplSubject { /** * @var object List of attached observerer */ private $observers; /** * @var array Data for notify to observerer */ private $updates = []; /** * Class Constructor. */ public function __construct() { $this->observers = new SplObjectStorage(); } /** * Attach an Observer class to this Subject for updates * when occour a subject state change. * * @param SplObserver $observer */ public function attach(SplObserver $observer) { if ($observer instanceof View) { $this->observers->attach($observer); } } /** * Detach an Observer class from this Subject. * * @param SplObserver $observer */ public function detach(SplObserver $observer) { if ($observer instanceof View) { $this->observers->detach($observer); } } /** * Notify a state change of Subject to all registered Observeres. */ public function notify() { foreach ($this->observers as $value) { $value->update($this); } } /** * Set the data to notify to all registered Observeres. * * @param array $data */ public function set(array $data) { $this->updates = array_merge_recursive($this->updates, $data); } /** * Get the data to notify to all registered Observeres. * * @return array */ public function get(): array { return $this->updates; } } /** * Parent class for custom view classes. * * This class was implemented like part of Observer pattern * https://en.wikipedia.org/wiki/Observer_pattern * http://php.net/manual/en/class.splobserver.php */ class View implements SplObserver { /** * @var array Data for the dynamic view */ protected $data = []; /** * @var string Output data */ protected $output = ''; /** * @var Model Model for access data */ protected $model; /** * Class Constructor. * * @param Model $model */ public function __construct(Model $model) { $this->model = $model; } /** * Render a template. */ public function render(): string { return $this->output; } /** * Update Observer data. * * @param SplSubject $subject */ public function update(SplSubject $subject) { if ($subject instanceof Model) { $this->data = array_merge($this->data, $subject->get()); } } } /** * Parent class for custom controller classes. */ class Controller { /** * @var object The model object for current controller */ protected $model = null; /** * Class Constructor. * * @param object $model */ public function __construct(Model $model) { $this->model = $model; } } /** * Our Custom Model */ class CalculatorModel extends Model { /** * Class Constructor. */ public function __construct() { parent::__construct(); } /** * Mutiply business logic. * * @param array $numbers */ public function multiply(array $numbers) { $this->set([ 'result' => $this->operation('*', $numbers), 'operands' => $numbers, ]); } /** * Divide business logic. * * @param array $numbers */ public function divide(array $numbers) { $this->set([ 'result' => $this->operation('/', $numbers), 'operands' => $numbers ]); } /** * Subtraction business logic. * * @param array $numbers */ public function sub(array $numbers) { $this->set([ 'result' => $this->operation('-', $numbers), 'operands' => $numbers ]); } /** * Addition business logic. * * @param array $numbers */ public function add(array $numbers) { $this->set([ 'result' => $this->operation('+', $numbers), 'operands' => $numbers ]); } /** * Do math operation. * This is an example, division by 0 and other problematic things not checked. * * @param string $operator * @param array $numbers * @return int|float */ private function operation(string $operator, array $numbers) { $temp = null; foreach ($numbers as $n) { if ($temp === null) { $temp = $n; continue; } //example, division by 0 and other not checked switch ($operator) { case '*': $temp = $temp * $n; break; case '/': $temp = $temp / $n; break; case '-': $temp = $temp - $n; break; case '+': $temp = $temp + $n; break; } } return $temp; } } /** * Our Custom Model */ class CalculatorView extends View { /** * Class Constructor. * * @param CalculatorModel $model */ public function __construct(CalculatorModel $model) { parent::__construct($model); } /** * Build output for multiply. */ public function multiply() { $this->output = $this->generateOutput( 'Multiplication', $this->data['operands'], $this->data['result'] ); } /** * Build output for divide. */ public function divide() { $this->output = $this->generateOutput( 'Division', $this->data['operands'], $this->data['result'] ); } /** * Build output for add. */ public function add() { $this->output = $this->generateOutput( 'Addiction', $this->data['operands'], $this->data['result'] ); } /** * Build output for sub. */ public function sub() { $this->output = $this->generateOutput( 'Subtraction', $this->data['operands'], $this->data['result'] ); } /** * Generate the output. * * @param string $operation * @param array $operand * @param type $result * * @return string */ private function generateOutput(string $operation, array $operands, $result): string { $string = $operation . ': '; foreach ($operands as $value) { $string .= $value . ' * '; } $string = substr($string, 0, strlen($string) - 2); $string .= '= ' . $result; return $string; } } /** * Our Custom Controller */ class CalculatorController extends Controller { /** * Class Constructor. * * @param CalculatorModel $model */ public function __construct(CalculatorModel $model) { parent::__construct($model); } /** * Multiply input point. * * @param mixed $numbers */ public function multiply(... $numbers) { //check user input $this->checkOperands($numbers); $this->filter($numbers); //manipulate the model $this->model->multiply($numbers); } /** * Divide input point. * * @param mixed $numbers */ public function divide(... $numbers) { //check user input $this->checkOperands($numbers); $this->filter($numbers); //manipulate the model $this->model->divide($numbers); } /** * Sub input point. * * @param mixed $numbers */ public function sub(... $numbers) { //check user input $this->checkOperands($numbers); $this->filter($numbers); //manipulate the model $this->model->sub($numbers); } /** * Add input point. * * @param mixed $numbers */ public function add(... $numbers) { //check user input $this->checkOperands($numbers); $this->filter($numbers); //manipulate the model $this->model->add($numbers); } /** * Filter user supplied numbers. * * @param mixed $numbers * @throws InvalidArgumentException */ private function filter(&$numbers) { foreach ($numbers as $key => $number) { switch (gettype($number)) { case 'string': $number[$key] = strtonum($number); break; case 'integer': break; case 'double': break; default: throw new InvalidArgumentException('Not a number'); } } } /** * Convert a number given as string in the proper type (int or float). * https://secure.php.net/manual/en/language.types.type-juggling.php * * @param string $string Number as string ex '1.0', '0.9' etc * @return int|float */ private function strtonum(string $string) { if (fmod((float) $string, 1.0) === 0.0) { return (int) $string; } return (float) $string; } /** * Check minimum numbers required for operations. * * @param array $numbers * @throws ArgumentCountError */ private function checkOperands(array &$numbers) { if (count($numbers) < 2) { throw new ArgumentCountError('Two number needed for operation'); } } } //create components $model = new CalculatorModel(); $view = new CalculatorView($model); $controller = new CalculatorController($model); //attach the observer $model->attach($view); //call controller $controller->multiply(2, 3); //notify changes to observer $model->notify(); //call view, build output for selected operation $view->multiply(); //get the output echo $view->render();
Output for 8.1.0 - 8.1.28, 8.2.0 - 8.2.18, 8.3.0 - 8.3.4, 8.3.6
Deprecated: Return type of Model::attach(SplObserver $observer) should either be compatible with SplSubject::attach(SplObserver $observer): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 35 Deprecated: Return type of Model::detach(SplObserver $observer) should either be compatible with SplSubject::detach(SplObserver $observer): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 46 Deprecated: Return type of Model::notify() should either be compatible with SplSubject::notify(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 55 Deprecated: Return type of View::update(SplSubject $subject) should either be compatible with SplObserver::update(SplSubject $subject): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 126 Multiplication: 2 * 3 = 6
Output for 8.3.5
Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0 Deprecated: Return type of Model::attach(SplObserver $observer) should either be compatible with SplSubject::attach(SplObserver $observer): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 35 Deprecated: Return type of Model::detach(SplObserver $observer) should either be compatible with SplSubject::detach(SplObserver $observer): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 46 Deprecated: Return type of Model::notify() should either be compatible with SplSubject::notify(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 55 Deprecated: Return type of View::update(SplSubject $subject) should either be compatible with SplObserver::update(SplSubject $subject): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /in/6Rprp on line 126 Multiplication: 2 * 3 = 6
Output for 7.0.0 - 7.0.33, 7.1.0 - 7.1.33, 7.2.0 - 7.2.33, 7.3.0 - 7.3.33, 7.4.0 - 7.4.33, 8.0.0 - 8.0.30
Multiplication: 2 * 3 = 6
Output for 5.4.0 - 5.4.45, 5.5.0 - 5.5.38, 5.6.0 - 5.6.40
Parse error: syntax error, unexpected ':', expecting ';' or '{' in /in/6Rprp on line 75
Process exited with code 255.
Output for 5.1.0 - 5.1.6, 5.2.0 - 5.2.17, 5.3.0 - 5.3.29
Parse error: syntax error, unexpected '[' in /in/6Rprp on line 20
Process exited with code 255.
Output for 5.0.0 - 5.0.5
Parse error: parse error, unexpected '[' in /in/6Rprp on line 20
Process exited with code 255.
Output for 4.4.2 - 4.4.9
Parse error: syntax error, unexpected T_STRING, expecting '{' in /in/6Rprp on line 10
Process exited with code 255.
Output for 4.3.0 - 4.3.1, 4.3.5 - 4.3.11, 4.4.0 - 4.4.1
Parse error: parse error, unexpected T_STRING, expecting '{' in /in/6Rprp on line 10
Process exited with code 255.
Output for 4.3.2 - 4.3.4
Parse error: parse error, expecting `'{'' in /in/6Rprp on line 10
Process exited with code 255.

preferences:
306.28 ms | 401 KiB | 457 Q