How to save result OOP? - php

I have the following class:
class Algorithm {
public function __constructor($arr){
$this->arr = $arr;
}
public function handle(){
return $this->Q($this->arr[0]); // 1, 2, 3 etc
}
public function Q(){
return 2;
}
public function R(){
return 1;
}
}
I create some instances of class in loop:
foreach($arr as $key){
$objects[] = new Algorithm(["1", "2", "3"]);
}
So, I need to handle all values passed to Algorithm and return state execution. I mean save result from Q and R methods in class.
So in result I need to get so as:
$objects[0]->Q; //1
$objects[1]->Q; //2
$objects[0]->R; //1
$objects[1]->R; //2
After I need to pass result objects in another class B:
$nextHandle = new B($objects);
Where get others result of two functions:
class B {
public function go($objects[0]) {
return 1;
}
public function do($objects[0]) {
return 1;
}
}
In result also return values do()and go()
So, problem is that I don't know how to operate of instances of object and pass these for calculation in another class. Also how to save intermediate states of calculations, I mean methods in each class?
Also one of main thinking is how to pass object with all states in another instance of class, that inside I can get state of element passed object?

Related

Calling member function on array

I don't truly understand how chaining functions work on the values that are returned.
Let's say I have a function that returns a string or array
public static $query;
public static function getArray($arr) {
Database::$query = $arr;
return Database::$query;
}
public function single() {
return Database::$query[0];
}
Why, when I call it can I then not chain a function onto this to affect the string (In this example I was to append ' test' and how would I go about doing this?
Why can I simply not call
Database::getArray(array("test","test2"))->single();
Without getting a Call to a member function single() on array error. But instead, make it return only the first value of the array.
How would I go append doing what I'm trying to achieve here? Why is my logic wrong?
When you call a method, the return value is whatever that method decides to return; the return value doesn't have any automatic relationship with the object you called the method on. For instance:
class A {
public function foo() {
return 'Hello, World!';
}
}
$a = new A;
echo $a->foo();
The value returned is just an ordinary string, just as if foo was a global function not attached to any object.
In PHP, strings (and other "basic types" like arrays) are not objects, so you can't call any methods on them. Even if you could, those methods would be built into the language, and you couldn't just decide that ->single() could be called on any array.
What may be confusing is that some people write methods with the convention that they return an object, known as a "fluent interface", or more generally "chained methods". This is not a feature of the language, just a natural consequence of returning an object from a method:
class A {
public function foo() {
return new B;
}
}
class B {
public function bar() {
return 'Hello, World!';
}
}
$a = new A;
$b = $a->foo(); // $b is a B object
echo $b->bar();
// We can combine this into one line:
echo (new A)->foo()->bar();
There is nothing special about this chaining; it's just that wherever you have an object, you can call appropriate methods on it, just as wherever you have a number, you can do maths with it. Compare with a simple addition:
function foo() {
return 1;
}
$a = foo();
$a = $a + 2;
echo $a;
// We can combine this into one line:
echo foo() + 2;
// Or keep the assignment:
$a = foo() + 2;
echo $a;
The object doesn't know it's being chained - in fact, it shouldn't need to know anything about the code around it, and that's an important part of structured programming.
A common pattern is then to have modifying methods which return the object they just modified, so you can make a series of modifications in one go:
class A {
private $words = [];
public function addWord($word) {
$this->words[] = $word;
// $this is the current object, which is an instance of class A
return $this;
}
public function getString() {
return implode(' ', $this->words);
}
}
$a = new A;
// Calling $a->addWord(...) gives us back the same object
$a = $a->addWord('Hello');
$a = $a->addWord('World');
// Calling $a->getString() gives us back a string
echo $a->getString();
// We can combine this into one line:
echo (new A)->addWord('Hello')->addWord('World')->getString();
Note that you can only refer to $this if you have created an instance of the object (with the new keyword), not in a method declared as static. A static method can still have this kind of pattern, but it will need to return some other object, like new self (a new instance of the current class) or self::$foo (an object created earlier).
it's called fluent interface, if you want to chain methods from same class you have to return this from each of them which you want to call fluently, so your code should look like:
public static $query;
public function getArray($arr) {
Database::$query = $arr;
return $this;
}
public function single() {
return Database::$query[0];
}
after applying changes, the construct Database::getArray(array("test","test2"))->single(); will work, however you may consider renaming method getArray, because as its name suggests, it shouldn't be returning $this, but array
#EDIT
you should change the type of function getArray from public static function to public function to make it work, also your final statement will change to something like:
(new Database())->getArray(array("test","test2"))->single();
however, in this case, I would consider redesigning your class and creating some kind of singleton so that you instantiate Database class only once and store the object somewhere

Query array or object property?

I'm still new to OOP and this is probably a simple question, not sure if I'm overthinking this.
Let's say we have a simple class like the following that we can use to instantiate an object that can generate an array:
class gen_arr {
public $arr = array();
public function fill_arr() {
$this->arr["key"] = "value";
}
}
// instantiate object from gen_arr
$obj = new gen_arr();
Now if you wanted to get the value of the object's array's item, would you generate an array first and then echo the value like:
$arr = $obj->fill_arr();
echo $arr["key"];
Or would you access the object's property directly?
echo $obj->arr["key"]
In the actual code the property is private and there is a method that allows the viewing of the property array, the above is just to simplify the question.
Are there performance considerations and/or just best practices when it comes to this kind of case?
UPDATE:
It's still unclear from the answers if the best way is to generate an array from the property and access that array or just access the property directly (through the getter method)
Since you are filling the array with items only on fill_arr, those items wont be availabl until you call $arr = $obj->fill_arr();.
If you want to directly call the array, then you have to fill this array on the constructor function of this call like this:
class gen_arr {
public $arr = array();
function __construct() {
$this->arr["key"] = "value";
}
}
First off, the class you shared with us has a range of problems:
its sole instance property is public and can be modified by anyone
you have some temporal coupling, the method fill_arr() needs to be invoked before accessing the the value makes any sense
Encapsulation
Reduce the visibility of the instance property from public to private, so that the property can only be modified by the object itself, and provide an accessor instead:
class gen_arr
{
private $arr;
public function fill_arr()
{
$this->arr["key"] = "value";
}
public function arr()
{
return $this->arr;
}
}
Temporal Coupling
Remove the method fill_arr() and instead initialize the property $arr in one of the following options:
initialize field lazily when accessed the first time
initialize field in the constructor
initialize field with a default value
initialize field with a value injected via constructor
Initialize field lazily when accessed the first time
Initialize the field when it's accessed the first time:
class gen_arr
{
private $arr;
public function arr()
{
if (null === $this->arr) {
$this->arr = [
'key' => 'value',
];
}
return $this->arr;
}
}
Initialize field in the constructor
Assign a value during construction:
class gen_arr
{
private $arr;
public function __construct()
{
$this->arr = [
'key' => 'value',
];
}
public function arr()
{
return $this->arr;
}
}
Initialize field with a default value
Assign a value to the field directly, which works fine if you don't need to do any computation:
class gen_arr
{
private $arr = [
'key' => 'value',
];
public function arr()
{
return $this->arr;
}
}
Initialize field with a value injected via constructor
If the values are not hard-coded or otherwise calculated (as in the previous examples), and you need to be able to instantiate objects with different values, inject values via constructor:
class gen_arr
{
private $arr;
public function __construct(array $arr)
{
$this->arr = $arr;
}
public function arr()
{
return $this->arr;
}
}
Accessing and dereferencing values
This seems like this is your actual question, so the answer is - of course - It depends!.
Let's assume we have provided an accessor instead of accessing the otherwise public field directly:
Since PHP 5.4, the following is possible:
$object = new gen_arr();
echo $object->arr()['key'];
If you are still using an older version of PHP, you obviously can't do that and have to do something like this instead:
$object = new gen_arr();
$arr = $object->arr();
echo $arr['key'];
Largely, though, the answer to this question depends on the circumstances, and what you want to achieve. After all, readability is key for maintenance, so it might just make sense for you to introduce an explaining variable.
Note About your example, you could just use an ArrayObject instead:
$arr = new \ArrayObject([
'key' => 'value',
]);
echo $arr['key']);
For reference, see:
http://wiki.c2.com/?EncapsulationDefinition
http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/
http://php.net/manual/en/language.oop5.properties.php
http://wiki.c2.com/?ItDepends
http://php.net/manual/en/migration54.new-features.php
https://refactoring.com/catalog/extractVariable.html
http://wiki.c2.com/?IntroduceExplainingVariable
http://php.net/manual/en/class.arrayobject.php
For an example, see:
https://3v4l.org/qVVBM
First fill up the array
$gen_arr = new gen_arr();
$gen_arr->fill_arr();
then get the values with a getter method
$val = $gen_arr->getValue($key);
A getter method would be like this
public function getValue($key) {
return $this->arr[$key];
}
And certailny make the $arr property private

