PHP SimpleXML to JSON Encode single element array - php

I am trying to parse xml with php into a single element json array.
This is what I have:
$t = array();
$a=array("url"=>$test->channel->item->link);
array_push($t,$a);
echo json_encode($t);;
Which gives me this:
[{"url":{"0":"http:www.example.com"}}]
But I am looking for this:
[{"url":"http:www.example.com"}]
It seems that $test->channel->item->link parses with curly brackets as {url}
but if I do echo $test->channel->item->link, I get: www.example.com without the curly brackets.

Not sure if this is what you're looking for, but it works :)
$xmlstr = '<?xml version=\'1.0\' standalone=\'yes\'?>
<container>
<channel>
<item>
<link>www.example.com</link>
</item>
</channel>
</container>';
$test = new SimpleXMLElement($xmlstr);
$t = array();
$a = array("url"=>$test->channel->item->link->__toString());
array_push($t,$a);
echo json_encode($t); // [{"url":"www.example.com"}]

Check out this implementation, this is working for me.
class eg{
public $link;
function __construct()
{
$this->link = "www.myweb.com";
}
}
class eg1{
public $item;
function __construct()
{
$this->item = new eg();
}
}
class eg2{
public $channel;
function __construct()
{
$this->channel = new eg1();
}
}
$test = new eg2();
$t = array();
$a=array("url"=>$test->channel->item->link);
array_push($t,$a);
echo json_encode($t);
And this will render the following string
[{"url":"www.myweb.com"}]

Related

Php create Dynamic Xml in foreach

