Ok my problem is as follows;
I have a class that describes a pet with this constructor;
public function __construct($name, $type, $age)
So what I want to do is make a number of pet objects, then I want to print all the attributes of all the objects of this class so that it looks something like this
What is the best way of going about it? I know how to iterate through an object's variables, but my main concern is how to iterate through all objects of a certain class. I would love it if someone could show me a code example of something, particularly if there is a way to do it without the use of arrays.
Any help is appreciated!
You could, in the class constructor, append $this to a static array that keeps all the elements of this type:
class Pet {
public static $allPets = array();
function __construct($name, $type, $age) {
self::$allPets[] = $this;
// more construction
}
}
Your list of all Pet objects is now in Pet::$allPets.
Normally you would expect to have some way of tracking the instances you've created, maybe in an array or some kind of containing class.
But for the sake of argument, you could check all the variables in the current scope with get_defined_vars(), recursively searching any arrays or objects you find, with something like this:
function findInstancesOf($classname, $vars)
{
foreach($vars as $name=>$var)
{
if (is_a($var, classname))
{
//dump it here
echo "$name is a $classname<br>";
}
elseif(is_array($var))
{
//recursively search array
findInstancesOf($classname, $var);
}
elseif(is_object($var))
{
//recursively search object members
$members=get_object_var($var);
findInstancesOf($classname, $members);
}
}
}
$vars = get_defined_vars();
findInstancesOf('MyPetClass', $vars);
I guess it depends on your structure, but I´d have another object / class that contains all generated pet objects, so I would loop through that.
Well, you could make a custom create option and use static variables to store an instance of each created class
Class Pet
{
public static $pets = array();
public static create($name, $type, $age)
{
$pet = new Pet($name, $type, $age);
self::$pets[] = $pet;
return $pet;
}
}
Pet::createPet("test", "test", 42);
Pet::createPet("test", "test", 42);
Pet::createPet("test", "test", 42);
foreach(Pet::$pets as $pet)
{
echo $pet->name;
}
i would make a foreach loop
foreach($myobject as $key => $pent)
{
echo $key;
echo $pent;
}
Related
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
Ive got this snippet of code below which works perfectly fine. I have been profiling it and the bit of code gets used alot of times, so I want to try figure out how to write it in a way that will perform better than the current way its written.
Is there a more efficient way to write this?
function objectToArray($d) {
if (is_object($d)) {
// Gets the properties of the given object
// with get_object_vars function
$d = get_object_vars($d);
}
if (is_array($d)) {
// Return array converted to object Using __FUNCTION__ (Magic constant) for recursive call
return array_map(__FUNCTION__, $d);
}
else {
// Return array
return $d;
}
}
You could implement a toArray() method to the class that needs to be converted:
e.g.
class foo
{
protected $property1;
protected $property2;
public function __toArray()
{
return array(
'property1' => $this->property1,
'property2' => $this->property2
);
}
}
Having access to the protected properties and having the whole conversion encapsulated in the class is in my opinion the best way.
Update
One thing to note is that the get_object_vars() function will only return the publically accessible properties - Probably not what you are after.
If the above is too manual of a task the accurate way from outside the class would be to use PHP (SPL) built in ReflectionClass:
$values = array();
$reflectionClass = new \ReflectionClass($object);
foreach($reflectionClass->getProperties() as $property) {
$values[$property->getName()] = $property->getValue($object);
}
var_dump($values);
depends what kind of object it is, many standard php objects have methods built in to convert them
for example MySQLi results can be converted like this
$resultArray = $result->fetch_array(MYSQLI_ASSOC);
if its a custom class object you might consider implementing a method in that class for that purpose as AlexP sugested
Ended up going with:
function objectToArray($d) {
$d = (object) $d;
return $d;
}
function arrayToObject($d) {
$d = (array) $d;
return $d;
}
As AlexP said you can implement a method __toArray(). Alternatively to ReflexionClass (which is complex and expensive), making use of object iteration properties, you can iterate $this as follow
class Foo
{
protected $var1;
protected $var2;
public function __toArray()
{
$result = array();
foreach ($this as $key => $value) {
$result[$key] = $value;
}
return $result;
}
}
This will also iterate object attributes not defined in the class: E.g.
$foo = new Foo;
$foo->var3 = 'asdf';
var_dump($foo->__toArray());)
See example http://3v4l.org/OnVkf
This is the fastest way I have found to convert object to array. Works with Capsule as well.
function objectToArray ($object) {
return json_decode(json_encode($object, JSON_FORCE_OBJECT), true);
}
This thread didn't helped me.
If I use
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
I get
attrib1_name : attrib2_name : attrib3_name
There are no values. Also a private attribute is shown, which I don't want.
If I use
echo "<pre>";
print_r(get_object_vars($this));
echo "</pre>";
I get
Array
(
[atrrib1_name] => attrib1_value
[attrib2_name] => attrib2_value
)
Here again I have a private attribute and all sub attributes. But this time I have the values. How can I constrain this to one level?
Isn't there a possibility to show all public attributes with their values of an object?
You are seeing non-public properties because get_class_vars works according to current scope. Since you are using $this your code is inside the class, so the non-public properties are accessible from the current scope. The same goes for get_object_vars which is probably a better choice here.
In any case, a good solution would be to move the code that retrieves the property values out of the class.
If you do not want to create a free function for that (why? seriously, reconsider!), you can use a trick that involves an anonymous function:
$getter = function($obj) { return get_object_vars($obj); };
$class_vars = $getter($this);
See it in action.
Update: Since you are in PHP < 5.3.0, you can use this equivalent code:
$getter = create_function('$obj', 'return get_object_vars($obj);');
$class_vars = $getter($this);
You can do this easily with php Reflection api
Extending Mr.Coder's answer, here is a snippet to fetch the public attributes of the object (name and value) as an array
public function getPublicProperties()
{
$results = [];
$reflectionObject = (new ReflectionObject($this));
$properties = $reflectionObject->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$results[$property->getName()] = $property->getValue($this);
}
return $results;
}
Use get_object_vars.
see: http://dk.php.net/manual/en/function.get-object-vars.php
I Fully recognize what you are trying to achieve so why not have something external like this to help out... (pasted from PHPFiddle)
<?php
final class utils {
public static function getProperties(& $what) {
return get_object_vars($what);
}
}
class ball {
var $name;
private $x, $y;
function __construct($name,$x,$y) {
}
function publicPropsToArray() {
return utils::getProperties($this);
}
function allPropsToArray() {
return get_object_vars($this);
}
}
$ball1 = new ball('henry',5,6);
//$ball2 = new ball('henry',3,4);
echo "<pre>";
print_r($ball1->publicPropsToArray());
echo "\r\n\r\n";
print_r($ball1->allPropsToArray());
echo "\r\n\r\n";
?>
This way I can both access all properties of the object or for something such as a database access layer or similarly for a function that send "safe" data to a view or another un-privileged model I can send just the public properties, but have the behaviour defined within the object.
Sure this leads to coupling with a utility class, but to be fair not all couplings are bad, some are nesecarry to achieve an end goal, dont get bogged down by these things
So for example I have this code:
class Object{
public $tedi;
public $bear;
...some other code ...
}
Now as you can see there are public variables inside this class. What I would like to do is to make these variables in a dynamic way, with a function something like:
private function create_object_vars(){
// The Array what contains the variables
$vars = array("tedi", "bear");
foreach($vars as $var){
// Push the variables to the Object as Public
public $this->$var;
}
}
So how should I create public variables in a dynamic way?
$vars = (object)array("tedi"=>"bear");
or
$vars = new StdClass();
$vars->tedi = "bear";
Yes, you can do this.
You're pretty much correct - this should do it:
private function create_object_vars(){
// The Array of names of variables we want to create
$vars = array("tedi", "bear");
foreach($vars as $var){
// Push the variables to the Object as Public
$this->$var = "value to store";
}
}
Note that this makes use of variable variable naming, which can do some crazy and dangerous things!
As per the comments, members created like this will be public - I'm sure there's a way of creating protected/private variables, but it's probably not simple (eg you could do it via the C Zend API in an extension).
As alternative, you can also derive your object from ArrayObject. So it inherits array-behaviour and a few methods which make injecting attributes easier.
class YourObject extends ArrayObject {
function __construct() {
parent::__construct(array(), ArrayObject::PROPS_AS_ARRAY);
}
function create_object_vars() {
foreach ($vars as $var) {
$this[$var] = "some value";
}
}
Attributes will then be available as $this->var and $this["var"] likewise, which may or not may suit the use case. The alternative method for setting attributes would be $this->offsetSet("VAR", "some value");.
Btw, there is nothing evil about variable variables. They're a proper language construct, as would be reusing ArrayObject.
I have a class with a static method. There is an array to check that a string argument passed is a member of a set. But, with the static method, I can't reference the class property in an uninstantiated class, nor can I have an array as a class constant.
I suppose I could hard code the array in the static method, but then if I need to change it, I'd have to remember to change it in two places. I'd like to avoid this.
You can create a private static function that will create the array on demand and return it:
class YourClass {
private static $values = NULL;
private static function values() {
if (self::$values === NULL) {
self::$values = array(
'value1',
'value2',
'value3',
);
}
return self::$values;
}
}
I put arrays in another file and then include the file wherever I need it.
I am having a really really hard time understanding your question. Here is essentially what I understood:
I need to maintain a proper set, where
no two elements are the same.
PHP does not have a set type, not even in SPL! We can emulate the functionality of a set but any solution I can think of is not pleasant. Here is what I think is the cleanest:
<?php
class Set {
private $elements = array();
public function hasElement($ele) {
return array_key_exists($ele, $elements);
}
public function addElement($ele) {
$this->elements[$ele] = $ele;
}
public function removeElement($ele) {
unset($this->elements[$ele]);
}
public function getElements() {
return array_values($this->elements);
}
public function countElements() {
return count($this->elements);
}
}
Example usage:
<?php
$animals = new Set;
print_r($animals->getElments());
$animals->addElement('bear');
$animals->addElement('tiger');
print_r($animals->getElements());
$animals->addElement('chair');
$animals->removeElement('chair');
var_dump($animals->hasElement('chair'));
var_dump($animals->countElements());