I thought that I would be able to use toArray() on my Zend resultset, but I find that using toArray() fails with the message:
Rows as part of this DataSource, with type object cannot be cast to an array
What I thought would work was something like
return new JsonModel($collections->toArray());
But that fails with the above error message.
Here is the Collection, a small class
class Collection
{
public $collectionID;
public $name;
public function exchangeArray($data)
{
$this->collectionID = (!empty($data['collectionID'])) ? $data['collectionID'] : null;
$this->name = (!empty($data['name'])) ? $data['name'] : null;
}
// Add the following method:
public function getArrayCopy()
{
return get_object_vars($this);
}
}
If I add my own
public function toArray()
{
return array(get_object_vars($this));
}
I can coerce it into doing what I'd expect, but I'm not sure this is the best approach. Also if I use that in conjunction with JsonModel the JSON outputted will also contain variables from settings.global.php
Thanks in advance,
You have to loop through the records, as it won't fetch all in one go. So something like:
$m = array();
foreach($resultSet as $r)
$m[] = (array)$r;
Or else try:
$resultSet = (array)resultSet;
You should be able to use, $result->getArrayCopy()
if it doesn't work you could try adding to Collection this function
public function getArrayCopy()
{
return get_object_vars($this);
}
Related
I am wondering how PHP determines the equality of instances of a class with private properties:
class Example {
private $x;
public $y;
public __construct($x,$y) {
$this->x = $x; $this->y = $y;
}
}
and something like
$needle = new Example(1,2);
$haystack = [new Example(2,2), new Example(1,2)];
$index = array_search($needle, $haystack); // result is 1
The result is indeed 1, so the private member is compared. Is there a possibility to only match public properties?
I know I could overwrite the __toString method and cast all arrays and needles to string, but that leads to ugly code.
I am hoping to find a solution that is elegant enough to work with in_array, array_search, array_unique, etc.
A possible solution could be the PHP Reflection API. With that in mind you can read the public properties of a class and compare them to other public properties of another instance of the same class.
The following code is a simple comparison of public class properties. The base for the comparison is a simple value object.
declare(strict_types=1);
namespace Marcel\Test;
use ReflectionClass;
use ReflectionProperty;
class Example
{
private string $propertyA;
public string $propertyB;
public string $propertyC;
public function getPropertyA(): string
{
return $this->propertyA;
}
public function setPropertyA(string $propertyA): self
{
$this->propertyA = $propertyA;
return $this;
}
public function getPropertyB(): string
{
return $this->propertyB;
}
public function setPropertyB($propertyB): self
{
$this->propertyB = $propertyB;
return $this;
}
public function getPropertyC(): string
{
return $this->propertyC;
}
public function setPropertyC($propertyC): self
{
$this->propertyC = $propertyC;
return $this;
}
public function __compare(Example $b, $filter = ReflectionProperty::IS_PUBLIC): bool
{
$reflection = new ReflectionClass($b);
$properties = $reflection->getProperties($filter);
$same = true;
foreach ($properties as $property) {
if (!property_exists($this, $property->getName())) {
$same = false;
}
if ($this->{$property->getName()} !== $property->getValue($b)) {
$same = false;
}
}
return $same;
}
}
The __compare method of the Example class uses the PHP Reflection API. First we build a reflection instance of the class to which we want to compare to the current instance. Then we request all public properties of the class we want to compare to. If a public property does not exist in the instance or the value of the property is not the same as in the object we want to compare to, the method returns false, otherwise true.
Some examples.
$objectA = (new Example())
->setPropertyA('bla')
->setPropertyB('yadda')
->setPropertyC('bar');
$objectB = (new Example())
->setPropertyA('foo')
->setPropertyB('yadda')
->setPropertyC('bar');
$result = $objectA->__compare($objectB);
var_dump($result); // true
In this example the comparison results into true because the public properties PropertyB and PropertyC exist in both instances and have the same values. Keep in mind, that this comparison works only, if the second instance is the same class. One could spin this solution further and compare all possible objects based on their characteristics.
In Array Filter Example
It is a kind of rebuild of the in_array function based on the shown __compare method.
declare(strict_types=1);
namespace Marcel\Test;
class InArrayFilter
{
protected ArrayObject $data;
public function __construct(ArrayObject $data)
{
$this->data = $data;
}
public function contains(object $b)
{
foreach ($this->data as $object) {
if ($b->__compare($object)) {
return true;
}
}
return false;
}
}
This filter class acts like the in_array function. It takes a collection of objects and checks, if an object with the same public properties is in the collection.
Conclusion
If you want this solution to act like array_unique, array_search or ìn_array you have to code your own callback functions which execute the __compare method in the way you want to get the result.
It depends on the amount of data to be handled and the performance of the callback methods. The application could consume much more memory and therefore become slower.
i have a class and i want to call dynamicly all functions starting by default name:
class social_button
{
public function __construct()
{
[...]
}
private function social_facebook()
{[...]}
private function social_instagramm();
{[...]}
private function social_twitter();
{[...]}
[and so on]
}
My matter is, that i wont write all time:
$this->social_facebook();
$this->social_twitter();
...
because it could/will be an endless list.
So here is my questions:
Is there a way to call all functions generic/dynamic starting with "social"?
Like: $this->social_*();
(The " * " is something like a placeholder, which contains an unlimited number of chars)
Sorry for my bad english and much thanks to all answers.
Best regards
You can build the method name with the string concatenation:
$service = 'facebook';
$this->{'social_' . $service}();
or
$service = 'social_facebook';
$this->$service();
If you wan to call all of them, go with:
$services = ['facebook', 'twitter'];
foreach ($services as $service) {
$this->{'social_' . $service}();
}
Edit: See the answer by localheinz below for a better method, using reflection. get_class_methods() will only return public methods.
Building off hsz's answer:
You can get the list of a class' methods using get_class_methods(). Then you can loop through the results, and call the method if it starts with "social_".
// Get the list of methods
$class_methods = get_class_methods("social_button");
// Loop through the list of method names
foreach ($class_methods as $method_name)
{
// Are the first 7 characters "social_"?
if (substr($method_name, 0, 7) == "social_")
{
// Call the method
$this->{$method_name}();
}
}
The problem with the accepted answer is that it will not work with the example posted with the question. get_class_methods() returns only public methods, but the methods in question are marked as private.
If you want to determine all methods, use reflection instead:
class social_button
{
private function social_facebook()
{
return 'Facebook';
}
private function social_instagram()
{
return 'Instagram';
}
private function social_twitter()
{
return 'Twitter';
}
public function all()
{
$reflection = new \ReflectionObject($this);
$prefix = 'social_';
// filter out methods which do not start with the given prefix
$methods = array_filter($reflection->getMethods(), function (\ReflectionMethod $method) use ($prefix) {
return 0 === strpos($method->getName(), $prefix);
});
// invoke all methods and collect the results in an array
$results = array_map(function (\ReflectionMethod $method) {
$name = $method->getName();
return $this->$name();
}, $methods);
return $results;
}
}
$button = new social_button();
var_dump($button->all());
For reference, see:
http://php.net/manual/en/class.reflectionobject.php
http://php.net/manual/en/class.reflectionmethod.php
http://php.net/manual/en/function.array-filter.php
http://php.net/manual/en/function.array-map.php
For an example, see:
https://3v4l.org/qTkOM
In PHP using method chaining how would one go about supplying a functional call after the last method being called in the chain?
Also while using the same instance (see below). This would kill the idea of implementing a destructor.
The end result is a return value and functional call of private "insert()" from the defined chain properties (of course) without having to call it publicly, no matter of the order.
Note, if I echo (__toString) the methods together it would retrieve the final generated unique code which is normal behavior of casting a string.
Example below:
class object
{
private $data;
function __construct($name) {
// ... some other code stuff
}
private function fc($num) {
// some wicked code here
}
public function green($num) {
$this->data .= fc($num*10);
return $this;
}
public function red($num) {
$this->data .= fc($num*25);
return $this;
}
public function blue($num) {
$this->data .= fc($num*1);
return $this;
}
// how to get this baby to fire ?
private function insert() {
// inserting
file_put_content('test_code.txt', $this->data);
}
}
$tss = new object('index_elements');
$tss->blue(100)->green(200)->red(100); // chain 1
$tss->green(0)->red(100)->blue(0); // chain 2
$tss->blue(10)->red(80)->blue(10)->green(0); // chain 3
Chain 1, 2, and 3 would generated an unique code given all the values from the methods and supply an action, e.g. automatically inserting in DB or creating a file (used in this example).
As you can see no string setting or casting or echoing is taking place.
You could keep a list of things that needs to be initialised and whether they
have been so in this instance or not. Then check the list each time you use
one of the initialisation methods. Something like:
class O {
private $init = array
( 'red' => false
, 'green' => false
, 'blue' => false
);
private function isInit() {
$fin = true;
foreach($this->init as $in) {
$fin = $fin && $in;
}
return $fin;
}
public function green($n) {
$this->init['green'] = true;
if($this->isInit()) {
$this->insert();
}
}
public function red($n) {
$this->init['red'] = true;
if($this->isInit()) {
$this->insert();
}
}
public function blue($n) {
$this->init['blue'] = true;
if($this->isInit()) {
$this->insert();
}
}
private function insert() {
echo "whee\n";
}
}
But personally I think this would be more hassle then it's worth. Better imo
to expose your insert method and let the user of you code tell when the
initialisation is finished. So something that should be used like:
$o->red(1)->green(2)->blue(0)->insert();
-update-
If it's the case that it's impossible to predict what functions need to be called
you really do need to be explicit about it. I can't see a way around that. The reason
is that php really can't tell the difference between
$o1 = new A();
$o2 = $o1->stuff();
and
$o2 = (new A())->stuff();
In a language that allows overloading = I guess it would be possible but really
really confusing and generally not a good idea.
It is possible to move the explicit part so that it's not at the end of the call
chain, but I'm not sure if that would make you happier? It would also go against
your desire to not use another instance. It could look something like this:
class O {
public function __construct(InitO $ini) {
// Do stuff
echo "Whee\n";
}
}
class InitO {
public function red($n) {
return $this;
}
public function green($n) {
return $this;
}
public function blue($n) {
return $this;
}
}
$o = new O((new InitO())->red(10)->red(9)->green(7));
You can of course use just one instance by using some other way of wrapping
but the only ways I can think of right now would look a lot uglier.
Im with PeeHaa, this makes no sense! :)
Only chance to have something magically happen after the last chain was used (without being able to look into the future) is a Destructor/Shutdown function OR a manually cast/call to insert()
You can also decide to implement this statically without using objects.
<?php
class Object
{
private static $data;
public static function set($name)
{
// ... some other code stuff
}
private static function fc($num)
{
// some wicked code here
}
public static function green($num)
{
self::$data .= self::fc($num*10);
return new static;
}
public static function red($num)
{
self::$data .= self::fc($num*25);
return new static;
}
public static function blue($num) {
self::$data .= self::fc($num*1);
return new static;
}
// how to get this baby to fire ?
public static function insert()
{
// inserting
file_put_content('test_code.txt', self::$data);
}
}
//$tss = new object('index_elements');
$Object::set('index_elements')->blue(100)->green(200)->red(100)->insert(); // chain 1
$Object::set('index_elements')->green(0)->red(100)->blue(0)->insert(); // chain 2
$Object::set('index_elements')->blue(10)->red(80)->blue(10)->green(0)->insert(); // chain 3
?>
Ok let's see a code example
<?php
// map dummy class
class map
{
// __call magic method
public function __call($name, $args)
{
return $this;
}
}
// now we chain
$map = new map;
// let's find me
$map->start('here')
->go('right')
->then()
->turn('left')
->and('get water')
->dontEat()
->keep('going')
->youShouldSeeMe('smiling');
here we don't know what the last method would be and we need to trigger a kinda operation or event once we hit the end.
According to data structure we can call this the LIFO stack. (Last in first out)
so how did i solve this on PHP?
// i did some back tracing
... back to the __call function
function __call($name, $args)
{
$trace = debug_backtrace()[0];
$line = $trace['line'];
$file = $trace['file'];
$trace = null;
$getFile = file($file);
$file = null;
$getLine = trim($getFile[$line-1]);
$line = null;
$getFile = null;
$split = preg_split("/(->)($name)/", $getLine);
$getLine = null;
if (!preg_match('/[)](->)(\S)/', $split[1]) && preg_match('/[;]$/', $split[1]))
{
// last method called.
var_dump($name); // outputs: youShouldSeeMe
}
$split = null;
return $this;
}
And whoolla we can call anything once we hit the bottom.
*(Notice i use null once i am done with a variable, i come from C family where we manage memory ourselves)
Hope it helps you one way or the other.
So there are two things that I am stuck on now. First
class DisplayTaxonomy {
public $MyArray[]= new DisplayTaxonomy(); //the compiler says syntax error
//things I have tried
//public $ss[]= new Object();
}
Second! in a function like this:
public function whatever()
{
$Temp = new DisplayTaxonomy();
$Temp->setADTitle("1");
$MyArray[]= $Temp;//works just fine.
//then i tried to return the array
return $MyArray[];
}
I get the following
//Cannot use [] for reading in C:\xampp\htdocs\wordpress\wp-//content\themes\twentyeleven\page.php on line 52
then in the client side
$y=new DisplayTaxonomy();
$myArray[]=new DisplayTaxonomy();//works fine dont know why I cant do this in theclass.
$myArray[]=$y->getArrayOfDisplayTaxonomyObjects();
echo $myArray[0]->getADTitle();
It seems you want to create a class that handles a collection of Taxonomy objects. In that case you should have two classes, instead of making a class store instances of itself.
class TaxonomyContainer
{
private $collection = array();
public function addElement(DisplayTaxonomy $element)
{
$this->collection[] = $element;
}
public function getElements()
{
return $this->collection;
}
}
class DisplayTaxonomy
{
private $adTitle;
public function setAdTitle($adTitle)
{
$this->adTitle = $adTitle;
}
//and other functionality the Taxonomy object should have
}
Then you can avoid the ugly self replicating behaviour and separate your concerns.
$container = new TaxonomyContainer();
$element = new DisplayTaxonomy();
$container->addElement($element);
On the next level, it might be worth considering the use of one of PHP's predefined interfaces for the Container class.
You declare objects in the function body and initiate them in the constructor (or a member function). You don't use [] when returning an array, $array[] has the same functionality as array_push, nothing more.
To clarify,
class myClass {
public $obj = array();
public function __construct() {
$this->obj[] = new OtherObject();
}
public function getObj() {
return $this->obj;
}
}
You cannot do this :
class DisplayTaxonomy {
public $MyArray[]= new DisplayTaxonomy();
}
because it's like an infinite loop :) So you have to use __contruct() function.
After change the :
return $MyArray[];
to :
return $MyArray;
Your first issue is due to trying to call the class you're declaring.
class DisplayTaxonomy {
public $MyArray[]= new DisplayTaxonomy();
You should initialize your object outside of the class, in the portion of code that you need to reference the class.
In addition, the object is already an array so you can omit attaching [] to the end return $MyArray:
public function whatever(){
$Temp = new DisplayTaxonomy();
$Temp->setADTitle("1");
$MyArray[] = $Temp;
return $MyArray;
}
You're declaring the array object here:
$MyArray[]= $Temp;//works just fine
You can't call code (new DisplayTaxonomy()) when definining class properties. You'll have to assign the value in the constructor of the class:
class Foo
{
public $MyArray;
public function __construct()
{
$this->MyArray = new DisplayTaxonomy();
}
}
The other issue is that the $array[] shortcut is for appending to an array. If you want to just return the array (and not write to the array which you're asking about with []), simply skip []:
return $MyArray;
Expanded:
As Vincent Joigƞie pointed out below; you're trying to create a class with the same name as the class you're already creating. This doesn't make any sense, except for static properties. In that case you can't use __construct(), but would rather create / set the object in the static method you're calling to retrieve the object the first time:
static public function getInstance()
{
if (self::$MyArray === null)
{
self::$MyArray = new DisplayTaxonomy();
}
return self::$MyArray;
}
This is however probably not what you want, and it seems you've confused something in your logic in your class definition. Guessing freely you might just want:
class Foo
{
public $MyArray = array();
}
As array() is a static assignment (and not a function call), it's allowed in the class definition.
Is there any way to control json_encode behavior on objects? Like excluding empty arrays, null fields and so on?
I mean something like when using serialize(), where you can implement magic __sleep() method and specify what properties should be serialized:
class MyClass
{
public $yes = "I should be encoded/serialized!";
public $empty = array(); // // Do not encode me!
public $null = null; // Do not encode me!
public function __sleep() { return array('yes'); }
}
$obj = new MyClass();
var_dump(json_encode($obj));
The most correct solution is extending the interface JsonSerializable;
by using this interface you just need to return with the function jsonSerialize() what you want json_encode to encode instead of your class.
Using your example:
class MyClass implements JsonSerializable{
public $yes = "I should be encoded/serialized!";
public $empty = array(); // // Do not encode me!
public $null = null; // Do not encode me!
function jsonSerialize() {
return Array('yes'=>$this->yes);// Encode this array instead of the current element
}
public function __sleep() { return array('yes'); }//this works with serialize()
}
$obj = new MyClass();
echo json_encode($obj); //This should return {yes:"I should be encoded/serialized!"}
Note: this works in php >= 5.4 http://php.net/manual/en/class.jsonserializable.php
You could make the variables private. Then they won't show up in the JSON encoding.
If that is not an option, you could make a private array, and use the magic methods __get($key) and __set($key,$value) to set and get values in/from this array. In your case the keys would be 'empty' and 'null'. You can then still access them publicly but the JSON encoder will not find them.
class A
{
public $yes = "...";
private $privateVars = array();
public function __get($key)
{
if (array_key_exists($key, $this->privateVars))
return $this->privateVars[$key];
return null;
}
public function __set($key, $value)
{
$this->privateVars[$key] = $value;
}
}
http://www.php.net/manual/en/language.oop5.overloading.php#object.get