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;
}
}
Related
Say I have to similar function :
public function auth(){
return $someResponse;
}
public function collect(){
return $someOtherResponse
}
Question : When one of the response get passed to another class, is there any way to check which function returned the response ?
In a purely object-oriented way, wanting to attach information to a value is akin to wrapping it into a container possessing context information, such as:
class ValueWithContext {
private $value;
private $context;
public function __construct($value, $context) {
$this->value = $value;
$this->context = $context;
}
public value() {
return $this->value;
}
public context() {
return $this->context;
}
}
You can use it like this:
function auth()
{
return new ValueWithContext($someresponse, "auth");
}
function collect()
{
return new ValueWithContext($someotherrpesonse, "collect");
}
This forces you to be explicit about the context attached to the value, which has the benefit of protecting you from accidental renamings of the functions themselves.
As per my comment, using arrays in the return will give you a viable solution to this.
It will allow a way to see what has been done;
function auth()
{
return (array("auth" => $someresponse));
}
function collect()
{
return (array("collect" => $someotherrpesonse));
}
class myClass
{
function doSomething($type)
{
if (function_exists($type))
{
$result = $type();
if (isset($result['auth']))
{
// Auth Used
$auth_result = $result['auth'];
}
else if (isset($result['collect']))
{
// Collect used
$collect_result = $result['collect'];
}
}
}
}
It can also give you a way to fail by having a return array("fail" => "fail reason")
As comments say also, you can just check based on function name;
class myClass
{
function doSomething($type)
{
switch ($type)
{
case "auth" :
{
$result = auth();
break;
}
case "collect" :
{
$result = collect();
break;
}
default :
{
// Some error occurred?
}
}
}
}
Either way works and is perfectly valid!
Letting the two user defined functions auth() & collect() call a common function which makes a call to debug_backtrace() function should do the trick.
function setBackTrace(){
$backTraceData = debug_backtrace();
$traceObject = array_reduce($backTraceData, function ($str, $val2) {
if (trim($str) === "") {
return $val2['function'];
}
return $str . " -> " . $val2['function'];
});
return $traceObject;
}
function getfunctionDo1(){
return setBackTrace();
}
function getfunctionDo2(){
return setBackTrace();
}
class DoSomething {
static function callfunctionTodo($type){
return (($type === 1) ? getfunctionDo1() : getfunctionDo2());
}
}
echo DoSomething::callfunctionTodo(1);
echo "<br/>";
echo DoSomething::callfunctionTodo(2);
/*Output
setBackTrace -> getfunctionDo1 -> callfunctionTodo
setBackTrace -> getfunctionDo2 -> callfunctionTodo
*/
The above function would output the which function returned the response
Hy,
i started learning PHP and i created a simple MVC Style Codebase.
The Script just generates a random number and displays this numer. I also write a function to display the number shown before but it does not work. The value is empty. Can you help me out, i have no clue whats wrong and there is no php error thrown.
view.php
<?php
class View
{
private $model;
private $view;
public function __construct()
{
$this->model = new Model();
}
public function output()
{
echo 'Current Entry: ';
echo $this->model->getData();
echo '<br />';
echo 'Update';
echo '<br />';
echo 'Last';
}
public function getModel()
{
return $this->model;
}
}
controller.php
<?php
class Controller
{
private $model;
private $view;
public function __construct($view)
{
$this->view = $view;
$this->model = $this->view->getModel();
}
public function get($request)
{
if (isset($request['action']))
{
if ($request['action'] === 'update')
{
for ($i = 0; $i<6; $i++)
{
$a .= mt_rand(0,9);
}
$this->model->setData($a);
}
elseif ($request['action'] === 'preview')
{
$this->model->setLast();
}
else
{
$this->model->setData('Wrong Action');
}
}
else
{
$this->model->setData('Bad Request');
}
}
}
model.php
<?php
class Model
{
private $data;
private $last;
public function __construct()
{
$this->data = 'Default';
}
public function setData($set)
{
if ( ! (($set == 'Wrong Action') && ($set == 'Bad Request')))
{
$this->last = $this->data;
}
$this->data = $set;
}
public function getData()
{
return $this->data;
}
public function setLast()
{
$this->data = $this->last;
}
public function getLast()
{
return $this->last;
}
}
index.php
<?php
require_once 'controller.php';
require_once 'view.php';
require_once 'model.php';
$view = new View();
$controller = new Controller($view);
if (isset($_GET) && !empty($_GET)) {
$controller->get($_GET);
}
$view->output();
Are there any other, bad mistakes in the Script?
Any input very welcome! :)
The problem with your code is that PHP does not preserve variable values between requests, therefore, when you set your $model->last value here:
$this->last = $this->data;
It gets reset on your next request.
You may want to store $last value in a session or a cookie instead. Something like:
$_SESSION['last'] = $this->data;
And then when you are instantiating your model you could initialize it with a value stored in a session if available:
index.php - add session_start() at the beginning
model.php:
public function __construct()
{
$this->data = isset($_SESSION['last']) ? $_SESSION['last'] : 'Default';
}
public function setData($set)
{
$this->data = $set;
if ( ! (($set == 'Wrong Action') && ($set == 'Bad Request')))
{
$_SESSION['last'] = $this->data;
}
}
controller.php
elseif ($request['action'] === 'preview')
{
//Remove this
//$this->model->setLast();
}
So I have a class I'm working on to manage PHP sessions, here's the class:
class SessionManagement {
public static function sessionStarted() {
if(session_id() == '') {
return false;
} else {
return true;
}
}
public static function sessionExists($session) {
if(sessionStarted() == false) {
session_start();
}
if(isset($_SESSION[$session])) {
return true;
} else {
return false;
}
}
public static function setSession($session, $value) {
if(sessionStarted() != true) {
session_start();
}
$_SESSION[$session] = $value;
if(sessionExists($session) == false) {
throw new Exception('Unable to Create Session');
}
}
public static function getSession($session) {
if(isset($_SESSION[$session])) {
return $_SESSION[$session];
} else {
throw new Exception('Session Does Not Exist');
}
}
}
Now trying this...
try {
SessionManagement::setSession('Foo', 'Bar');
echo SessionManagement::sessionStarted();
echo SessionManagement::getSession('Foo');
echo SessionManagement::sessionExists('Foo');
} catch(Exception $e) {
echo $e->getMessage();
}
...produces no output...I'm not sure where we're breaking here...any helpful eyes is greatly appreciated...
Unlike other OO languages, like C++, in your class PHP needs to know that the static methods called are from this object. For an instantiated class, that would be through $this, and in your case, static methods, this is done via self:
class SessionManagement {
public static function sessionStarted() {
if(session_id() == '') {
return false;
} else {
return true;
}
}
public static function sessionExists($session) {
if(self::sessionStarted() == false) {
session_start();
}
if(isset($_SESSION[$session])) {
return true;
} else {
return false;
}
}
public static function setSession($session, $value) {
if(self::sessionStarted() != true) {
session_start();
}
$_SESSION[$session] = $value;
if(self::sessionExists($session) == false) {
throw new Exception('Unable to Create Session');
}
}
public static function getSession($session) {
if(isset($_SESSION[$session])) {
return $_SESSION[$session];
} else {
throw new Exception('Session Does Not Exist');
}
}
}
Prepending self:: to all internal calls to the SessionManagement static methods should solve your problem.
I have problem in using ExceptionMatcher...My example spec:
class DescribeBall extends \PHPSpec\Context {
private $_ball = null;
function before() {
$this->_ball = $this->spec(new Ball);
}
function itShouldHaveStatusRolledOnRoll() {
$this->_ball->roll();
$this->_ball->getStatus()->should->be('Rolled');
}
function itShouldThrowException() {
$this->_ball->getException()->should->throwException('Exception','Error');
}
}
My example class
class Ball {
private $status = null;
public function roll() {
$this->status = 'Rolled';
}
public function getStatus() {
return $this->status;
}
public function getException() {
throw new Exception('Error');
}
}
Anyone used this matcher with success?
$this->_ball->getException()->should->throwException('Exception','Error');
Thanks to my colleagues:
"The last time I looked at it, it used closures (unless Marcello changed it meanwhile) it should still work like this":
function itShouldThrowException() {
$ball = $this->_ball;
$this->spec(function() use ($ball) {
$ball->getException();
})->should->throwException('Exception','Error');
}
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.