I'm trying to create a priority queue using this code and I can't find where the problem is. Someone tell me where I went wrong.
<?php
class PriorityQueue implements Iterator , Countable
{
public function __construct() {
$flags = self::EXTR_DATA;
$items = array();
}
function compare ( mixed $priority1 , mixed $priority2 ){}
function count (){
return count($this->items);
}
function current (){
switch ($this->flags) {
case self::EXTR_BOTH:
$ret = array();
$ret['Patient'] = current($this->items);
$ret['Priority'] = $this->key();
break;
case self::EXTR_DATA:
$ret = current($this->items);
break;
case self::EXTR_PRIORITY:
$ret = $this->key();
break;
};
return $ret;
}
function extract (){
$ret = $this->current();
$this->next();
return $ret;
}
function insert ($name,$priority){
$patient = array();
return $patient[$name] = $priority;
}
function isEmpty ()
{
return empty($this->items);
}
function key (){
return substr(key($this->items), 0, 9);
}
function next (){
//array_shift($this->items);
return($this->items);
echo "<br />";
}
function recoverFromCorruption (){}
function rewind (){}
function setExtractFlags (int $flags ){
switch ($flags) {
case self::EXTR_BOTH:
case self::EXTR_DATA:
case self::EXTR_PRIORITY:
$this->flags = $flags;
break;
};
}
function top (){
return $this->current();
}
function valid () {
if (NULL !== key($this->items)) {
return TRUE;
}
return FALSE;
}// function valid
/**
* Extract the data.
*/
const EXTR_DATA = 1;
/**
* Extract the priority.
*/
const EXTR_PRIORITY = 2;
/**
* Extract an array containing both priority and data.
*/
const EXTR_BOTH = 3;
};
$objPQ = new splPriorityqueue();
$objPQ->insert('Richard',9);
$objPQ->insert('paul',1);
$objPQ->insert('Ken',8);
$objPQ->insert('peter',2);
$objPQ->insert('Rick',7);
$objPQ->insert('Dan',5);
echo "PATIENTS = ".$objPQ->count()."<br />";
//mode of extraction
$objPQ->setExtractFlags(splPriorityqueue::EXTR_BOTH);
//Go to TOP
$objPQ->top();
for($i=0,$j=$objPQ->count(); $i<$j; $i++){
//print_r($objPQ->current());
$patients = $objPQ->current();
foreach ($patients as $patient=>$value){
echo $patient."<br />".$value;
$objPQ->next();
echo "<br />";
}
}
?>
I'm now getting some weird result
data-patient Richard
priority-9
......
etc
I want to get results to be
Richard - 9
Ken - 8
Rick - 7
Dan - 5
Peter - 2
Paul - 1
Considering the priority given
The Standard PHP Library (SPL) implements the SplPriorityQueue class :
$pq = new SplPriorityQueue();
// The insert method inserts an element in the queue by shifting it up
$pq->insert('A', 3);
$pq->insert('B', 6);
$pq->insert('C', 1);
$pq->insert('D', 2);
// Count the elements
echo "count ->" . $pq->count() . PHP_EOL;
// Sets the mode of extraction (EXTR_DATA, EXTR_PRIORITY, EXTR_BOTH)
$pq->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
// Go at the node from the top of the queue
$pq->top();
// Iterate the queue (by priority) and display each element
while ($pq->valid()) {
print_r($pq->current());
echo PHP_EOL;
$pq->next();
}
Try this modified class:
class PriorityQueue implements Iterator, Countable {
/**
* Extract the data.
*/
const EXTR_DATA = 1;
/**
* Extract the priority.
*/
const EXTR_PRIORITY = 2;
/**
* Extract an array containing both priority and data.
*/
const EXTR_BOTH = 3;
private $flags;
private $items;
public function __construct() {
$this->flags = self::EXTR_DATA;
$this->items = array();
}
function compare($priority1, $priority2) {}
function count() {
return count($this->items);
}
function extract() {
$result = $this->current();
$this->next();
return $result;
}
function current() {
switch ($this->flags) {
case self::EXTR_BOTH:
$result = $this->key() . ' - ' . current($this->items);
break;
case self::EXTR_DATA:
$result = $this->key();
break;
case self::EXTR_PRIORITY:
$result = current($this->items);
break;
default:
$result = '';
}
return $result;
}
function key() {
return key($this->items);
}
function next() {
return next($this->items);
}
function insert($name, $priority) {
$this->items[$name] = $priority;
asort($this->items);
return $this;
}
function isEmpty() {
return empty($this->items);
}
function recoverFromCorruption() {}
function rewind() {}
function setExtractFlags($flags) {
switch ($flags) {
case self::EXTR_BOTH:
case self::EXTR_DATA:
case self::EXTR_PRIORITY:
$this->flags = $flags;
break;
};
}
function valid() {
return (null === key($this->items)) ? false : true;
}
}
Usage:
$patients = new PriorityQueue();
$patients->setExtractFlags(PriorityQueue::EXTR_BOTH);
$patients->insert('Richard', 9)
->insert('paul', 1)
->insert('Ken', 8)
->insert('peter', 2)
->insert('Rick', 7)
->insert('Dan', 5);
foreach($patients as $patient) {
echo $patient->current();
}
Related
I am using PHP 5 and I've heard of a new featured in the object-oriented approach, called 'method chaining'. What is it exactly? How do I implement it?
It's rather simple, really. You have a series of mutator methods that all return the original (or other) object. That way, you can keep calling methods on the returned object.
<?php
class fakeString
{
private $str;
function __construct()
{
$this->str = "";
}
function addA()
{
$this->str .= "a";
return $this;
}
function addB()
{
$this->str .= "b";
return $this;
}
function getStr()
{
return $this->str;
}
}
$a = new fakeString();
echo $a->addA()->addB()->getStr();
This outputs "ab"
Try it online!
Basically, you take an object:
$obj = new ObjectWithChainableMethods();
Call a method that effectively does a return $this; at the end:
$obj->doSomething();
Since it returns the same object, or rather, a reference to the same object, you can continue calling methods of the same class off the return value, like so:
$obj->doSomething()->doSomethingElse();
That's it, really. Two important things:
As you note, it's PHP 5 only. It won't work properly in PHP 4 because it returns objects by value and that means you're calling methods on different copies of an object, which would break your code.
Again, you need to return the object in your chainable methods:
public function doSomething() {
// Do stuff
return $this;
}
public function doSomethingElse() {
// Do more stuff
return $this;
}
Try this code:
<?php
class DBManager
{
private $selectables = array();
private $table;
private $whereClause;
private $limit;
public function select() {
$this->selectables = func_get_args();
return $this;
}
public function from($table) {
$this->table = $table;
return $this;
}
public function where($where) {
$this->whereClause = $where;
return $this;
}
public function limit($limit) {
$this->limit = $limit;
return $this;
}
public function result() {
$query[] = "SELECT";
// if the selectables array is empty, select all
if (empty($this->selectables)) {
$query[] = "*";
}
// else select according to selectables
else {
$query[] = join(', ', $this->selectables);
}
$query[] = "FROM";
$query[] = $this->table;
if (!empty($this->whereClause)) {
$query[] = "WHERE";
$query[] = $this->whereClause;
}
if (!empty($this->limit)) {
$query[] = "LIMIT";
$query[] = $this->limit;
}
return join(' ', $query);
}
}
// Now to use the class and see how METHOD CHAINING works
// let us instantiate the class DBManager
$testOne = new DBManager();
$testOne->select()->from('users');
echo $testOne->result();
// OR
echo $testOne->select()->from('users')->result();
// both displays: 'SELECT * FROM users'
$testTwo = new DBManager();
$testTwo->select()->from('posts')->where('id > 200')->limit(10);
echo $testTwo->result();
// this displays: 'SELECT * FROM posts WHERE id > 200 LIMIT 10'
$testThree = new DBManager();
$testThree->select(
'firstname',
'email',
'country',
'city'
)->from('users')->where('id = 2399');
echo $testThree->result();
// this will display:
// 'SELECT firstname, email, country, city FROM users WHERE id = 2399'
?>
Another Way for static method chaining :
class Maker
{
private static $result = null;
private static $delimiter = '.';
private static $data = [];
public static function words($words)
{
if( !empty($words) && count($words) )
{
foreach ($words as $w)
{
self::$data[] = $w;
}
}
return new static;
}
public static function concate($delimiter)
{
self::$delimiter = $delimiter;
foreach (self::$data as $d)
{
self::$result .= $d.$delimiter;
}
return new static;
}
public static function get()
{
return rtrim(self::$result, self::$delimiter);
}
}
Calling
echo Maker::words(['foo', 'bob', 'bar'])->concate('-')->get();
echo "<br />";
echo Maker::words(['foo', 'bob', 'bar'])->concate('>')->get();
Method chaining means that you can chain method calls:
$object->method1()->method2()->method3()
This means that method1() needs to return an object, and method2() is given the result of method1(). Method2() then passes the return value to method3().
Good article: http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html
There are 49 lines of code which allows you to chain methods over arrays like this:
$fruits = new Arr(array("lemon", "orange", "banana", "apple"));
$fruits->change_key_case(CASE_UPPER)->filter()->walk(function($value,$key) {
echo $key.': '.$value."\r\n";
});
See this article which shows you how to chain all the PHP's seventy array_ functions.
http://domexception.blogspot.fi/2013/08/php-magic-methods-and-arrayobject.html
A fluent interface allows you to chain method calls, which results in less typed characters when applying multiple operations on the same object.
class Bill {
public $dinner = 20;
public $desserts = 5;
public $bill;
public function dinner( $person ) {
$this->bill += $this->dinner * $person;
return $this;
}
public function dessert( $person ) {
$this->bill += $this->desserts * $person;
return $this;
}
}
$bill = new Bill();
echo $bill->dinner( 2 )->dessert( 3 )->bill;
I think this is the most relevant answer.
<?php
class Calculator
{
protected $result = 0;
public function sum($num)
{
$this->result += $num;
return $this;
}
public function sub($num)
{
$this->result -= $num;
return $this;
}
public function result()
{
return $this->result;
}
}
$calculator = new Calculator;
echo $calculator->sum(10)->sub(5)->sum(3)->result(); // 8
If you mean method chaining like in JavaScript (or some people keep in mind jQuery), why not just take a library that brings that dev. experience in PHP? For example Extras - https://dsheiko.github.io/extras/ This one extends PHP types with JavaScript and Underscore methods and provides chaining:
You can chain a particular type:
<?php
use \Dsheiko\Extras\Arrays;
// Chain of calls
$res = Arrays::chain([1, 2, 3])
->map(function($num){ return $num + 1; })
->filter(function($num){ return $num > 1; })
->reduce(function($carry, $num){ return $carry + $num; }, 0)
->value();
or
<?php
use \Dsheiko\Extras\Strings;
$res = Strings::from( " 12345 " )
->replace("/1/", "5")
->replace("/2/", "5")
->trim()
->substr(1, 3)
->get();
echo $res; // "534"
Alternatively you can go polymorphic:
<?php
use \Dsheiko\Extras\Any;
$res = Any::chain(new \ArrayObject([1,2,3]))
->toArray() // value is [1,2,3]
->map(function($num){ return [ "num" => $num ]; })
// value is [[ "num" => 1, ..]]
->reduce(function($carry, $arr){
$carry .= $arr["num"];
return $carry;
}, "") // value is "123"
->replace("/2/", "") // value is "13"
->then(function($value){
if (empty($value)) {
throw new \Exception("Empty value");
}
return $value;
})
->value();
echo $res; // "13"
Below is my model that is able to find by ID in the database. The with($data) method is my additional parameters for relationship so I return the $this which is the object itself. On my controller I am able to chain it.
class JobModel implements JobInterface{
protected $job;
public function __construct(Model $job){
$this->job = $job;
}
public function find($id){
return $this->job->find($id);
}
public function with($data=[]){
$this->job = $this->job->with($params);
return $this;
}
}
class JobController{
protected $job;
public function __construct(JobModel $job){
$this->job = $job;
}
public function index(){
// chaining must be in order
$this->job->with(['data'])->find(1);
}
}
I'm trying to get a reference to a function of an object.
I tried what you can see on the way 2 with no success.
Any advice on this?
<?
function echoc($data) {
echo "\n<pre>\n";
print_r($data);
echo "</pre>\n";
}
class Person {
const STATUS_SLEEPING = 0;
const STATUS_EATING = 1;
const STATUS_SEEING = 2;
const STATUS_WALKING = 3;
function __construct() {
$this->status = self::STATUS_SLEEPING;
}
function see() {
$this->status = self::STATUS_SEEING;
echo 'I\'m seeing now!';
}
function eat($what) {
$this->status = self::STATUS_EATING;
echo 'I\'m eating '.$what.' now!';
}
function walk() {
$this->status = self::STATUS_WALKING;
echo 'I\'m walking now!';
}
function getStatus() {
return $this->status;
}
function getStatusStr() {
switch ($this->status) {
case self::STATUS_SLEEPING: return 'STATUS_SLEEPING';
case self::STATUS_EATING: return 'STATUS_EATING';
case self::STATUS_SEEING: return 'STATUS_SEEING';
case self::STATUS_WALKING: return 'STATUS_WALKING';
}
}
};
$p = new Person();
echoc('Status: '.$p->getStatusStr());
$p->see();
echoc('Status: '.$p->getStatusStr());
$p->walk();
echoc('Status: '.$p->getStatusStr());
$way = 2;
switch ($way) {
case 1:
$p->eat('piza');
break;
case 2:
$method = 'eat'; // the name of the function is stored on a variable
// begin of code I'm looking for
$callback = $p->$method; // I tried this with no success
// end of code I'm looking for
call_user_func($callback, 'pizza'); // this line cannot be changed. I'm not allowed to
break;
}
echoc('Status: '.$p->getStatusStr());
?>
What you are looking for is:
$callback = [$p, 'eat']; // Callback: $p->eat()
call_user_func($callback, 'pizza'); // Run $p->eat('pizza');
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!
Is there a way to iterate over an object's keys implementing ArrayAccess and Iterator interfaces? Array access works as a charm but I can't use foreach on those objects which would help me a lot. Is it possible? I have such code so far:
<?php
class IteratorTest implements ArrayAccess, Iterator {
private $pointer = 0;
public function offsetExists($index) {
return isset($this->objects[$index]);
}
public function offsetGet($index) {
return $this->objects[$index];
}
public function offsetSet($index, $newValue) {
$this->objects[$index] = $newValue;
}
public function offsetUnset($index) {
unset($this->objects[$index]);
}
public function key() {
return $this->pointer;
}
public function current() {
return $this->objects[$this -> pointer];
}
public function next() {
$this->pointer++;
}
public function rewind() {
$this->pointer = 0;
}
public function seek($position) {
$this->pointer = $position;
}
public function valid() {
return isset($this->objects[$this -> pointer]);
}
}
$it = new IteratorTest();
$it['one'] = 1;
$it['two'] = 2;
foreach ($it as $k => $v) {
echo "$k: $v\n";
}
// expected result:
// one: 1
// two: 2
Thanks for any help and hints.
I use this to implement iterator. Maybe you can adapt to your code ;)
class ModelList implements Iterator{
public $list;
private $index = 0;
public $nb;
public $nbTotal;
/**
* list navigation
*/
public function rewind(){$this->index = 0;}
public function current(){$k = array_keys($this->list);$var = $this->list[$k[$this->index]];return $var;}
public function key(){$k = array_keys($this->list);$var = $k[$this->index];return $var;}
public function next(){$k = array_keys($this->list);if (isset($k[++$this->index])) {$var = $this->list[$k[$this->index]];return $var;} else {return false;}}
public function valid(){$k = array_keys($this->list);$var = isset($k[$this->index]);return $var;}
/**
*
* Constructor
*/
public function __construct() {
$this->list = array();
$this->nb = 0;
$this->nbTotal = 0;
return $this;
}
}
while ($it->valid()) {
echo $it->key().' '.$it->current();
$it->next();
}
Would be my approach, however, this function looks iffy:
public function next() {
$this->pointer++;
}
Incrementing 'one' isn't likely to give you 'two'. Try the code in the answers to this question to get the next array key:
$keys = array_keys($this->objects);
$position = array_search($this->key(), $keys);
if (isset($keys[$position + 1])) {
$this->pointer = $keys[$position + 1];
} else {
$this->pointer = false;
}
I tried to learn Facade pattern, but got stuck with a problem. PHP doesnt output $totalprice from PrintPrice method. Also, here is additional information: var_dump after initializing and object gives me:
private 'SkiRent' =>
object(SkiRent)[2]
private 'SkiResortTicketSystem' =>
object(SkiResortTicketSystem)[3]
private 'HotelBookingSystem' =>
object(HotelBookingSystem)[4]
private 'totalprice' => null
but var_dump after calling HaveGoodRest gives me blank page.
I wonder if you could help me, here is the code:
<?php
class SkiRent
{
public function RentBoots($feetSize, $skierLevel)
{
return 20*$skierLevel*100/$feetSize;
}
public function RentSki($weight, $skierLevel)
{
return 40*$skierLevel*100/$weight;
}
public function RentPole($height)
{
return 5*$height/100;
}
};
class SkiResortTicketSystem
{
public function BuyOneDayTicket()
{
return 115;
}
public function BuyHalfDayTicket()
{
return 60;
}
};
class HotelBookingSystem
{
public function BookRoom($roomQuality)
{
switch ($roomQuality)
{
case 3:
return 250;
case 4:
return 500;
case 5:
return 900;
default:
throw new ArgumentException("roomQuality should be in [3;5]");
}
}
};
class SkiResortFacade
{
private $SkiRent;
private $SkiResortTicketSystem;
private $HotelBookingSystem;
private $totalprice;
function __construct()
{
$this->SkiRent = new SkiRent();
$this->SkiResortTicketSystem = new SkiResortTicketSystem();
$this->HotelBookingSystem = new HotelBookingSystem();
}
function HaveGoodRest($height, $weight, $feetSize, $skierLevel, $roomQuality)
{
$skiPrice = $SkiRent->RentSki($weight, $skierLevel);
var_dump($skiPrice);
$skiBootsPrice = $SkiRent->RentBoots($feetSize,$skierLevel);
$polePrice = $SkiRent->RentPole($height);
$oneDayTicketPr = $SkiResortTicketSystem->BuyOneDayTicket();
$hotelPrice = $HotelBookingSystem->BookRoom($roomQuality);
$this->totalprice = $skiPrice + $skiBootsPrice + $polePrice + $oneDayTicketPr + $hotelPrice;
}
public function HaveRestWithOwnSkis($roomQuality)
{
$oneDayTicketPr = $SkiResortTicketSystem->BuyOneDayTicket();
$hotelPrice = $HotelBookingSystem->BookRoom($roomQuality);
return $oneDayTicketPr + $hotelPrice;
}
public function PrintPrice()
{
echo $this->totalprice;
}
};
$rest = new SkiResortFacade();
var_dump($rest);
$height = 181.5;
$weight = 70.1;
$feetSize = 45.2;
$skierLevel = 1.2;
$roomQuality = 3;
$rest->HaveGoodRest($height, $weight, $feetSize, $skierLevel, $roomQuality);
var_dump($rest);
$rest->PrintPrice();
?>
Fatal error: Call to a member function RentSki() on a non-object on line 65
$skiPrice = $SkiRent->RentSki($weight, $skierLevel); // Line 65
$SkiRent isn't an object because you haven't instantiated it or passed it in as an argument in the HaveGoodRest() method
You have multiple calls to non-objects being acted on in your code.
You have defined $this->SkiRent attribute in SkiResortFacade class:
$this->SkiRent = new SkiRent();
but you have referenced it without $this:
$skiPrice = $SkiRent->RentSki($weight, $skierLevel);
it should be:
$skiPrice = $this->SkiRent->RentSki($weight, $skierLevel);
This is a working code, so you can compare woth your version:
<?php
class SkiRent
{
public function RentBoots($feetSize, $skierLevel)
{
return 20*$skierLevel*100/$feetSize;
}
public function RentSki($weight, $skierLevel)
{
return 40*$skierLevel*100/$weight;
}
public function RentPole($height)
{
return 5*$height/100;
}
};
class SkiResortTicketSystem
{
public function BuyOneDayTicket()
{
return 115;
}
public function BuyHalfDayTicket()
{
return 60;
}
};
class HotelBookingSystem
{
public function BookRoom($roomQuality)
{
switch ($roomQuality)
{
case 3:
return 250;
case 4:
return 500;
case 5:
return 900;
default:
throw new ArgumentException("roomQuality should be in [3;5]");
}
}
};
class SkiResortFacade
{
private $SkiRent;
private $SkiResortTicketSystem;
private $HotelBookingSystem;
private $totalprice;
function __construct()
{
$this->SkiRent = new SkiRent();
$this->SkiResortTicketSystem = new SkiResortTicketSystem();
$this->HotelBookingSystem = new HotelBookingSystem();
}
function HaveGoodRest($height, $weight, $feetSize, $skierLevel, $roomQuality)
{
$skiPrice = $this->SkiRent->RentSki($weight, $skierLevel);
var_dump($skiPrice);
$skiBootsPrice = $this->SkiRent->RentBoots($feetSize,$skierLevel);
$polePrice = $this->SkiRent->RentPole($height);
$oneDayTicketPr = $this->SkiResortTicketSystem->BuyOneDayTicket();
$hotelPrice = $this->HotelBookingSystem->BookRoom($roomQuality);
$this->totalprice = $skiPrice + $skiBootsPrice + $polePrice + $oneDayTicketPr + $hotelPrice;
}
public function HaveRestWithOwnSkis($roomQuality)
{
$oneDayTicketPr = $SkiResortTicketSystem->BuyOneDayTicket();
$hotelPrice = $HotelBookingSystem->BookRoom($roomQuality);
return $oneDayTicketPr + $hotelPrice;
}
public function PrintPrice()
{
echo $this->totalprice;
}
};
$rest = new SkiResortFacade();
$height = 181.5;
$weight = 70.1;
$feetSize = 45.2;
$skierLevel = 1.2;
$roomQuality = 3;
$rest->HaveGoodRest($height, $weight, $feetSize, $skierLevel, $roomQuality);
$rest->PrintPrice();