How to filter all returned values from methods in class

I inherited this PHP project. I've got one class which contains maybe 20 different methods, most of which return values. I'd like to filter every value returned by methods in this class. Is there a simple and elegant way to do this, or must I wrap every value in a filter method before it is returned?
The class looks like this:
class db_ops
{
// ...
public function get_var($query) {
// ...
return $values;
}
public function get_name($query) {
// ...
return $name;
}
// ...
}
I've used a sort of wrapper class to do something like this before. Here's a generic example, just to show how it works. If you like this idea you should be able implement your own along these lines.
class FilteredExample
{
private $example;
function __construct($example)
{
$this->example = $example;
}
public function __call($name, $arguments)
{
if (method_exists($this->example, $name)) {
$result = call_user_func_array([$this->example, $name], $arguments);
if (is_string($result)) {
return "$result filtered";
} elseif (is_array($result)) {
return array_map(function($item){ return "$item filtered"; }, $result);
} else {
return $result;
}
} else {
throw new BadMethodCallException("Method '$name' not found", 1);
}
}
}
You inject the object whose methods you want to filter in the constructor, and then use the __call() magic method so that you can call all of the original object's methods. With method_exists, you can validate any called method and throw an exception if it doesn't exist, just like you'd get if you called it on the original object. If it does exist, then you can call it with call_user_func_array, filter the result, and return it.
I added a little section to show that you can handle different result types, or just return the unmodified result if it's not a certain type. You obviously wouldn't need to do this if all your original object's methods return the same type.
Here is an example of how you could use it based on the example class from your question:
$dbOps = new db_ops;
$filteredDbOps = new FilteredExample($dbOps);
$result = $filteredDbOps->get_var('something');

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 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)

Categories