I have a PHP class I would like to transform in a JSON on several levels like this type:
{"interface":{"Version":"0"},"Container":[{"id":"1","Element":[{"text":"Test","id":"0"},{"text":"Toto","id":"1"}]}]}
In my PHP class I have a function who returns the JSON of my private attributes who are arrays:
return (json_encode((get_object_vars($this)), JSON_UNESCAPED_UNICODE));
Private attributes of my class:
private $interface = '';
private $Container = array(array('id' => '1'));
private $Element = array('text' => 'Test', 'id' => '0');
Do you know how I could have a JSON like above ?
In pleasure to read you.
Not sure about your class members but as long as they are accessible you can generate the JSON string. Below if the example of this
$Contenant = array(array('id' => '1'));
$Element = array('text' => 'Test', 'id' => '0');
$json = json_encode(array('inerrface'=>array(
'content'=>$Contenant,
"Element"=>$Element
)
)
);
echo $json ;
You could implement the IteratorAggregate interface like in the following example
class YourClass implements IteratorAggregate {
protected $member1 = array();
protected $member2 = array();
...
public function getIterator() {
$tmpArr = array();
// create the structure you want in $tmpArr
return new ArrayIterator($tmpArr);
}
}
$myClass = new MyClass();
$iterator = $myClass->getIterator();
$encodedData = json_encode($iterator);
As of PHP5.4 you have the JsonSerializable interface ready to use. With this interface, you cann use direct modifications like in the example given in: http://de2.php.net/manual/en/jsonserializable.jsonserialize.php
have fun! ;)
This will get you started, take a look at the constructor what data input requires. You could make a private function doing the same thing, assigning your values to the structure and then printing it:
class Test{
private $data;
public function __construct($version = 0, $records = array()){
$data['interface'] = array('Version' => $version);
$data['Container'] = array();
for ($i = 0; $i < count($records); $i++) {
$data['Container'][$i] = $records[$i];
}
// your test input
print_r(json_decode('{"interface":{"Version":"0"},"Container":[{"id":"1","Element":[{"text":"Test","id":"0"},{"text":"Toto","id":"1"}]}]}',true));
// actual input
print_r($data);
// printing our actual data as json string
echo json_encode($data);
}
public function __destruct(){
}
}
$element1 = array('text' => 'Test', 'id' => 0);
$element2 = array('text' => 'Toto', 'id' => 1);
$elements = array($element1, $element2);
$record = array('id' => 1, 'element' => $elements);
$records = array($record);
new Test(0, $records);
You need to structure it, working example: example
class test {
private $interface = '';
private $Contenant = array(array('id' => '1'));
//private $Element = array(array('text' => 'Test', 'id' => '0'));
public function json(){
$this->Contenant = array(
array('id' => '1',
'Element' => array(array('text' => 'Test', 'id' => '0'))
),
);
return json_encode((get_object_vars($this)), JSON_UNESCAPED_UNICODE);
}
}
$t = new test();
$encode = $t->json();
echo $encode;
OUTPUT
{"interface":"","Contenant":[{"id":"1","Element":[{"text":"Test","id":"0"}]}]}
Related
I need the output in following format:
<ns1:CustomerDetails>
<_cd:Name>My Name</_cd:Name>
<_pd:PersonalDetails>
<_bio:Age>20</_bio:Age>
</_pd:PersonalDetails>
<_cd:SomeVar>Var Value</_cd:SomeVar>
</ns1:CustomerDetails>
I can create an array in any format, For example:
$vars['ns1']['CustomerDetails']['_cd']['Name']="MY Name";
$vars['ns1']['CustomerDetails']['_pd']['PersonalDetails']['_bio']['Age']="20";
$vars['ns1']['CustomerDetails']['_cd']['SomeVars']="Var Value";
I have used the following code:
class ArrToXml{
static function parse($arr){
$dom = new DOMDocument('1.0');
self::recursiveParser($dom,$arr,$dom);
return $dom->saveXML();
}
private static function recursiveParser(&$root, $arr, &$dom){
foreach($arr as $key => $item){
if(is_array($item) && !is_numeric($key)){
$node = $dom->createElement($key);
self::recursiveParser($node,$item,$dom);
$root->appendChild($node);
}elseif(is_array($item) && is_numeric($key)){
self::recursiveParser($root,$item,$dom);
}else{
$node = $dom->createElement($key, $item);
$root->appendChild($node);
}
}
}
}
$xmlRequest = ArrToXml::parse($requestArray);
... But I am not getting namespace values. What changes I should do in the code to get my desired values?
The data array does not allow to differentiate between the namespace indicators and the tag names. Both are just keys. One solution would be to use QCNames as keys:
$data = [
'c:CustomerDetails' => [
'cd:Name' => "MY Name",
'pd:PersonalDetails' => [
'bio:Age' => "20"
],
'cd:SomeVars' => "Var Value",
]
];
Additionally you would need namespace information for the indicators:
$xmlns = [
'c' => ['prefix' => 'ns1', 'uri' => 'urn:c'],
'cd' => ['prefix' => '_cd', 'uri' => 'urn:cd'],
'pd' => ['prefix' => '_pd', 'uri' => 'urn:pd'],
'bio' => ['prefix' => '_bio', 'uri' => 'urn:bio']
];
I suggest including a prefix to decouple the code.
Appending the data directly to the DOM is a better, less complex solution most of the time. I typically define an interface for this and implement it in the different data and serializer classes:
interface XMLAppendable {
public function appendTo(DOMNode $parentNode);
}
A mapper for the data array could implement the interface as well. It would need the data
array and information about the namespaces.
class ArrayAppender implements XMLAppendable {
private $_data;
private $_xmlns;
public function __construct(array $data, array $xmlns = []) {
$this->_data = $data;
$this->_xmlns = $xmlns;
}
public function appendTo(DOMNode $parentNode) {
$this->appendArrayTo($parentNode, $this->_data);
}
private function appendArrayTo(DOMNode $parentNode, array $data) {
$document = $parentNode instanceof DOMDocument
? $parentNode : $parentNode->ownerDocument;
foreach ($data as $key => $value) {
if (FALSE !== strpos($key, ':')) {
[$prefix, $tagName] = explode(':', $key);
if (isset($this->_xmlns[$prefix])) {
$namespace = $this->_xmlns[$prefix];
} else {
throw new UnexpectedValueException(
'Can not find namespace for '.$prefix
);
}
} else {
$namespace = ['prefix' => '', 'uri' => ''];
$tagName = $key;
}
$parentNode->appendChild(
$child = $document->createElementNS(
$namespace['uri'],
$namespace['prefix'] ? $namespace['prefix'].':'.$tagName : $tagName
)
);
if (is_array($value)) {
$this->appendArrayTo($child, $value);
} else {
$child->textContent = (string)$value;
}
}
}
}
Usage demo:
$document = new DOMDocument();
$arrayToXML = new ArrayAppender($data, $xmlns);
$arrayToXML->appendTo($document);
$document->formatOutput = TRUE;
echo $document->saveXML();
Output:
<?xml version="1.0"?>
<ns1:CustomerDetails xmlns:ns1="urn:c">
<_cd:Name xmlns:_cd="urn:cd">MY Name</_cd:Name>
<_pd:PersonalDetails xmlns:_pd="urn:pd">
<_bio:Age xmlns:_bio="urn:bio">20</_bio:Age>
</_pd:PersonalDetails>
<_cd:SomeVars xmlns:_cd="urn:cd">Var Value</_cd:SomeVars>
</ns1:CustomerDetails>
can someone help me with mocking ZF1 fetchAll() method, it returns Zend_Db_Table_Rowset object
So tried to do mock in this way, here is my code.
$rowSet = $this->getMockBuilder(Zend_Db_Table_Rowset::class)
->disableOriginalConstructor()
->getMock();
and set private property tru reflaction class
$dataArray = [['id' => 1, 'name' => 'test_name'], ['id' => 2, 'name'=>'test2']];
$this->setProtectedFieldValue($rowSet, '_data', $dataArray);
protected function setProtectedFieldValue($entity, string $propertyName, $value) : void
{
$class = new ReflectionClass($entity);
$property = $class->getProperty($propertyName);
$property->setAccessible(true);
$property->setValue($entity, $value);
}
at the end in won't enter $rows set loop, i don't have idea how to loop or set data in this Zend_Db_Table_Rowset object.
$rows = $table->fetchAll($tableSelect);
foreach ($rows as $row) {
Small update i need something like this
https://hermanradtke.com/2010/02/08/mocking-zend-frameworks-row-and-rowset-objects.html, but in my version that My_ZendDbTable_Row_TestMockRow class doesn't exist
array(
'first_name' => 'Herman',
'last_name' => 'Radtke',
'email' => 'herman#example.com'
)
);
$row = new My_ZendDbTable_Row_TestMockRow($data);
$this->assertEquals('Herman', $row->first_name);
$this->assertEquals('Radtke', $row->last_name);
$this->assertEquals('herman#example.com', $row->email);
}
}
I know this question is more data structures but since I am doing it in Symfony there might be a simpler way. I have a recursive function treeBuilder() I want to call on some data to create a hierarchy. Say a database of people and I want to create a tree structure if they live with their parents. I know I am passing an array of object to the function but it needs to be an array. I am pretty sure I need to rewrite this function so that it handles the the array of object but am stumped. I am not sure how to access the elements of the array to check the parentid. I know the code below is not correct but that is where I am at now.
Controller:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CompanyMyBundle:Org')->findAll();
var_dump($entities);
$tree=$this->treeBuilder($entities);
return array(
'entities' => $tree,
);
}
private function treeBuilder($ar, $pid=null)
{
$op=array();
foreach( $ar as $item ) {
// I know I have an array of objects
if( $item['ParentId'] == $pid ) {
$op[$item['Id']] = array(
'Street' => $item['Street'],
'ParentId' => $item['ParentId']
);
$children = self::treeBuilder( $ar, $item['Id'] );
if( $children ) {
$op[$item['Id']]['children'] = $children;
}
}
}
return $op;
}
var_dump($entities) from indexAction():
/export/www/working/symfony/src/Company/MyBundle/Controller/DepController.php:34:
array (size=60)
0 =>
object(Company\MyBundle\Entity\Org)[1556]
private 'Name' => string 'Me' (length=46)
private 'Street' => string '123 Sesame' (length=255)
private 'City' => string 'Myhometown' (length=255)
private 'ParentId' => int 0
private 'Id' => int 1
1 =>
object(Company\MyBundle\Entity\Org)[1557]
private 'Name' => string 'Me2' (length=46)
private 'Street' => string '123 Sesame' (length=255)
private 'City' => string 'Myhometown' (length=255)
private 'ParentId' => int 1
private 'Id' => int 2
If you need to get entities as arrays instead of objects, you would need to use Doctrine's hydrator:
$em = $this->getDoctrine()->getManager();
$orgRepo = $em->getRepository('CompanyMyBundle:Org');
$entities = $orgRepo->createQueryBuilder('org')
->getQuery()
->getResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
Note:
I would suggest to leave entities as objects and use getters:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CompanyMyBundle:Org')->findAll();
$tree = $this->treeBuilder($entities);
return array(
'entities' => $tree,
);
}
private function treeBuilder($entities, $pid = null)
{
$op = array();
/** Org $entity */ //Type hinting, if you use autocompletion
foreach ($entities as $entity) {
if ($entity->getParentId() == $pid) {
$op[$entity->getId()] = [
'Street' => $entity->getStreet(),
'ParentId' => $entity->getParentId()
];
$children = self::treeBuilder($entities, $entity->getId());
if (!empty($children)) {
$op[$entity->geId()]['children'] = $children;
}
}
}
return $op;
}
i'm trying to refresh my memory of OO & array structure. i have,
class room{
private $people = array(
'name' => array(
'height' => null,
'age' => null
)
);
function set($list){
foreach($list as $person){
$this->people[$person['name']]['height'] = $person['height'];
$this->people[$person['name']]['age'] = $person['age'];
}
}
function print(){
foreach($this->people as $k => $v){
echo $k . "<br>";
echo $v['height'] . ":" . $v['age'] . "<br><br>";
}
}
}
$input = array( array('name' => 'John', 'height' => '6.4', 'age' => '20'),
array('name' => 'Jane', 'height' => '5.2', 'age' => '21')
);
$i = new room;
$i->set($input);
$i->print();
the output is,
name
:
John
6.4:20
Jane
5.2:21
i'm confused as why name : appears first, when the input array only contains 2 values of each person. i am unsure if i am using my arrays correctly, could someone point out my mistake?
My overall aim of this is to have correct understanding of arrays within arrays & how to best set & get the values
It's because you've initialised the $people array to contain those values
private $people = array(
'name' => array(
'height' => null,
'age' => null
)
);
Change it to:
private $people = array();
that's the good way to do it
your people class
class people {
//properties
private $name;
private $height;
private $age;
//setters
public function setName($name) {
$this->name = $name;
}
public function setHeight($height) {
$this->height = $height;
}
public function setAge($age) {
$this->age = $age;
}
//getters
public function getName() {
return $this->name;
}
public function getHeight() {
return $this->height;
}
public function getAge() {
return $this->age;
}
}
your room class
class room {
//properties
private $people = array();
//setters
public function setPeople($people) {
$this->people[] = $people;
}
//getters
public function getPeoples() {
return $this->people;
}
}
and how to control it in OOP
$people1 = new people();
$people1->setName('John');
$people1->setHeight('6.4');
$people1->setAge('20');
$people2 = new people();
$people2->setName('Jane');
$people2->setHeight('5.2');
$people2->setAge('21');
$room = new room();
$room->setPeople($people1);
$room->setPeople($people2);
// Removing people array initial data will solve the issue :)
class room{
private $people = array();
function set($list){
foreach($list as $person){
$this->people[$person['name']]['height'] = $person['height'];
$this->people[$person['name']]['age'] = $person['age'];
}
}
function print(){
foreach($this->people as $k => $v){
echo $k . "<br>";
echo $v['height'] . ":" . $v['age'] . "<br><br>";
}
}
}
$input = array( array('name' => 'John', 'height' => '6.4', 'age' => '20'),
array('name' => 'Jane', 'height' => '5.2', 'age' => '21')
);
$i = new room;
$i->set($input);
$i->print();
I have a following class hierarchy, which shown in a reproduction script below:
<?php
header('Content-Type: text/plain');
class A
{
public $config = array(
'param1' => 1,
'param2' => 2
);
public function __construct(array $config = null){
$this->config = (object)(empty($config) ? $this->config : array_merge($this->config, $config));
}
}
class B extends A
{
public $config = array(
'param3' => 1
);
public function __construct(array $config = null){
parent::__construct($config);
// other actions
}
}
$test = new B();
var_dump($test);
?>
Output:
object(B)#1 (1) {
["config"]=>
object(stdClass)#2 (1) {
["param3"]=>
int(1)
}
}
What I wanted, is that A::$config not be overriden by B::$config. There might be a lot of descendant classes from B, where I would like to change $config, but I need that those $config values to merge / overwrite if match $config values of all it's parents.
Q: How can I do that ?
I've tried to use array_merge() but in non-static mode those variables just override themselves. Is there a way to achieve merge effect for class tree without static (late static binding) ?
Instead of declaring a $config property with values that you're going to change in the constructor, it's better to declare those values as default values. This is also described in Orangepill's answer:
class A
{
public $config;
private $defaults = array(
'param1' => 1,
'param2' => 2,
);
public function __construct(array $config = array())
{
$this->config = (object)($config + $this->defaults);
}
}
A few twists there; by declaring the default value of the $config constructor argument as an empty array, you can simplify your code by using array operators like I did above. Undefined keys in $config are filled in by $this->defaults.
The extended class will look very similar:
class B extends A
{
private $defaults = array(
'param3' => 1
);
public function __construct(array $config = array())
{
parent::__construct($config + $this->defaults);
}
}
You can restructure how your extended class is instantiated
class B extends A
{
private $defaults = array('param3' => 1);
public function __construct(array $config = null){
parent::__construct($config?array_merge($this->defaults, $config):$this->defaults);
}
}
You can do that using ReflectionClass. Start by introspecting $this, use getProperty(), then use getParentClass() to do the same on the parent class and its parent etc. and merge the resulting arrays together.
This is probably not the best solution for the problem you're facing though.
I believe the following is what you are looking for. Adapted from Inherit static properties in subclass without redeclaration?
<?php
class MyParent {
public static $config = array('a' => 1, 'b' => 2);
public static function getConfig() {
$ret = array();
$c = get_called_class();
do {
$ret = array_merge($c::$config, $ret);
} while(($c = get_parent_class($c)) !== false);
return $ret;
}
}
class MyChild extends MyParent {
public static $config = array('a' => 5, 'c' => 3, 'd' => 4);
public function myMethod($config) {
$config = array_merge(self::getConfig(), $config);
}
}
class SubChild extends MyChild {
public static $config = array('e' => 7);
}
var_export(MyChild::getConfig());
// result: array ( 'a' => 5, 'b' => 2, 'c' => 3, 'd' => 4, )
$mc = new MyChild();
var_export($mc->myMethod(array('b' => 6)));
// result: array ( 'a' => 5, 'b' => 6, 'c' => 3, 'd' => 4, )
var_export(SubChild::getConfig());
// result: array ( 'a' => 5, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 7, )