Dynamically changing attribute in PHP - php

I have some abstract knowledge of OOP but this is the first time I am trying to code some OOP in PHP. I want to create a class that will have some attributes from construction but some attributes that dynamically change.
I am a little confused about all the terms (objects, classes, methods,...) so I do not know exactly what to search for. I made a simplified example below.
This is where I declared my class, that will accept 2 parameters on construction and calculate the third one, which is the higher number (please ignore that I don't check the type).
class test{
public function __construct($p1, $p2){
$this->p1 = $p1;
$this->p2 = $p2;
$this->p_max = max(array($this->p1, $this->p2));
}
}
Then I initialize the object and check the p_max:
$test = new test(1,2);
echo $test->p_max; // Prints 2
But if I change p1 and p2, the p_max won't change:
$test->p1 = 3;
$test->p2 = 4;
echo $test->p_max; // Prints 2 (I want 4)
How should I define the p_max inside my class to update every time I change p1 or p2? Is there a way without turning p_max into a method?

You can achieve this, using the magic __get method, which will be called, if a property of a class is accessed, but not defined. This is pretty hacky in my opinion, but works just as you want it to.
<?php
class test {
public function __construct($p1, $p2) {
$this->p1 = $p1;
$this->p2 = $p2;
}
public function __get($name) {
if ('p_max' === $name) {
return max(array($this->p1, $this->p2));
}
}
}
$test = new test(1,2);
echo $test->p_max; // Prints 2
$test->p1 = 3;
$test->p2 = 4;
echo $test->p_max; // Prints 4
Doing it this way, the max value will be calculated every time, you access this property.
Edit: Because the __get method will only be called for a property, which is not defined in the class itself, this wont work, if you assign the variable a value in the constructor or create it as property.
Edit2: I´d like to point out - again - that it´s pretty hacky to do it this way. For a way cleaner way, go with AbraCadaver´s answer. That´s how I personally would do it, too.

You don't really need to use a magic method, just a method that returns the calculated value:
class test{
public function __construct($p1, $p2){
$this->p1 = $p1;
$this->p2 = $p2;
}
public function p_max() {
return max($this->p1, $this->p2);
}
}
$test->p1 = 3;
$test->p2 = 4;
echo $test->p_max(); // call method
You can also accept optional arguments to p_max() to set new values and return the calculated value:
class test{
public function __construct($p1, $p2){
$this->p1 = $p1;
$this->p2 = $p2;
}
public function p_max($p1=null, $p2=null) {
$this->p1 = $p1 ?? $this->p1;
$this->p2 = $p2 ?? $this->p2;
return max($this->p1, $this->p2);
}
}
echo $test->p_max(3, 4); // call method
Also notice that max accepts multiple arguments so you don't have to specify an array.

Related

PHP, closures and extracting parameters

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.

Callable and closures

I've created not very complex test code (tested in PHP 5.5.12):
<?php
class Test
{
private $cached = null;
public function __construct()
{
$this->cached = [];
$this->cached[0] = 12;
}
function wrap($function, $index)
{
if (isset($this->cached[$index])) {
return $this->cached[$index];
}
$result = call_user_func($function);
return $result;
}
}
class B
{
public function run()
{
$x = 6;
$obj = new Test();
$value = $obj->wrap(
function () use ($x) {
return $this->test($x);
},
1
);
echo $value."<br />";
}
protected function test($x)
{
echo "I'm running ";
return $x * $x;
}
}
class C extends B
{
public function run()
{
$x = 6;
$obj = new Test();
$myFunc = function () use ($x) {
return $this->test($x);
};
$value = $obj->wrap($myFunc, 1);
echo $value."<br />";
}
}
class D extends B
{
public function run()
{
$x = 6;
$obj = new Test();
$value = $obj->wrap(array($this, 'test'), 1);
echo $value."<br />";
}
}
$b = new B();
$b->run();
$c = new C();
$c->run();
$d = new D();
$d->run();
Probably there are some parts of the code you could say it could be done better but the main point are closures function and callable. Those classes simulate in a very simple way caching system. If data is in cache it returns data from cache otherwise function that gets data is called (of course this cache system doesn't work because it doesn't have to - it's just a sample code).
Questions:
1) Why when using object $d I get the following warning:
call_user_func() expects parameter 1 to be a valid callback, cannot access protected method D::test()
and is it possible to launch protected method from parent? When I change this method from protected to public it can be launched without a problem
2) As you probably noticed I want to use some arguments for function I call using call_user_sync. Unfortunately I don't know those parameters when I call call_user_func so in class B and C I used closures where I can use/pass extra parameters. I have 2 extra questions connected to this:
is it the way where closures are useful and commonly used?
is it possible using object $d to pass parameters to test method without using closures but not when calling call_user_sync but inside class D?
It is important to note scope at the time of the execution. You are creating the callback in the correct scope, but you are executing the callback in another object with no access to the protected method (the callback get's executed in class Test not in a parent or child of class B.
I ran in to this issue some time ago when writing my own dispatcher class. One option was to set a "parent" on the dispatcher, and pass the dispatcher as one of the parameters on the callback. The callback then checks the "parent" associated with the Dispatcher for === $this, and then knows that it has access and goes to town.
You have to do your own access checking, is the point.

PHP trying to create dynamic variables in classes

I need to construct a class with alot of variables directly from the Database, For simplicity we'll name them 'userX', I've looked into ORM just a little, but its way over my head.
Essentially I thought I could use my procedural code
for ($i=0; $i<100; $i++) {
public ${'user'.$i};
}
But, in a class
class test() {
private $var1;
for ($i=0; $i<10000; $i++) {
public ${'user'.$i};
}
function __constructor .....
}
Obviously not.. but it leaves me with the same problem, how can I add $user0, $user1, $user2, etc etc, without having to type all 10k of them in..
Obviously, it would be 1000x easier to just grab the names from the Database, but again, that looks even harder to code. Should I buckle down and grab them all ORM style?
You could simply use the magic accessors to have as many instance attributes as you wish :
class test{
private $data;
public function __get($varName){
if (!array_key_exists($varName,$this->data)){
//this attribute is not defined!
throw new Exception('.....');
}
else return $this->data[$varName];
}
public function __set($varName,$value){
$this->data[$varName] = $value;
}
}
Then you could use your instance like this :
$t = new test();
$t->var1 = 'value';
$t->foo = 1;
$t->bar = 555;
//this should throw an exception as "someVarname" is not defined
$t->someVarname;
And to add a lot of attributes :
for ($i=0;$i<100;$i++) $t->{'var'.$i} = 'somevalue';
You could also initialize a newly created instance with a given set of attributes
//$values is an associative array
public function __construct($values){
$this->data = $values;
}
Try $this->{$varname}
class test
{
function __construct(){
for($i=0;$i<100;$i++)
{
$varname='var'.$i;
$this->{$varname}=$i;
}
}
}
You can use variable variables ($$var) - content of one variable is used as a name for other variable (double $$)
Therefore not $this->varname but $this->$varname.
class test
{
for($i=0;$i<100;$i++)
{
$varname='var'.$i;
$this->$varname=$i;
}
}
This will dynamically create 100 variables with names $var0, $var1 ...

In PHP, is it possible to have a function within a class that's non-static, but also isn't an instance function?

In PHP, is it possible to have a function within a class that's non-static, but also isn't an instance function?
For example, if I have the following:
class A
{
public $i;
function setValue($val) {
$this->i = $val;
}
}
$a1 = new A;
$a1->setValue(5);
echo $a1->i; // result: 5
$a2 = new A;
$a2->setValue(2);
echo $a2->i; // result: 2
Can I add a function to that class that can have "visibility" of all instances of itself so I can do something like so (which I know doesn't work, but communicates my thought):
class A
{
public $i;
function setValue($val) {
$this->i = $val;
}
function getTotal() {
return sum($this->i); // I know sum() isn't a built-in function, but it helps explain what I want. I'm not sure if $this makes sense here too.
}
}
$a1 = new A;
$a1->setValue(5);
echo $a1->i; // result: 5
$a2 = new A;
$a2->setValue(2);
echo $a2->i; // result: 2
echo A::getTotal(); // returns: 7
I guess A::getTotal() means getTotal() would need to be static, but if it was static then it wouldn't then be able to "see" each class instance.
Is this type of thing possible, and what's the correct terminology I should be using?
No, there is no built-in instance enumeration, you will need to keep references to each instantiated object yourself. You can keep an array of instances in a static property of the class and populate it in your __construct(). You can then have a static method loop over this array and process all instances.
I think you would like something like this:
class A
{
public $i;
function setValue($val) {
$this->i = $val;
}
}
$a1 = new A;
$a1->setValue(5);
echo $a1->i; // result: 5
$a2 = new A;
$a2->setValue(2);
echo $a2->i; // result: 2
$total = 0;
foreach( get_defined_vars() as $name => $obj ) {
if ( $obj instanceof A ) {
$total += $obj->i;
}
}
echo $total; // returns: 7
The function you need here is "get_defined_vars". But it only gets the variables within the current scope!
Just make the class member of the sum static as well. If you do this, then you will need to make sure it's maintained properly within each class (i.e. setValue needs to update that sum appropriarely).
This is probably not a great way to do things, though. I think it would get pretty confusing. In what context do you need the sum that you don't have access to all the instances?
Are you looking for a protected function foo($s){...} which the class can use but cannot be accessed from the outside? (PHP5 only)

How to pass method argument to class property?

I am trying to create properties based on the method arguments. For example:
class Test{
public function newProperty($prop1,$prop2){
//I want to create $this->argu1 and $this->argu2 after calling newProperty method.
}
}
$test=new Test();
$test->newProperty('argu1','argu2')
Is this possible? Thanks for any helps.
as simple as:
$this->$prop1 = 'whatever';
suppose you wanted to handle an undefined number of arguments, you could use:
foreach(func_get_args() as $arg) {
$this->$arg = 'some init value';
}
On the other hand, all this is quite unnecessary as all these properties will be public and thus:
$test->argu1 = 'whatever';
would do exactly the same.
Try this:
class Test{
private argu1 = '';
private argu2 = '';
public function newProperty($argu1,$argu2){
//This is a great place to check if the values supplied fit any rules.
//If they are out of bounds, set a more appropriate value.
$this->prop1 = $argu1;
$this->prop2 = $argu2;
}
}
I'm a little unclear from the question if the class properties should be named $prop or $argu. Please let me know if I have them backwards.

Categories