I want to include this function in a class. This function will essentially json_encode the output of the previous function (I hope that makes sense).
I want to do something like this:
<?php
$app = new App;
// $app->error(); // Should return ['some error', 'some other error']
echo $app->error()->encode(); // Should return {'errors': ['some error', 'some other error']}.
Also what's the correct term for such function? I've been searching but couldn't find anything as I didn't really know what I was looking for.
Thanks!
Edit
I think you got that wrong. It's my mistake I didn't mention it before.
That's just an example. Just like in frameworks like Slim, where you can do something like:
$response->getBody()->write('Something');
I want to do something similar to that. Not just that. I want to learn how it's done.
Here is some boilerplate code you could use. The idea is that you should make the error method return an object of yet another class. That object should then in turn have the encode method.
In your example, you want $app->error() to return an array. For it to behave as an array, we can extend the ArrayObject class.
Secondly, you want that same $app->error() to expose another method encode. So you define that method in that same class mentioned above:
// Extend ArrayObject to make objects behave as arrays
class ErrorMsg extends ArrayObject {
// Add method to return JSON string
public function encode() {
return json_encode(array("errors" => $this->getArrayCopy()));
}
}
class App {
private $error;
public function doSomething() {
// For demo sake, just set an error:
$this->error = ["An error occurred in doSomething", "No further info"];
}
public function error() {
// This is the key: instantiate and return another object
return new ErrorMsg($this->error);
}
}
$app = new App;
// Set an error condition in $app
$app->doSomething();
// Loop over error array
foreach ($app->error() as $index => $error) {
// Display the error
echo "Error $index is: $error\n";
}
// Display the JSON encoding of the same.
echo $app->error()->encode() . "\n";
Output:
Error 0 is: An error occurred in doSomething
Error 1 is: No further info
{"errors":["An error occurred in doSomething","No further info"]}
See it run on eval.in
General idea to chain method calls
In general, when you want your objects to support chained -> notation, you must make sure to define each method as returning yet another object with its own methods. Then those methods can again return objects, with again exposed methods, etc. And so you can chain-call on and on.
So if you want to be able to write:
$a = new A();
$result = $a->b()->c()->d();
...then your code would look something like this:
class D {
// ...
}
class C {
private $d;
public function C() { // constructor
$this->d = new D();
}
public function d() {
return $this->d;
}
}
class B {
private $c;
public function B() { // constructor
$this->c = new C();
}
public function c() {
return $this->c;
}
}
class A {
private $b;
public function A() { // constructor
$this->b = new B();
}
public function b() {
return $this->b;
}
}
Of course, this is just the structure, and you'd pass some specific data to either the constructors and/or methods. But the main idea is that you never return simple types (String, Number, ...), but always objects.
Related
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
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');
In PHP sometimes it would be nice if I could define a function or a class with a variable name like
$myfunctionname="test";
function $myfunctionname(){
//...
}
so it would create the function test()
or with classes too like:
$foo = bar;
class $foo {
// lots of complicated stuff
// ...
}
but this doesen't work. like this it would give parse errors!
Is there a solution to this?
(I know, this is not good practise, but just as a workaround, it would be handy)
EDIT: My actual problem:
I have a framework with a migration process where every migration step is in a separate php include file in a folder.
Each file contains only one migration class that contains the name of the include file.
Because the class has to have that certain name, I would like to create the name of the class to a generic name that is created by the filename constant __FILE__
Yes, you can, but I dont want you to.
$classname = "test";
eval("class {$classname}{ public function hello(){ echo \"hello!\"; } }");
$evil_class = new $classname();
$evil_class->hello(); // echo's "hello!"
now, if you don't mind me I'm going for a shower.
You can use a factory pattern:
class poly_Factory {
public function __construct() {
$class = 'poly';
return new $class();
}
}
If that is anything you want to get to.
http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
Scroll down to step 4, last part...
I know you did not ask for that, but what can your question be good for else?
No. This code throws a parse error on line 3 because of the $:
$foo = 'bar';
class $foo {
function hello() {
echo "World";
}
}
$mybar = new bar();
$mybar->hello();
Result:
Parse error: syntax error, unexpected T_VARIABLE, expecting T_STRING on line 3
And as Jan Dvorak pointed out in the comments: even if you figure out a way to do this, don't do this.
If you want to create a value object you can just use the stdClass builtin type.
$object = new stdClass;
$object->someValue = "Hello World";
echo $object->someValue;
See it in Action
If you want to assign methods then you have to use the magic __call function, here is how I would do it.
class AnonObject{
private $properties = array();
private $methods = array();
public function __get($property){
return array_key_exists($property, $this->properties)?$this->properties[$property]:null;
}
public function __set($property, $value){
if (!is_string($value) && is_callable($value)){
if ($value instanceof \Closure){
// bind the closure to this object's instance and static context
$this->methods[$property] = $value->bindTo($this,get_class($this));
} else {
// invokable objects
$this->methods[$property] = $value;
}
} else {
$this->properties[$property] = $value;
}
}
public function __call($method, $args){
if (array_key_exists($method, $this->methods)){
call_user_func_array($this->methods[$method], $args);
} else {
throw new RuntimeException("Method ".$method." does not exist on object");
}
}
}
See it In Action
Note, as stated by several other people this is bad practice. If the goal of this exercise is to compose the behavior of an instance of an object at runtime a more maintainable solution would be to use the Strategy Pattern
For example record like this: property3->property2->property1 is understood for me, this means that property of class, is object of another class, for example:
class a {
public $property1 = "some";
}
class b {
function __construct () {
$this->property2 = new a();
}
}
$obj = new b();
echo $obj->property2->property1;
this understood. but I can not understood records like this: method1()->method2()->method3()
can you post simple example, who using these records?
A simple example :
class A {
function fooA() {
echo "a...";
return $this;
}
function fooB() {
echo "b...";
return $this;
}
function fooC() {
echo "c...";
}
}
$a = new A();
$a->fooA()->fooB()->fooC();
Or with several classes :
class A
{
private $b;
public function __construct()
{
$this->b = new B();
}
public function fooA()
{
echo "A...";
return $this->b;
}
}
class B
{
public function fooB()
{
echo "B...";
}
}
$a = new A();
$a->fooA()->fooB();
It means that these functions return objects. For example, the following is possible (assuming $pdo is a valid PDO object):
$result = $pdo->query("SELECT * FROM `table`")->fetchAll();
This may not always be favorable, because you:
Lose readabiliy.
Lose the ability to use the objects in between.
Lose the ability to check for errors, and you are counting on the methods to return what you think it is.
In this example, you only get the resultset in the form of an array, but you cannot access PDOStatement which is returned by PDO::query(). In this case, it may not matter, in some cases it might.
Also, PDO::query() may return a BOOLEAN false in the case of an error, which would give an unexplained "Method fetchAll() used on an non-object" error.
Hey, I have a small test case set up as following:
class T {
public function __construct(){
$obj = new SimpleXMLElement(file_get_contents('vote.xml'));
return $obj;
}
}
$vv=new T;
var_dump($vv);
The dump of $vv equals, in this case, object(T)#1 (0) { } - in other words, not the expected output
When I return the object in a separate function, though, like this:
class T {
public function stackOverflow(){
$obj = new SimpleXMLElement(file_get_contents('vote.xml')); // or simplexml_load_file
return $obj;
}
}
$vv=new T;
$vv = $vv->stackOverflow();
var_dump($vv);
output is as expected (the object containing contents of 'vote.xml', tags and attributes). Why can I not return the object inside of the constructor? Thanks!
The constructor will only ever return a reference to the newly created object. This is intentional -- how else would you get a reference to the new object?
You could, however, create an object property in your constructor and then access it from outside. This would mean that you would create the object during the constructor process, so it would be done at the right time and, what's more, could be guaranteed to be done.
class T {
public $sxml;
public function __construct(){
$this->sxml = new SimpleXMLElement(file_get_contents('vote.xml'));
}
}
$vv=new T;
var_dump($vv->sxml);
Of course, if you don't need the reference to the new object, you could use a static method instead and never use the constructor:
class T {
public static function sxml() {
return new SimpleXMLElement(file_get_contents('vote.xml'));
}
}