I built an basic xml class (using simple xml), so far I built simple xml nodes.
Now I want to create a function in which generate a specific number of nodes in a foreach, which I specify in the parameter.
it looks like this now:
class Xml
{
private $data = "";
protected $xml = "";
protected $xmlManager = "";
protected $menus = "";
protected $xmlMenus = "";
public function __construct($data = [])
{
$this->data = $data;
$this->xml = new \SimpleXmlElement('<test></test>');
$this->setMenus();
return $this->xml;
}
private function setMenus()
{
$this->xmlMenus = $this->xmlManager->addChild('menus');
}
public function setMenuNode($parameter)
{
$this->data->menu = []
foreach ($this->data->menus as $menuKey => $menuValue)
{
$this->xmlMenu = $this->xmlMenus->addChild('menu');
$menueValue->addAttribute($param1, $param2, $param3);
$menueValue->addAttribute($param1, $param2, $param3);
}
}
}
Later on I want to call it like this
Xml->setMenuNode($param1, $param2, $param3);
Which should create 3 menu nodes.
My xml should look like this later on.
<?xml version="1.0"?>
<menus>
<menu id="1" entry="title">
...
</menu>
<menu id="2" entry="title2">
...
</menu>
<menu id="3" entry="title2">
...
</menu>
</menus>
</dvd>
I am not quiet sure how to manager this in a good way.
This are two jobs, so you should split it into two classes. For a loose dependency define an interface. The menu and the items need to be append itself to their parent.
I use DOM because SimpleXMLElement does not support an empty XML document. This solution would work with SimpleXML, too. But only with an additional outer XML element node.
Interface
This is a contract, so you should define an interface for it.
interface DOMAppendable {
public function appendTo(\DOMNode $parent);
}
Item Class
For a single item implement the interface:
class MenuItem implements DOMAppendable {
private $_id = '';
private $_title = '';
public function __construct($id, $title) {
$this->_id = $id;
$this->_title = $title;
}
public function appendTo(\DOMNode $parent) {
$document = $parent->ownerDocument ?: $parent;
$item = $parent->appendChild($document->createElement('menu'));
$item->setAttribute('id', $this->_id);
$item->setAttribute('title', $this->_title);
}
}
The constructor stores the data into private properties. The method from the interface appends the nodes to the provided parent. The menu items contain more data, have subitems. As long as they implement the interface it will still work.
Menu/List Class
A class for the menu itself stores added menu items into an array property. It implements the interface, too. (So it can be appended to another menu). Implementing __toString() allows to cast the menu into a string.
class Menu implements DOMAppendable {
private $_items = [];
public function add(\DOMAppendable $item) {
foreach (func_get_args() as $item) {
if (!($item instanceOf DOMAppendable)) {
throw new \InvalidArgumentException("Invalid menu item.");
}
$this->_items[] = $item;
}
}
public function appendTo(\DOMNode $parent) {
$document = $parent->ownerDocument ?: $parent;
$menu = $parent->appendChild($document->createElement('menus'));
foreach ($this->_items as $item) {
$item->appendTo($menu);
}
}
public function __toString() {
$document = new \DOMDocument();
$document->formatOutput = TRUE;
$this->appendTo($document);
return $document->saveXml();
}
}
Use
With that you can create the menu using the new classes.
$menu = new Menu();
$menu->add(
new MenuItem(1, 'title1'),
new MenuItem(2, 'title2'),
new MenuItem(3, 'title3')
);
echo $menu;
Output:
<?xml version="1.0"?>
<menus>
<menu id="1" title="title1"/>
<menu id="2" title="title2"/>
<menu id="3" title="title3"/>
</menus>
The interface allows to use different menu item classes, even a menu can be an item:
$menu = new Menu();
$menu->add(
new MenuItem(1, 'title1'),
new MenuItem(2, 'title2')
);
$subMenu = new Menu();
$subMenu->add(new MenuItem(3.1, 'title3.1'));
$menu->add($subMenu);
echo $menu;
Output:
<?xml version="1.0"?>
<menus>
<menu id="1" title="title1"/>
<menu id="2" title="title2"/>
<menus>
<menu id="3.1" title="title3.1"/>
</menus>
</menus>
PHP >= 5.6
PHP 5.6 and later support the new variadics syntax. This allows to remove the func_get_args() call and simplify the Menu::add() method.
class Menu implements DOMAppendable {
private $_items = [];
public function add(\DOMAppendable ...$items) {
foreach ($items as $item) {
$this->_items[] = $item;
}
}
...
Based on what you described you would get (PHP 5.6):
function setMenuNode(...$parameters)
{
foreach ($parameters as $parameter) {
$this->xmlMenu = $this->xmlMenus->addChild('menu');
$menueValue->addAttribute('id', $parameter);
$menueValue->addAttribute('entry', 'title' . $parameter);
}
}

Array reference in a class error in php

I have this class which populates and prints an array
<?php
class testArray
{
private $myArr;
public function __construct() {
$myArr = array();
}
public static function PopulateArr() {
$testA = new testArray();
$testA->populateProtectedArr();
return $testA;
}
protected function populateProtectedArr()
{
$this->myArr[0] = 'red';
$this->myArr[1] = 'green';
$this->myArr[2] = 'yellow';
print_r ($this->myArr);
}
public function printArr() {
echo "<br> 2nd Array";
print_r ($this->myArr);
}
}
?>
I instantiate this class from another file and try to print the array in different function.
<?php
require_once "testClass.php";
$u = new testArray();
$u->PopulateArr();
$u->printArr();
?>
I am not able to print the array in the printArr() function. I want to get reference to the array that I had set the values in .
You just missed one thing, you have to assign result of $u->PopulateArr(); to $u again, otherwise you will not get the object you created from that method call, so:
$u = new testArray();
$u = $u->PopulateArr(); // this will work
$u->printArr();
This also can be done like this:
$u = testArray::PopulateArr();
$u->printArr();
It seems that your $u object never populates the private array.
Instead you create a new object $testA and populate its array.
This might help you understanding the way
class testArray
{
private $myArr;
public function __construct() {
$this->myArr = array();
}
public static function PopulateArr() {
$testA = new testArray();
$testA->populateProtectedArr();
return $testA;
}
protected function populateProtectedArr()
{
$this->myArr[0] = 'red';
$this->myArr[1] = 'green';
$this->myArr[2] = 'yellow';
return $this->myArr;
}
public function printArr() {
echo "<br> 2nd Array";
return $this->PopulateArr();
}
}
another.php
require_once "testClass.php";
$u = new testArray();
print_r($u->PopulateArr());
print_r($u->printArr());
Here we are accessing the values of protected function PopulateArr instead of printing within function I just replaced it with return and print it over another file and within printArr function just call the PopulateArr function and that's it

Convert String To Object-Variable-Name?

i want to turn a simple string like that "response->dict->words" into a variable name that i can actually work with. I will give an example now. Lets assume the value of $response->dict->words is 67.
Example:
$var = "response->dict->words"
echo $$var; /* PRINT THE VALUE 67 FROM $response->dict->words*/
As you may notice i put an extra dollar sign before the $var because this should work, but it doesn't.
Can anyone help me with this?
class ClassOne {
public function test() {
return 'test';
}
}
class ClassTwo {
public function test2() {
return 'test2';
}
}
$one = new ClassOne();
$two = new ClassTwo();
$objects = array('one', 'two');
$methods = array('test', 'test2');
for ($i = 0; $i < count($objects); $i++) {
echo ${$objects[$i]}->$methods[$i]();
}
You can store classnames or method names as strings and later use them, or even store variable names, like here ${$objects} (variable variables), but you cannot store whole logic.
To evaluate whole logic, you have to use eval(), which is most probably bad idea
$var = "response->dict->words"
eval("?> <?php echo $".$var.";");
You can split your string and make the call as below:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = 'words test';
}
}
$response = new Response();
$var = 'response->dict->words';
$elements = explode('->', $var);
echo ${$elements[0]}->$elements[1]->$elements[2];
Results into words test
Or, if you don't know the level of nesting the object call, you can perform the call in a foreach loop. When the loop exits, the last call will be available after it:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = new stdClass();
$this->dict->words->final = 'test chained string';
}
}
$response = new Response();
$var = 'response->dict->words->final';
$elements = explode('->', $var);
foreach ($elements as $key => $element) {
if ($key == 0) {
$call = ${$element};
continue;
}
$call = $call->$element;
}
echo $call;
Results into: test chained string
There is a better way, why don't you cache the variable like
$var = $response->dict->words;

