PHP - Method Chaining - Best Approach? - php

I need a way to have breadcrumbs that are not generated from the URL.
My thought process is to have a class, "Breadcrumbs" which will work as:
$breadcrumbs = new BreadCrumb('<<')->crumb('www.google.com', 'Google')->crumb('www.youtube.com', 'Youtube');
Which will generate an array, and with each method chain it will push back into the array, resulting in one array which I can then turn into a URL structure.
I have tried the following:
class Breadcrumbs {
public $del;
public $breadcrumbs;
public function __construct($del)
{
$this->breadcrumbs = array();
$this->del = $del;
}
public function crumb($uri, $name)
{
$this->breadcrumbs[] = array($uri => $name);
}
}
This, however, does not provide an accurate result, and get 'unexpected '->'' when trying to do the structure as I plan to do.
Any ideas to where I am going wrong?

To do method chaining, you need to return an object from your method call. Typically you'll want:
return $this; // in function crumb()
The correct syntax to instantiate an object and immediately call a method on it is:
(new BreadCrumb('<<'))->crumb(..)
Without the extra parentheses it's ambiguous to PHP what you want to do.
Aside: you don't need to initialise your array inside the constructor, you can do that while declaring the array:
public $breadcrumbs = array();
Aside: this is pretty inefficient:
.. = array($uri => $name)
You'll have a hard time getting that $uri key back out of the array, which makes your code unnecessarily complicated. You should simply do:
$this->breadcrumbs[$uri] = $name;
Alternatively, if your URIs aren't guaranteed to be unique, use a structure that's easier to work with later:
$this->breadcrumbs[] = array($uri, $name); // $arr[0] is the URI, $arr[1] the name

You need to return $this from crumb to be able to do method chaining.
public function crumb($uri, $name)
{
$this->breadcrumbs[] = array($uri => $name);
return $this;
}

Related

Problems using extract() function to insert arguments into class method directly

Is this possible Im trying to do this with the extract() function since Im unable to get the method arguments in advance
class Test{
public function getData($id){
//use $id here
}
}
$class = 'Test'; //this is determined dymanically
$method = 'getData'; //this is also determined dynamically
$arguments = ['id'=>'1234'];
$test = new $class();
$test->{$method}(extract($arguments));
//this generates a warning Missing argument 1 for Test::getData(),
called
How can this be implemented?
EDIT
It appears I've simplified it too much, the code is intended to be the main deployment mechanism in a mini-framework Im developing so the method - getData is determined dynamically and therefore I cant know the arguments for each method in advance.
Thanks
extract is for assigning to variables. For each element of the associative array, it will assign to a variable in the current scope with that name. It returns the number of variables it assigned, not the value of any of the variables.
There's no reason to use extract in your case, just get the element of the array that you want:
$test->getData($arguments['id']);
I'm not sure why you're getting an error about a missing argument. It should pass 1 as the $id argument, since there's one element in the array.
If you don't know which elements the function needs, a better design would be to pass the whole $arguments array to the function, and let it use the parts it wants.
public function getData($args) {
$id = $args['id'];
// Use $id
}
...
$test->getData($arguments);
Just extract your array and pass $id
<?php
class Test{
public function getData($id){
echo $id;
}
}
$arguments = array('id'=>'1234');
extract($arguments);
$test = new Test();
$test->getData($id);
or
$arguments = array('id'=>'1234');
extract($arguments);
$test = new Test();
foreach($arguments as $key=>$value){
$test->getData($$key);
}
Ive found the solution using ReflectionMethod
$reflection = new \ReflectionMethod('Test', 'getData');
$pass = [];
foreach($reflection->getParameters() as $param){
//parse the method to get its arguments and filter through the sent arguments array
if(isset($args[$param->getName()])){
//check if the arguments exists and select them
$pass[] = $args[$param->getName()];
}
else{
$pass[] = $param->getDefaultValue();
}
}
//execute the resolved parameters
return $reflection->invokeArgs(new Test, $pass);

