I have a class, that actually operates over a complicated array to make the manipulation more simple. The format of original array looks like this:
array(
array(
"name" =>"foo",
"type" =>8, //The array is NBT format and 8 stands for string
"value" =>"somevalue"
)
}
The class takes array like above as constructor:
class NBT_traverser implements ArrayAccess {
function __construct(&$array) {
$this->array = $array;
}
}
Then, this is how the members are accessed:
$parser = new NBT_traverser($somearray);
echo $parser["foo"]; //"somevalue"
When I print_R the class, I get the list of its values and the original complicated array. Like this:
object(NBT_traverser)#2 (1) {
["nbt":"NBT_traverser":private]=> &array(1) {
/*Tons of ugly array contents*/
}
Instead, I'd like to get this like the output of print_r:
array(
"foo" => "somevalue"
)
Is it possible to trick the print_r to do this? Current behavior makes it harder to debug with the class, than without it.
Of course, I can write my own method to print it, but I want to make the usage more simple for the users of the class. Instead, I wanted to give print_R something, that It will print as array.
You should not have issues if you are extending ArrayAccess just write a method to get your values
Example
$random = range("A", "F");
$array = array_combine($random, $random);
$parser = new NBT_traverser($array);
echo $parser->getPrint();
Output
Array
(
[A] => A
[B] => B
[C] => C
[D] => D
[E] => E
[F] => F
)
Class Used
class NBT_traverser implements ArrayAccess {
private $used; // you don't want this
protected $ugly = array(); // you don't want this
public $error = 0202; // you don't want this
private $array = array(); // you want this
function __construct(&$array) {
$this->array = $array;
}
function getPrint() {
return print_r($this->array, true);
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->array[] = $value;
} else {
$this->array[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->array[$offset]);
}
public function offsetUnset($offset) {
unset($this->array[$offset]);
}
public function offsetGet($offset) {
return isset($this->array[$offset]) ? $this->array[$offset] : null;
}
}
You could use the __toString function in your class
class Test
{
private $_array = array();
public function __toString()
{
return print_r($this->_array, true);
}
}
And then just echo out your class
$test = new Test();
echo $test;
I think this would print out your array as you want it to be?
Array
(
)
Related
I am trying to create a class that is going to generate dynamic class properties according to a user input.
There will be an array created from user input data. This array should work as an example:
$array = array(
# The boolean values are not relevant in this example
# The keys are important
'apple' => true,
'orange' => false,
'pear' => false,
'banana' => true,
);
Right now I want to create a new class with the array keys as the class properties:
class Fruit {
public $apple;
public $orange;
public $pear;
public $banana;
(etc.)
}
I had to manually write down all four properties now.
Is there a way to make it automated?
<?php
class MyClass
{
public function __construct ($config = [])
{
foreach ($config as $key => $value) {
$this->{$key} = $value;
}
}
}
$myClass = new MyClass(['apple' => 1, 'orange' => 2]);
echo $myClass->apple;
?>
this should help you
Here you go,
I put a few bonus things in there:
class MyClass implements Countable, IteratorAggregate
{
protected $data = [];
public function __construct (array $data = [])
{
foreach ($data as $key => $value) {
$this->{$key} = $value;
}
}
public function __set($key, $value){
$this->data[$key] = $value;
}
public function __get($key)
{
if(!isset($this->{$key})) return null; //you could also throw an exception here.
return $this->data[$key];
}
public function __isset($key){
return isset($this->data[$key]);
}
public function __unset($key){
unset($this->data[$key]);
}
public function __call($method, $args){
$mode = substr($method, 0, 3);
$property = strtolower(substr($method, 3)); //only lowercase properties
if(isset($this->{$property})) {
if($mode == 'set'){
$this->{$property} = $args[0];
return null;
}else if($mode == 'get'){
return $this->{$property};
}
}else{
return null; //or throw an exception/remove this return
}
throw new Exception('Call to undefined method '.__CLASS__.'::'.$method);
}
//implement Countable
public function count(){
return count($this->data);
}
//implementIteratorAggregate
public function getIterator() {
return new ArrayIterator($this->data);
}
}
Test it:
$myClass = new MyClass(['one' => 1, 'two' => 2]);
echo $myClass->two."\n";
//Countable
echo count($myClass)."\n";
//dynamic set
$myClass->three = 3;
echo count($myClass)."\n";
//dynamic get/set methods. I like camel case methods, and lowercase properties. If you don't like that then you can change it.
$myClass->setThree(4);
echo $myClass->getThree()."\n";
//IteratorAggregate
foreach($myClass as $key=>$value){
echo $key.' => '.$value."\n";
}
Outputs
2 //value of 2
2 //count of $data
3 //count of $data after adding item
4 //value of 3 after changing it with setThree
//foreach output
one => 1
two => 2
three => 4
Test it online
Disclamer
Generally though it's better to define the class by hand, that way things like IDE's work. You may also have issues because you won't necessarily know what is defined in the class ahead of time. You don't have a concrete definition of the class as it were.
Pretty much any method(at least in my code) that starts with __ is a PHP magic method (yes, that's a thing). When I first learned how to use these I thought it was pretty cool, but now I almost never use them...
Now if you want to create an actual .php file with that code in it, that's a different conversation. (it wasn't 100% clear, if you wanted functionality or an actual file)
Cheers.
I would like to use the list() statement in combination with an object.
$tuple = new Tuple();
// ..
list ($guestbook, $entry, $author) = $tuple;
This would work if $tuple was an array with three elements. But its an object.
Is there any way without using a method of Tuple (returning that kind of array) like implementing a fancy native interface I yet don't know?
You can implement the interface ArrayAccess to do so:
class Tuple implements ArrayAccess {
private $arr;
public function __construct($arr) {
$this->arr = $arr;
}
public function offsetExists($offset) {
return array_key_exists($offset, $this->arr);
}
public function offsetGet($offset) {
return $this->arr[$offset];
}
public function offsetSet($offset, $value) {
return $this->arr[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->arr[$offset]);
}
}
$tuple = new Tuple([1, 2, 3]);
list($am, $stram, $gram) = $tuple;
echo $am;
echo $stram;
echo $gram;
// outputs: 123
See this previous post:
Convert PHP object to associative array
I am assuming (I haven't tested it) you could then do:
$tuple = new Tuple();
list ($guestbook, $entry, $author) = (array) $tuple;
You can do this:
$tumple = new Tumple();
$properties = get_object_vars($tumple);// ["guestbook" => "Feel", "entry" => "Good", "author" => "Inc"];
I am new to PHP OOP.
I have this constructor
protected $path='default';
function __construct($var1, $var2 ..){
$this->var1=$var1;
$this->var2=$var2;
}
$obj = MyClass($var1='www')
I want that if I make object and don't pass any arguments then I get the object with empty values.
But if mention the properties in the constructor like above then the object should have those properties set.
Currently if I define the constructor with args and don't supply any then I get error
.
You could make use func_get_args() in PHP
<?php
class foo
{
private $arrParams;
function __construct()
{
if (func_num_args() != 0) {
$this->arrParams = $this->setValues(func_get_args());
}
}
public function setValues($params)
{
return $params;
}
public function dispParams()
{
print_r($this->arrParams);
}
public function retVal($var)
{
return $this->arrParams[$var];
}
}
$foo1 = new Foo(3, 45, 64, 34);
$foo1->dispParams();
OUTPUT :
Array
(
[0] => 3
[1] => 45
[2] => 64
[3] => 34
)
For getting a corresponding value from the array... You could just call
$foo1->retVal(2); // Prints you 64
you need to declare class property like this:
class yourClass{
protected $path='default';
public $var1;
public $var2;
function __construct($var1="dd", $var2='ss'){
$this->var1=$var1;
$this->var2=$var2;
}
}
$obj = new yourClass();
echo "without params: ".$obj->var1.$obj->var2;
$obj2 = new yourClass("sdf","sfdsdf");
echo " with params : ".$obj2->var1.$obj2->var2;
Demo
In php mysql / mysqli / postgre / etc... there are fetch_object functions where you can get an object for your row of data. By default it will return an object of stdClass, but you can also define a class_name and an array of params for the constructor.
I would like to do the same thing with a plain set of values. Preferably, setting the properties of the object before calling the constructor, which is the same behaviour the database-functions show. However, this doesn't seem to be possible.
The only way to even create an object with properties set seems to be to unserialize a preconstructed string. But that example still creates a new object as well, then sets the properties of that object from the unserialized object to ensure the constructor is called. But this means the constructor is called before the properties are set.
In short: I would like the following:
array_fetch_object(array $properties, string $class_name [, array $params ])
with the constructor called after the properties are set.
In the end I wrote the following class which performs the task using unserializing a fabricated string. It uses reflection to determine what the access-type of properties is and what the name of the constructor is (if any).
The heart of the class is the following line:
$object = unserialize('O:'.strlen($class_name).':"'.$class_name.'"'.substr(serialize($properties), 1));
which serializes the property-array and renames the serialized to the desired class_name.
class ObjectFactory {
private $properties;
private $constructors;
public function __construct() {
$this->properties = array();
$this->constructors = array();
}
private function setClass($class_name) {
$class = new ReflectionClass($class_name);
$this->properties[$class_name] = array();
foreach($class->getProperties() as $property) {
$name = $property->getName();
$modifier = $property->getModifiers();
if($modifier & ReflectionProperty::IS_STATIC) {
continue;
} else if($modifier & ReflectionProperty::IS_PUBLIC) {
$this->properties[$class_name][$name] = $name;
} else if($modifier & ReflectionProperty::IS_PROTECTED) {
$this->properties[$class_name][$name] = "\0*\0".$name; // prefix * with \0's unserializes to protected property
} else if($modifier & ReflectionProperty::IS_PRIVATE) {
$this->properties[$class_name][$name] = "\0".$class_name."\0".$name; // prefix class_name with \0's unserializes to private property
}
}
if($constructor = $class->getConstructor()) {
$this->constructors[$class_name] = $constructor->getName();
}
}
private function hasClassSet($class_name) {
return array_key_exists($class_name, $this->properties);
}
private function hasClassProperty($class_name, $property_name) {
if(!$this->hasClassSet($class_name))
$this->setClass($class_name);
return array_key_exists($property_name, $this->properties[$class_name]);
}
private function getClassProperty($class_name, $property_name) {
if(!$this->hasClassProperty($class_name, $property_name))
return false;
return $this->properties[$class_name][$property_name];
}
private function hasClassConstructor($class_name) {
if(!$this->hasClassSet($class_name))
$this->setClass($class_name);
return $this->constructors[$class_name] !== false;
}
private function getClassConstructor($class_name) {
if(!$this->hasClassConstructor($class_name))
return false;
return $this->constructors[$class_name];
}
public function fetch_object(array $assoc, $class_name = 'stdClass', array $params = array()) {
$properties = array();
foreach($assoc as $key => $value) {
if($property = $this->getClassProperty($class_name, $key)) {
$properties[$property] = $value;
}
}
$object = unserialize('O:'.strlen($class_name).':"'.$class_name.'"'.substr(serialize($properties), 1));
if($constructor = $this->getClassConstructor($class_name)) {
call_user_func_array(array($object, $constructor), $params);
}
return $object;
}
}
Well, you can cast an array to an object, like this:
$array = array('a' => 'a', 'b' => 'c');
$object = (object) $array;
or just:
$object = (object) array('a' => 'a', 'b' => 'c');
This will give you a stdClass object with the properties of the array.
Ok, I didn't expect this to work, but it does:
class TestObject {
public $property;
public $preset;
public function __construct($param) {
$this->property = $param;
}
}
$object = unserialize('O:10:"TestObject":1:{s:6:"preset";i:1;}');
$object->__construct(1);
print_r($object);
results:
TestObject Object
(
[property] => 1
[preset] => 1
)
I just have to check the access type of the property before creating the serialized string because the classname is prepended for private properties. However, is calling the constructor of an already constructed object expected to stay working?
If I have the following registry class:
Class registry
{
private $_vars;
public function __construct()
{
$this->_vars = array();
}
public function __set($key, $val)
{
$this->_vars[$key] = $val;
}
public function __get($key)
{
if (isset($this->_vars[$key]))
return $this->_vars[$key];
}
public function printAll()
{
print "<pre>".print_r($this->_vars,true)."</pre>";
}
}
$reg = new registry();
$reg->arr = array(1,2,3);
$reg->arr = array_merge($reg->arr,array(4));
$reg->printAll();
Would there be an easier way to push a new item onto the 'arr' array?
This code: 'array[] = item' doesn't work with the magic set method, and I couldn't find any useful info with google. Thanks for your time!
If you have:
$reg = new registry();
$reg->arr = array(1,2,3);
$reg->arr = 4;
And you're expecting:
Array
(
[arr] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
)
All you need to do is update your __set method to:
public function __set($key, $val){
if(!array_key_exists($key, $this->_vars)){
$this->_vars[$key] = array();
}
$this->_vars[$key] = array_merge($this->_vars[$key], (array)$val);
}
You need to alter the definition of __get() so that it returns by reference:
public function &__get($key) {
return $this->_vars[$key];
}