PHP - XML Parsing (Trying to get property of non-object)

Good morning!
I try to describe my problem: I have a language class and in the class there is a function Text(category, item).
so I can call the function like:
Language::Text("indexPage", "hello_world");
And the output would be:
Hello World!
The Values of the items are in a xml file.
Structure of my XML file:
<?xml version="1.0" encoding="UTF-8"?>
<languageBase>
<language filename="en" language="English">
<category name="indexPage">
<item name="hello_world"><![CDATA[Hello World!]]></item>
</category>
</language>
</languageBase>
But if I use the function Text(..), I get an error:
Trying to get property of non-object in
C:\xampp\htdocs\Mvc\system\classes\class.language.php on line 20
This is my class:
<?php
if (!defined("OK")) {
header("Location: ../");
}
class Language {
private static $_language = "de";
public static function params($string) {
$string = str_replace("%USERNAME%", User::GetUsername(User::$_id, 1), $string);
$string = str_replace("%#USERNAME%", User::GetUsername(User::$_id), $string);
return $string;
}
public static function Text($category, $item) {
$xml = new SimpleXMLElement(file_get_contents(self::GetPath()));
$category = "indexPage";
$item = "hello_world";
$obj = $xml->xpath("/languageBase/language/category[#name=\"{$category}\"]/item[#name=\"{$item}\"]");
return $obj->entry;
}
public static function Set($language) {
$_SESSION[SESSION_LANGUAGE] = $language;
}
public static function Get() {
if(isset($_SESSION[SESSION_LANGUAGE])) {
if(file_exists("system/lang/".$_SESSION[SESSION_LANGUAGE].".xml")) {
self::$_language = $_SESSION[SESSION_LANGUAGE];
}
} else {
$_SESSION[SESSION_LANGUAGE] = self::$_language;
}
return self::$_language;
}
public static function GetPath() {
return "system/lang/".self::Get().".xml";
}
}
You never define that you want to access the attribute called "name". There could be any number of other attributes like "id".
What you could do is use xpath like this:
public static function Text($category, $item) {
$xml = new SimpleXMLElement(file_get_contents(self::GetPath()));
$items = $xml->xpath("//category[#name='$category']/item[#name='$item']");
if ($items[0]) {
return (string)$items[0];
}
return null;
}
With this solution it is important to make sure that $category and $item never contains xpath control characters, i.e. $item = 'my[item]' would produce errors.
In that case, you would have to escape $item and $category somehow.
xpath is not always very straight forward to understand. A nice resource is: http://www.w3schools.com/xpath/xpath_syntax.asp