how to call Joomla component method by variable?

$model = JModelLegacy::getInstance('NameOfModel', $prefix = 'my_componentModel', $config = array());
Normally, I would call the models method like this:
$this->items = $model->my_method();
In my case, I need to call the method by variable, because it is dynamic:
$this->items = $model->$variable; ...but this won't work.
$this->items = $model->{$variable}; ...this also won't work.
Does anybody know how to solve this?
If your code sample is right, the most likely answer is that you mean to call a method whose name is $variable, but you've forgotten the () at the end. i.e. your calling line should read:
$this->items = $model->$variable();
If, that's not a typo and you did mean to call a property of the class, it's likely that the contents of $variable don't have a matching property/method in $model.
When using variable properpty or method names you will better off wrapping your calls in a simple check the existence of the property or method, to catch the problem before hand. e.g.
// Check $model has a method $variable
if (method_exists($model, $variable)
{
$this->items = $model->$variable();
}
else
{
... raise a error/warning message here ...
}
// Check $model has a property $variable
if (property_exists($model, $variable)
{
$this->items = $model->$variable;
}
else
{
... raise a error/warning message here ...
}

Show all public attributes (name and value) of an object

This thread didn't helped me.
If I use
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
I get
attrib1_name : attrib2_name : attrib3_name
There are no values. Also a private attribute is shown, which I don't want.
If I use
echo "<pre>";
print_r(get_object_vars($this));
echo "</pre>";
I get
Array
(
[atrrib1_name] => attrib1_value
[attrib2_name] => attrib2_value
)
Here again I have a private attribute and all sub attributes. But this time I have the values. How can I constrain this to one level?
Isn't there a possibility to show all public attributes with their values of an object?
You are seeing non-public properties because get_class_vars works according to current scope. Since you are using $this your code is inside the class, so the non-public properties are accessible from the current scope. The same goes for get_object_vars which is probably a better choice here.
In any case, a good solution would be to move the code that retrieves the property values out of the class.
If you do not want to create a free function for that (why? seriously, reconsider!), you can use a trick that involves an anonymous function:
$getter = function($obj) { return get_object_vars($obj); };
$class_vars = $getter($this);
See it in action.
Update: Since you are in PHP < 5.3.0, you can use this equivalent code:
$getter = create_function('$obj', 'return get_object_vars($obj);');
$class_vars = $getter($this);
You can do this easily with php Reflection api
Extending Mr.Coder's answer, here is a snippet to fetch the public attributes of the object (name and value) as an array
public function getPublicProperties()
{
$results = [];
$reflectionObject = (new ReflectionObject($this));
$properties = $reflectionObject->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$results[$property->getName()] = $property->getValue($this);
}
return $results;
}
Use get_object_vars.
see: http://dk.php.net/manual/en/function.get-object-vars.php
I Fully recognize what you are trying to achieve so why not have something external like this to help out... (pasted from PHPFiddle)
<?php
final class utils {
public static function getProperties(& $what) {
return get_object_vars($what);
}
}
class ball {
var $name;
private $x, $y;
function __construct($name,$x,$y) {
}
function publicPropsToArray() {
return utils::getProperties($this);
}
function allPropsToArray() {
return get_object_vars($this);
}
}
$ball1 = new ball('henry',5,6);
//$ball2 = new ball('henry',3,4);
echo "<pre>";
print_r($ball1->publicPropsToArray());
echo "\r\n\r\n";
print_r($ball1->allPropsToArray());
echo "\r\n\r\n";
?>
This way I can both access all properties of the object or for something such as a database access layer or similarly for a function that send "safe" data to a view or another un-privileged model I can send just the public properties, but have the behaviour defined within the object.
Sure this leads to coupling with a utility class, but to be fair not all couplings are bad, some are nesecarry to achieve an end goal, dont get bogged down by these things

PHP object method doesn't behave as I expect

I can't quite understand why the output of this code is '1'.
My guess is that php is not behaving like most other OO languages that I'm used to, in that the arrays that php uses must not be objects. Changing the array that is returned by the class does not change the array within the class. How would I get the class to return an array which I can edit (and has the same address as the one within the class)?
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function getArr()
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = $t->getArr();
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
EDIT: I expected the output to be 0
You have to return the array by reference. That way, php returns a reference to the array, in stead of a copy.
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function & getArr() //Returning by reference here
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = &$t->getArr(); //Reference binding here
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
This returns 0.
From the returning references subpage:
Unlike parameter passing, here you have to use & in both places - to
indicate that you want to return by reference, not a copy, and to
indicate that reference binding, rather than usual assignment, should
be done
Note that although this gets the job done, question is if it is a good practice. By changing class members outside of the class itself, it can become very difficult to track the application.
Because array are passed by "copy on write" by default, getArr() should return by reference:
public function &getArr()
{
return $this->arr;
}
[snip]
$tobj_arr = &$t->getArr();
For arrays that are object, use ArrayObject. Extending ArrayObject is probably better in your case.
When you unset($tobj_arr[0]); you are passing the return value of the function call, and not the actual property of the object.
When you call the function again, you get a fresh copy of the object's property which has yet to be modified since you added 5 to it.
Since the property itself is public, try changing:
unset($tobj_arr[0]);
To: unset($t->arr[0]);
And see if that gives you the result you are looking for.
You are getting "1" because you are asking PHP how many elements are in the array by using count. Remove count and use print_r($tobj_arr_fresh)

How can I protect part of an array in php from being modified?

I have an array in php like this:
$myArray = array('name'=>'juank', 'age'=>26, 'config'=>array('usertype'=>'admin','etc'=>'bla bla'));
I need this array to be accesible along the script to allow changes in any field EXCEPT in the "config" field. Is there a way to protect an array or part of an array from being modified as if it where declared private inside a class? I tried defining it as a constant but it's value changes during script execution. Implementing it as a class would mean I'd have to rebuild the complete application from scratch :S
thanks!
I do not think you can do this using "pure" "real" arrays.
One way to get to this might be using some class that implements ArrayInterface ; you code would look like it's using arrays... But it would actually be using objects, with accessor methods that could forbid write-access to some data, I guess...
It would have you change a couple of things (creating a class, instanciating it) ; but not everything : access would still be using an array-like syntax.
Something like this might do the trick (adapted from the manual) :
class obj implements arrayaccess {
private $container = array();
public function __construct() {
$this->container = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
}
public function offsetSet($offset, $value) {
if ($offset == 'one') {
throw new Exception('not allowed : ' . $offset);
}
$this->container[$offset] = $value;
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$a = new obj();
$a['two'] = 'glop'; // OK
var_dump($a['two']); // string 'glop' (length=4)
$a['one'] = 'boum'; // Exception: not allowed : one
You have to instanciate an object with new, which is not very array-like... But, after that, you can use it as an array.
And when trying to write to an "locked" property, you can throw an Exception, or something like that -- btw, declaring a new Exception class, like ForbiddenWriteException, would be better : would allow to catch those specifically :-)
You could make the array private and create a method to modify its contents that will check if someone doesn't try to overwrite the config key.
<?php
class MyClass {
private static $myArray = array(
'config' => array(...),
'name' => ...,
...
);
public static function setMyArray($key, $value) {
if ($key != 'config') {
$this::myArray[$key] = $value;
}
}
}
Then when you want to modify the array you call:
MyClass::setMyArray('foo', 'bar'); // this will work
MyClass::setMyArray('config', 'bar'); // this will be ignored
No, unfortunately there isn't a way to do what you're describing. Variables don't have any concept of public or private unless they are encapsulated within an object.
Your best solution is unfortunately to re-work the configuration into an object format. You might be able to use a small object inside your array that contains the private settings, which might allow you to only have to update a few places in your code, depending where that portion of the array is used.

Categories