I have a class called the 'analyst' and he has many 'analysers'. The analysers go looking for certain patterns in a given input.
For now, I create the instances of the 'analysers' in the constructor function of the anaylser like this:
<?php
class analyser {
protected $analysers = array();
public function __construct() {
$analyser1 = new Analyser1();
$this->analysers[] = $analyser1;
$analyser2 = new Analyser1();
$this->analysers[] = $analyser2;
...
}
This works for a limited amount of analysers but I want to create an array ('analyser1', 'analyser2', ...) that will be used in a loop to create all the needed instances. Is this possible or do I need to create every instance manually?
public function __construct() {
foreach ($names as $analyser){
$instance = new $analyser();
$this->analysers[] = $instance;
}
How can i do this ?
This should be straightforward - furthermore you could use nested associative array data to add some initial properties to each object, for example, you might want to give each object a name property:
<?php
class Analyser1
{
protected $name;
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}
$analyserData = [
['name' => 'analyser1'],
['name' => 'analyser2'],
['name' => 'analyser3'],
];
$analysers = [];
foreach ($analyserData as $data) {
$obj = new Analyser1();
$name = $data['name'];
$obj->setName($name);
$analysers[$name] = $obj;
echo 'created ' . $obj->getName() . PHP_EOL;
}
print_r($analysers);
Yields:
created analyser1
created analyser2
created analyser3
Array
(
[analyser1] => Analyser1 Object
(
[name:protected] => analyser1
)
[analyser2] => Analyser1 Object
(
[name:protected] => analyser2
)
[analyser3] => Analyser1 Object
(
[name:protected] => analyser3
)
)
Example here: https://eval.in/133225
Hope this helps! :)
You can use this simple code:
public function __construct() {
for ($i=1; $i<=10; $i++){
// if you want associative array, you should use this part
$key = 'analyser' . $i;
$this->analysers[$key] = new Analyser1();
}
}
And it would create 10 instances of class Analyser1 in array $this->analysers
Related
i have a object like this:
CORE::$ObjClassInstABS['ABS']['DATA']
that contains an array of Class->Method:
array (
'DATA' =>
array (
'USERDATAMANAGER' =>
Class_UserdataManager::__set_state(array(
)),
'PRODDATAMANAGER' =>
Class_ProddataManager::__set_state(array(
)),
),
)
i create a new object, of type class Like this:
CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER'] = new class;
i cant but need pass all the methods of the first object, ignoring the class of origin to the class i create on fly, and that allows me to execute the functions from the class declared on the fly.
does this exist in php 7.0 or is there any way to achieve this reach??
It would be like cloning the methods of several classes to a single and new class.
Answer for #Damian Dziaduch comments
the piece of code that i used to Dynamically Instance all class file from a directory is this, and populate the first object with instance of class:
CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER']= new class;
foreach (CORE::$ObjClassABS['DATA'] as $key => $name) {
if (strpos($name, 'class.') !== false) {
$name = basename($name);
$name = preg_replace('#\.php#', '', $name);
$names = explode(".", $name);
foreach ($names as $key => $namesr) {
$names[$key] = ucfirst(strtolower($namesr));
}
$name = implode('_', $names);
$NamesClass = $name . 'Manager';
$InstanceClass = strtoupper(preg_replace('#\Class_#', '', $NamesClass));
CORE::$ObjClassInstABS['ABS']['DATA'][$InstanceClass] = $this->$InstanceClass = new $NamesClass();
}
}
the result of it is the Array printed at start of the post CORE::$ObjClassInstABS['ABS']['DATA'] .
if you see at start of foreach i have the new class declaration to use, in loop, how can i populate CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER'] in the loop, it with all methods of the first object instance, and make it executables?
that i whant (not work):
foreach ( CORE::$ObjClassInstABS['ABS']['DATA'] as $key => $value ) {
CORE::$ObjClassInstABS['ABS']['ABSDATAMANAGER'] .= Clone($value);
}
$value represent where is storing the methods:
::__set_state(array()),
As requested.
Not sure whether this will fill you requirements... The question is whether you are able to overwrite the CORE::$ObjClassInstABS
<?php
CORE::$ObjClassInstABS = new class extends \ArrayIterator {
private $container = [];
public function __construct(array $container)
{
$this->container = [
'ABS' => [
'DATA' => [
'USERDATAMANAGER' => new class {},
'PRODDATAMANAGER' => new class {},
],
],
];
}
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
public function offsetGet($offset)
{
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetUnset($offset)
{
unset($this->container[$offset]);
}
};
Basically I was encoding a response with json and couldn't figure out why it kept returning the right number of array members but they were empty.
$app->get('/api/server_list', function ($request, $response, $args) {
$serverlist = new ServerListing($this->db);
$servers = $serverlist->getServers();
$newResponse = $response->withJson($servers);
return $newResponse;
});
This is the output of the above with an added print_r($servers)
[{},{}]Array
(
[0] => ServerEntity Object
(
[id:protected] => 1
[serverName:protected] => dc1.domain.com
)
[1] => ServerEntity Object
(
[id:protected] => 2
[serverName:protected] => dc2.domain.com
)
)
Here is the class code for ServerListing:
<?php
class ServerListing extends Listing
{
public function getServers() {
$sql = "SELECT * from servers";
$stmt = $this->db->query($sql);
$results = [];
while($row = $stmt->fetch()) {
$results[] = new ServerEntity($row);
}
return $results;
}
}
Here is ServerEntity:
<?php
class ServerEntity
{
public $id;
public $serverName;
public function __construct(array $data) {
if(isset($data['id'])) {
$this->id = $data['id'];
}
$this->serverName = $data['name'];
}
public function getId() {
return $this->id;
}
public function getServerName() {
return $this->serverName;
}
}
Only way it works is with public.
I understand public/private/protected. Though this is my first time with a framework and Object Oriented php.
Using the same database call in another route I can then pass the server list to a view and it works fine.
So I guess two questions.
Why does the json encode fail?
Am I doing something fundamentally wrong/ is there a better way to do this?
Slim's Response::withJson() doesn't do anything magic. It relies on the PHP function json_encode() to do the encoding. json_encode() also doesn't know any special trick. If you pass an object to it to encode it gets all the data it can get from it. And that means only its public properties because, well, this is how OOP works.
However, if you implement the JsonSerializable interface in a class then you can control what data is available to json_encode() when it comes to encode an object of that class.
For example:
class ServerEntity implements JsonSerializable
{
private $id;
private $serverName;
// ... your existing code here
public function jsonSerialize()
{
return array(
'id' => $this->id,
'name' => $this->serverName,
);
}
}
Some test code:
echo(json_encode(new ServerEntity(array('id' => 7, 'name' => 'foo'))));
The output is:
{"id":7,"name":"foo"}
In short, an object can be converted into an array.
The object's public properties will be used as $key => $value pairs in the array.
Since the properties are protected, the values are not included.
While it would seem logical that the array actually be empty, the process in which PHP converts the object to an array is not really documented well enough.
In practice what I would recommend is you create a public method that converts the Object to an array.
class ServerEntity {
//...
public function toArray() {
return array("id" => $this->id, "name" => $this->name);
}
//...
}
Then you may simply do...
$app->get('/api/server_list', function ($request, $response, $args) {
$serverlist = new ServerListing($this->db);
$servers = $serverlist->getServers();
$objects = array();
foreach ($servers as $server) {
$objects[] = $server->toArray();
}
$newResponse = $response->withJson($objects);
return $newResponse;
});
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
(
)
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];
}
I can't use simply get_class_vars() because I need it to work with PHP version earlier than 5.0.3 (see http://pl.php.net/get_class_vars Changelog)
Alternatively: How can I check if property is public?
This is possible by using reflection.
<?php
class Foo {
public $alpha = 1;
protected $beta = 2;
private $gamma = 3;
}
$ref = new ReflectionClass('Foo');
print_r($ref->getProperties(ReflectionProperty::IS_PUBLIC));
the result is:
Array
(
[0] => ReflectionProperty Object
(
[name] => alpha
[class] => Foo
)
)
Or you can do this:
$getPublicProperties = create_function('$object', 'return get_object_vars($object);');
var_dump($getPublicProperties($this));
You can make your class implement the IteratorAggregate interface
class Test implements IteratorAggregate
{
public PublicVar01 = "Value01";
public PublicVar02 = "Value02";
protected ProtectedVar;
private PrivateVar;
public function getIterator()
{
return new ArrayIterator($this);
}
}
$t = new Test()
foreach ($t as $key => $value)
{
echo $key." = ".$value."<br>";
}
This will output:
PublicVar01 = Value01
PublicVar02 = Value02