Build XML file from class in PHP

I'm wondering if anyone knows how to build an XML file, dynamically from a list of classes?
The classes contains public variables, and look like this.
class node {
public $elementname;
}
I do note that some of the classes are named the same as variables, and in this case, the node would be a subnode element of a node:
class data {
public $dataaset;
}
class dataset {
public $datasetint;
}
would be:
<data>
<dataset>datasetint</dataset>
</data>
Maybe something in SimpleXML or something?
The only solution i can think of linking 2 or more unrelated class is using Annotations.
Annotations is not supported by default in PHP but currently in RFC (Request for Comments: Class Metadata) but bending the time is supported or rejected you can create yours using ReflectionClass & Comments functionality
Example If you have 3 classes like this
class Data {
/**
*
* #var Cleaner
*/
public $a;
/**
*
* #var Extraset
*/
public $b;
public $runMe;
function __construct() {
$this->runMe = new stdClass();
$this->runMe->action = "RUN";
$this->runMe->name = "ME";
}
}
class Cleaner {
public $varInt = 2;
public $varWelcome = "Hello World";
/**
*
* #var Extraset
*/
public $extra;
}
class Extraset {
public $boo = "This is Crazy";
public $far = array(1,2,3);
}
Then you can run a code like this
$class = "Data";
$xml = new SimpleXMLElement("<$class />");
getVariablesXML($class, $xml);
header("Content-Type: text/xml");
$xml->asXML('data.xml');
echo $xml->asXML();
Output
<?xml version="1.0"?>
<Data>
<Cleaner name="a">
<varInt type="integer">2</varInt>
<varWelcome type="string">Hello World</varWelcome>
<Extraset name="extra">
<boo type="string">This is Crazy</boo>
<far type="serialized">a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}</far>
</Extraset>
</Cleaner>
<Extraset name="b">
<boo type="string">This is Crazy</boo>
<far type="serialized">a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}</far>
</Extraset>
<runMe type="serialized">O:8:"stdClass":2:{s:6:"action";s:3:"RUN";s:4:"name";s:2:"ME";}</runMe>
</Data>
Function Used
function getVariablesXML($class, SimpleXMLElement $xml) {
$reflect = new ReflectionClass($class);
foreach ( $reflect->getProperties(ReflectionProperty::IS_PUBLIC) as $property ) {
$propertyReflect = $reflect->getProperty($property->getName());
preg_match("/\#var (.*)/", $propertyReflect->getDocComment(), $match);
$match and $match = trim($match[1]);
if (empty($match)) {
$value = $property->getValue(new $class());
if (is_object($value) || is_array($value)) {
$type = "serialized";
$value = serialize($value);
} else {
$type = gettype($value);
}
$child = $xml->addChild($property->getName(), $value);
$child->addAttribute("type", $type);
} else {
$child = $xml->addChild($match);
$child->addAttribute("name", $property->getName());
if (class_exists($match)) {
getVariablesXML($match, $child);
}
}
}
}

Categories