I am trying to understand object oriented PHP programming and wrote a small class to learn. I am having trouble understanding why its not working the way I intend. I have two variables inside the class method hello() $result and $test. I am trying to access the data that is stored in those two variables and print it to the screen. I know I can just call an echo inside the method but I am trying to get it to echo outside of it.
What I get printed to the screen is 88 it does not print out the second variable $test. I am trying to understand why thats happening. My lack of understanding probably shows in the code.
<?php
class simpleClass{
public function hello($result,$test) {
$result = 4+4;
$test = 10+5;
return $result;
return $test;
}
}
$a = new simpleClass;
echo $a->hello();
echo $a->hello($result, $test);
?>
you can return a list or array
public function hello($result,$test) {
$result = 4+4;
$test = 10+5;
return array($result, $test);
}
Use parameter referencing :
class simpleClass{
public function hello(&$result, &$test) {
$result = 4+4;
$test = 10+5;
}
}
$a = new simpleClass;
$result=''; $test='';
$a->hello($result, $test);
echo $result;
echo '<br>';
echo $test;
8
15
To clarify, when you add & to a function param, the value of that param - if you change or manipulate it inside the function - is handled back to your original variable passed. So you dont even have to return a result, and lets say pack it into an array or stdObject and unpack it afterwards. But you can still return something from the function, eg
$ok = $a->hello($result, $test);
as a flag to indicate if the calculation went right, for instance.
You cannot have multiple return statements in the same function because of the way return works. When a return statement is encountered the function stops executing there and then, passing back to the caller. The rest of the function never runs.
The complicated answer is to use a model.
class simpleResultTestModel {
public $result;
public $test;
public function __construct($result,$test) {
$this->result = $result;
$this->test = $test;
}
}
class simpleClass {
public function hello($result=4, $test=10) {
$result = $result+4;
$test = $test+5;
return new simpleResultTestModel($result, $test);
}
}
This way, you know simpleClass->hello() will always return an instance of simpleResultTestModel.
Also, I updated your hello method definition. You have two parameters, but don't actually apply them; I took the liberty of setting default values and then used them in the computation.
Usage:
$a = new simpleClass();
$first = $a->hello();
echo $first->result;
echo $first->test;
$second = $a->hello($first->result,$first->test);
echo $second->result;
echo $second->test;
I would try to stay away from passing by reference (especially within a class definition) unless you have a legitimate reason for doing so. It is bad practice when creating instances of classes (i.e. "sticky values" if you will).
Related
Good day. I'm trying to execute a function. i declare a global variable to get data (variable) outside the function, and the function i put inside a public function of a Class.
class Test {
public function execute(){
$data = "Apple";
function sayHello() {
global $data;
echo "DATA => ". $data;
}
sayHello();
}
}
$test = new Test;
$test->execute();
The expected result:
DATA => Apple
The real result:
DATA =>
the global variable is not getting the variable outside the function. Why it happened? Thank you for the help.
$data is not a global variable. It's inside another function, inside a class. A global variable sits outside any function or class.
But anyway your use case is unusual - there's rarely a need to nest functions like you've done. A more conventional, logical and usable implementation of these functions would potentially look like this:
class Test {
public function execute(){
$data = "Apple";
$this->sayHello($data);
}
private function sayHello($data) {
echo "DATA => ". $data;
}
}
$test = new Test;
$test->execute();
Working demo: http://sandbox.onlinephpfunctions.com/code/e91b98bb15fcfa71b1c6cbbc305b5a93df678e8b
(This is just one option, but it's a reasonable one, although since this is clearly a reduced, abstract example, it's hard to be sure what your real scenario would actually require or be best served by.)
I am trying to unit-test some of my code and it would be easier to just call my setters dynamically based on some variables. Unfortunately my approach does not work as expected and I couldn't find more information regarding on how to do that.
I have one variable which always is a string. It is used as property name and together with the "set" keyword it should result in "setSomething" or "setSomethingElse".
I already tried
$obj->set{$property}($value);
// or
$obj->set$property($value);
But those do not seem to work.
Maybe someone of you pro's know the right approach ;)!
You need to make the entire method name a variable, or enclose the whole name in {} e.g.
class test {
public $Something;
public $SomethingElse;
function setSomething($value) {
$this->Something = $value;
}
function setSomethingElse($value) {
$this->SomethingElse = $value;
}
}
$property = "Something";
$t = new test;
$setter = "set$property";
$t->$setter(4);
echo $t->Something;
$property = "SomethingElse";
$t->{"set$property"}(8);
echo $t->SomethingElse;
Output
4
8
Demo on 3v4l.org
I'm not sure how to best title this so I thought I would post an example of what I'm trying to achieve. I've super-simplified this for now.
So first is the very basic class that will do the action.
abstract class AuditedSave extends AuditedSaveImplementation
{
public static function run($callback = null)
{
return new AuditedSaveImplementation($callback);
}
}
class AuditedSaveImplementation
{
public function __construct(Closure $closure)
{
echo ' - I ran before'; // point 1, $test = 0
$closure();
echo ' - i ran after!'; // point 2, $test = 1
}
}
Then the code that calls it.
$test = 0;
AuditedSave::run(function() use ($test)
{
$test = 1;
});
So between point 1 and 2 as commented, the closure runs and would set the value of $test to 1. However, I want to store the value of whatever is passed as the first parameter (in this case, $test) as a copy of what it was at the time of function calling - which will always run, the closure then modifies it (this is the part that can be variable), and then afterwards a comparison gets made and actions happen based on differences - which will always run.
However, in order to do this, I need to be able to access the $test variable within the __construct() method of AuditedSaveImplementation without knowing what it's called.
Is this possible at all?
You can use ReflectionFunction class getStaticVariables method to get params passed to closure via use. Like this:
class AuditedSaveImplementation
{
public function __construct(Closure $closure)
{
echo ' - I ran before'; // point 1, $test = 0
$closureReflection = new ReflectionFunction($closure);
$variables = $closureReflection->getStaticVariables();
var_dump($variables);
$closure();
echo ' - i ran after!'; // point 2, $test = 1
}
}
But as mentioned in the comments you should rethink how you do this.
It's not a good approach. Try to create a special class as was suggested in comments.
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)
I have the following function:
<?php
class Test{
function myFunction(){
return array('first','second','third');
}
}
?>
And I can print out the elements of the array:
$var=new Test();
$varr=$var->myFunction();
print($varr[1]);
Is there a way to condense this statement so I don't have to assign $var->myFunction() to a second variable (in this case $varr)?
PHP does not support this very well (as of PHP 5.3) as Tim Cooper already highlighted. So you need to think twice if you really need to have this compacted.
You can do things quite dynamically e.g. by return an ArrayObject instead of an array:
class Test
{
function myFunction()
{
return new ArrayObject(array('first','second','third'), 3);
}
}
$var = new Test();
print($var->myFunction()->{1});
Which will decorate the array data with some additional methods and ways of accessing. Another way would be for functions w/o parameter to fool the PHP parser and offer a property instead of a function dynamically:
class Test
{
function myFunction()
{
return array('first','second','third');
}
public function __get($name)
{
return $this->$name();
}
}
$var = new Test();
print($var->myFunction[1]);
But I don't know if this is really useful in an application.
So check your motivation why you want to compact the code and then decide on your own.
In PHP 5.4 you'll be able to do:
$varr=$var->myFunction()[1];
Until then, using list might help out:
list(,$varr) = $var->myFunction();
Another solution is to modify your method to accept an optional index of the item to return:
function myFunction($index = null){
$arr = array('first','second','third');
return $index == null ? $arr : $arr[$index];
}
$varr = $var->myFunction(1);