I am building a custom data handler to save some precious bandwidth for my users.
Problem is, I am trying to add an object on the fly, into an array.
It is not working as expected.
Any help would be appreciated
function getName() {
return "test";
}
class Parser {
private $results = [];
public function parseCalls($callArray){
for ($i=0; $i < count($callArray); $i++) {
switch($callArray[$i]->call){
case 'getName':
$results[] = (object) ['result' => getName()]; // this is where it fails.
break;
}
}
}
public function sendResult(){
echo json_encode($this->results);
}
}
$parser = new Parser();
if(isset($_POST["callArray"])){
// $_POST['callArray'] contains [{call: 'getName'}];
$callsArray = json_decode($_POST["callArray"]);
$parser->parseCalls($callsArray);
}
$parser->sendResult();
How do I make it that the results array contains something like this:
[{result: "test"}]
Thank you very much! :)
You appear to now have solved that little conundrum but I wrote this and spotted the problem whilst doing so.
public function parseCalls( $calls=array() ){
foreach( $calls as $i => $call ){
try{
switch( strtolower( $call ) ){
case 'getname':
$this->results[]=(object)array( 'result'=>getName() );
break;
}
}catch( Exception $e ){
die( $e->getMessage() );
}
}
}
Related
Is there any solution to download STANDARD-XML metadata from RETS using PHRETS?
Currently am able to extract each class metadata as an array using PHRETS function GetMetadataTable and combining & converting to XML format.
But then recently I found difference in single STANDARD-XML metadata(of entire resources and classes) and individual class metadata. Using metadata viewer service RETSMD.com(built on PHRETS) also, the class name getting from STANDARD-XML metadata is different and unable to view the details.
Note: I got the STANDARD-XML metadata via direct browser log-in using credentials, like this
http://rets.login.url/GetMetadata?Type=METADATA-TABLE&Format=STANDARD-XML&ID=0
Anyone faced the same? Is there any solution using PHP?
Thanks in Advance!
I got a solution by modifying PHRETS library.
Added a new function there with following code,
if (empty($this->capability_url['GetMetadata'])) {
die("GetServerInformation() called but unable to find GetMetadata location. Failed login?\n");
}
$optional_params['Type'] = 'METADATA-SYSTEM';
$optional_params['ID'] = '*';
$optional_params['Format'] = 'STANDARD-XML';
//request server information
$result = $this->RETSRequest($this->capability_url['GetMetadata'], $optional_params );
if (!$result) {
return false;
}
list($headers, $body) = $result;
$xml = $this->ParseXMLResponse($body);
Note: Main thing to note is,
$optional_params['ID'] = '*';
Should be '*' instead '0'
If anyone is still unable to retrieve STANDARD-XML data from the CREA DDF data feed using PhRETS v2.x.x, I created a fork to the ./src/Parsers/Search/OneX.php file. You can add the following protected methods to the end of the file:
protected function parseDDFStandardXMLData(&$xml)
{
// we can only work with an array
$property_details = json_decode(json_encode($xml), true);
$retn = array();
if(! empty($property_details['RETS-RESPONSE']['PropertyDetails'])) {
foreach($property_details['RETS-RESPONSE']['PropertyDetails'] as $property_array) {
$retn[] = $this->parseArrayElements(null, $property_array);
}
}
return $retn;
}
protected function parseArrayElements($parent_key, $element)
{
// three possible $element types
// 1. scalar value
// 2. sub-array
// 3. SimpleXMLElement Object
$retn = array();
if(is_object($element)) {
$element = json_decode(json_encode($element), true);
}
if(is_array($element)) {
foreach($element as $node_key => $node) {
$key = $node_key;
if(! empty($parent_key)) {
$key = $parent_key . '|' . $key;
}
if(is_array($node) || is_object($node)) {
$nodes = $this->parseArrayElements($key, $node);
if(!empty($nodes)) {
foreach($nodes as $k => $n) {
$retn[$k] = $n;
}
}
}else{
$retn[$key] = $node;
}
}
}else{
$retn[$parent_key] = $element;
}
return $retn;
}
protected function parseRecordFromArray(&$array, Results $rs)
{
$r = new Record;
foreach($rs->getHeaders() as $key => $name) {
$r->set($name, $array[$name]);
}
return $r;
}
Then replace the parseRecords() method with:
protected function parseRecords(Session $rets, &$xml, $parameters, Results $rs)
{
if (isset($xml->DATA)) {
foreach ($xml->DATA as $line) {
$rs->addRecord($this->parseRecordFromLine($rets, $xml, $parameters, $line, $rs));
}
}elseif (isset($xml->{"RETS-RESPONSE"}->PropertyDetails)) {
$data = $this->parseDDFStandardXMLData($xml);
if(! empty($data)) {
$fields_saved = false;
foreach ($data as $line) {
if(!$fields_saved) {
$rs->setHeaders(array_keys($line));
}
$rs->addRecord($this->parseRecordFromArray($line, $rs));
}
}
}
}
The line, }elseif (isset($xml->{"RETS-RESPONSE"}->PropertyDetails)) { in the latter method does the trick to identify the STANDARD-XML RETS-RESPONSE node and parse the data.
Hope this helps,
Cheers!
Looking for best way to accomplish below. I want to return TRUE if ANYTHING is empty in my user object, I don't care which one is empty. I think I've done this before with searching for empty values in an array but can't think of the best way to do this with objects.
I know I can loop thru the object and break once I find an empty value in the object, but checking first to see if there is a better way of doing this, thanks!
function is_it_empty($user)
{
$is_it_empty = FALSE;
if( isset($user->first_name) )
$is_it_empty = TRUE;
if( isset($user->last_name) )
$is_it_empty = TRUE;
return $is_it_empty;
}
also:
function name($user)
{
foreach($user as $u):
if( isset ($user->value) ): // or should it be isset?
return true;
break;
endif;
endforeach;
return true;
}
$user = new stdClass();
$user->firstName = 'Mark';
$user->lastName = 'Baker';
$user->sanity = NULL;
function is_it_empty($user) {
foreach($user as $property) {
if (empty($property)) {
return true;
}
}
return false;
}
var_dump(is_it_empty($user));
u can use foreach. it's better than ur code.
function is_it_empty($user)
{
foreach($user as $key=>$value)
{
if( empty($value) ){
return = TRUE;
}
}
}
In case any one ever happens to care about encapsulation, this code might come in handly. The first function inspect public attributes only.
function is_prop_empty( $object ) {
$reflect = new ReflectionClass($object);
$props = $reflect->getProperties();
foreach ($props as $prop) {
try {
$result = $prop->getvalue( $object );
if( $result === null ) {
return true;
}
} catch( Exception $e ) {
}
}
return false;
}
var_dump( is_prop_empty($user) );
And with this second one, you can look into private and protected properties:
function is_prop_empty( $object ) {
$reflect = new ReflectionClass($object);
$props = $reflect->getProperties();
foreach ($props as $prop) {
$prop->setAccessible(true);
$result = $prop->getvalue( $object );
if( $result === null ) {
return true;
}
}
return false;
}
EDIT
By empty, i assumed you meant null but you can modify the function(s) above to meet your requirements of "empty".
I think having this generic function to check if a value exists would do the job
function is_empty($data) {
if(empty($data) || !isset($data) || $data == false) {
return true;
}
return false;
}
What is the easiest way to map array of objects to key-value array of objects, where key is some property of an object?
For example:
class Cartoon
{
public $title;
public function __construct($title)
{
$this->title = $title;
}
}
$cartoons = array(new Cartoon('Tom and Jerry'), new Cartoon('Cheburashka'));
$mappedCartoons = array();
foreach ($cartoons as $cartoon)
{
$mappedCartoons[$cartoon->title] = $cartoon;
}
print_r ($mappedCartoons);
PS. I wonder if iteration and the extra variable $mappedCartoons can be disposed of?
You could use array_reduce to achive your goal.
<?php
$cartoons = array_reduce($cartoons, function($result, $cartoon) {
$result[$cartoon->title] = $cartoon;
return $result;
}, array());
?>
the idea is not mine, found here: http://www.danielauener.com/howto-use-array_map-functionality-on-an-associative-array-changing-keys-and-values/
If you must use basic arrays you can simply type out the keys when creating them:
$cartoons = array(
'Tom and Jerry' => new Cartoon('Tom and Jerry'),
'Cheburashka' => new Cartoon('Cheburashka'),
);
Alternatively you can create container object that implements various array/iteration related interfaces and can be used as a regular array:
class CartoonArray extends ArrayObject {
public function __construct($input, $flags = 0, $iterator_class = "ArrayIterator") {
parent::__construct(array(), $flags, $iterator_class);
// since the original ArrayObject implemented in C and they don't it won't pick up our overriden ossetSet we have to type it out here explicitly
foreach ($input as $value) {
$this->offsetSet(null, $value);
}
}
public function offsetSet($index, $value) {
parent::offsetSet($value->title, $value);
}
}
$cartoons = new CartoonArray(array(new Cartoon('Tom and Jerry'), new Cartoon('Cheburashka')));
$cartoons[] = new Cartoon('Sesame street');
print $cartoons['Tom and Jerry']->title;
unset($cartoons['Tom and Jerry']);
foreach ($cartoons as $k => $v) {
print $k."\n";
}
I can't think of a way that doesn't involve iterating. I can think of a way of not needing an additional variable though:
$cartoons = array(new Cartoon('Tom and Jerry'), new Cartoon('Cheburashka'));
foreach( $cartoons as $key => $cartoon )
{
$cartoons[ $cartoon->title ] = $cartoon;
unset( $cartoons[ $key ] );
}
var_dump( $cartoons );
However, the documentation of foreach notes:
As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior.
I'm not entirely sure that this applies to my example as well. Maybe someone else can chime in here.
To be safe, perhaps this alternative is more appropriate, as it initiates $len with the initial length of $cartoons, before iteration:
$cartoons = array(new Cartoon('Tom and Jerry'), new Cartoon('Cheburashka'));
for( $i = 0, $len = count( $cartoons ); $i < $len; $i++ )
{
$cartoon = $cartoons[ $i ];
$cartoons[ $cartoon->title ] = $cartoon;
unset( $cartoons[ $i ] );
}
var_dump( $cartoons );
This example assumes the initial $cartoons has 'proper' sequentially numbered keys, starting at 0, though.
i am actually working on this mentioned title. player show the list, its generating perfectly. but i am no where to make it actually play that file. i must be wrong some where.
i need advise fox. (ahh if i can attach the files.)
my class
class DecodDir
{
function getFiles($directory)
{
$all_files = array();
$handler = opendir($directory);
while($files=readdir($handler))
{
if($files!="." && $files!="..")
{
$all_files[]= $files;
}
}
closedir($handler);
return $all_files;
}
}
################# file where i am using this class *###############
<?php
include("decoddir.php");
$obj = new DecodDir();
$results = $obj->getFiles("mp3");
$total = count($results);
$string = "";
for($i=0; $i<$total; $i++){
$string .="
{
name:'$results[$i]',
mp3:'mp3/$results[$i]',
ogg:'$results[$i]'
},
";
}
?>
// its at the top of that html file (ofcorse with the php ext)
and below, this is where it is generating the playlist
var audioPlaylist = new Playlist("2", [
<?php echo $string; ?>
],
http://www.jplayer.org/latest/demo-02/ (the link from where i get jplayer) you can see the audio player with playlist.
(actually i don't know hot format the code in here stackoverflow)
thanks
Rafay
I have taken the liberty of re-factoring the code a bit for you. I don't know exactly what you are trying to do, but it will help to have the beginnings of a better class on your side.
<?php
class DecodDir
{
private
$directory,
$files;
public function __construct( $directory = null )
{
if ( ! is_null($directory) )
{
$this->setDirectory( $directory );
}
}
public function setDirectory( $directory )
{
$this->directory = $directory;
$this->files = null;
// TODO put some validation in here;
return $this;
}
public function getDirectory()
{
if ( is_null($this->directory) )
{
$this->directory = './';
}
return $this->directory;
}
private function getFiles()
{
if ( is_null($this->files) )
{
$this->files = array();
$handler = opendir( $this->getDirectory() );
while($files=readdir($handler))
{
if($files!="." && $files!="..")
{
$this->files[]= $files;
}
}
closedir($handler);
}
return $this->files;
}
public function getJson()
{
$list = array();
foreach ( $this->getFiles() as $filename )
{
$item = new stdClass();
$item->name = $filename;
$item->mp3 = "mp3/{$filename}";
$item->ogg = $filename;
$list[] = $item;
}
$json = json_encode( $list );
return $json;
}
public function countFiles()
{
return sizeof( $this->getFiles() );
}
}
$obj = new DecodDir( 'mp3' );
echo $obj->getJson();
I wrote the code at the following site to do what you are trying to do, I think:
http://jplaylister.yaheard.us/
Sadly, it doesn't currently collapse a song stored in multiple formats (mysong1.mp3, mysong1.ogg) into one playlist item, but otherwise it is pretty feature-complete and has lots of customizable options.
Hope this helps!
I've been trying to think of a way to dynamically return property values for a class using __call instead of creating a slew of functions whose only purpose would be to return those values. The idea I have is to be able to request ( for example ) $this->fetch_PersonFirstName() and have the class check if $this->person["first_name"] is set and return the value. Another example would be calling $this->fetch_BookGenreTitle() and have the class return the value of $this->book["genre"]["title"]. I know something like this would have to do a bit of checking in order for it to automatically determine that, for example, since $this->book["genre_title"] doesn't exist, then it should check for $this->book["genre"]["title"].
So far I've come up with code that ( for some reason ) works for returning the values of an array ( such as my person example ) but my problem quickly develops when I try to return the values of a multidimensional array ( such as with my above book example ). Bare in mind, I'm still trying to think of a way for the __call method to check for the existence of one, and if it doesn't exist, then the other.
Please, throw me a line here. I've been banging my head against the wall trying to figure this out and it's killing me.
<?php
class Test
{
protected $person;
protected $book;
public function __construct()
{
$this->person["first_name"] = "John Smith";
$this->book["genre"]["title"] = "Suspense";
}
public function __call($method, $args)
{
$args = implode(", ", $args);
if (preg_match("/^fetch_([A-Z]{1,}[a-z]{1,})(.*)?/", $method, $match))
{
print_r($match);
echo "<br><br>";
$property = strtolower($match[1]);
$indexes = $match[2];
if (property_exists("Test", $property))
{
if ($indexes)
{
$indexes = preg_split("/(?<=[a-z])(?=[A-Z])/", $indexes);
$num_indexes = count($indexes);
$count_indexes = 1;
for ($count=0; $count<$num_indexes; $count++)
{
$record = strtolower($indexes[$count]);
$index .= $record;
$array .= "{$record}";
$var_index = $index;
$var_array = $array;
echo $var_index." {$count}<br>";
echo $var_array." {$count}<br>";
//print_r($this->{$property}{$var_array});
if ($count_indexes == $num_indexes)
{
if (isset($this->{$property}{$var_index}))
{
return $this->{$property}{$var_index};
}
else
{
return $this->{$property}{$var_array};
}
}
else
{
$index .= "_";
}
$count_indexes++;
}
echo "<br><br>";
}
else
{
return $this->{$property};
}
}
}
}
}
?>
<?php
$test = new Test();
echo $test->fetch_PersonFirstName();
echo "<br><br>";
echo $test->fetch_BookGenreTitle();
?>
Thanks again folks. I think I finally have my solution, which works for multidimensional arrays or any depth and accounts for determining whether a property is, for example $this->book["genre_title"] or $this->book["genre"]["title"] :)
I'm posting the code below as someone else may randomly find it useful in the future
<?php
class Test
{
protected $person;
protected $book;
public function __construct()
{
$this->person["first_name"] = "John Smith";
$this->book["genre"]["title"] = "Suspense";
}
public function __get($var)
{
if (preg_match_all("/([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)/", $var, $matches))
{
$matches = $matches[1];
$property = strtolower($matches[0]);
if (property_exists($this, $property))
{
unset($matches[0]);
$matches = array_values($matches);
$num_matches = count($matches);
$var = &$this->{$property};
if (!$num_matches)
{
return $var;
}
else
{
foreach($matches as &$match)
{
$match = strtolower($match);
$index .= $match;
if ($probe = $this->iterateArray($var, $index))
{
$var = $probe;
unset($index);
}
elseif ($probe = $this->iterateArray($var, $index))
{
$var = $probe;
}
else
{
$index .= "_";
}
}
return $var;
}
}
}
}
public function iterateArray($var, $index)
{
if (array_key_exists($index, $var))
{
return $var[$index];
}
else
{
return false;
}
}
}
?>
<?php
$test = new Test();
echo $test->PersonFirstName;
echo "<br><br>";
echo $test->BookGenreTitle;
?>
There's more than likely some ways to improve/streamline the code, in which case anyone wanting to do so is more than welcome to post an improved version.
Given "BookGenreTitle" :
Use some sort of regex to separate "Book", "Genre", and "Title"
property_exists($this, "Book")
array_key_exists("genre", $this->book)
If key exists, return $this->book["genre"]
If key doesn't exist, array_key_exists("genre_title", $this->book)
If key exists, return $this->book["genre_title"]
If key doesn't exist, array_key_exists("genre", $this->book) && array_key_exists("title", $this->book["genre"])
Keep going
There's probably a way to use a loop or some sort of recursion instead of hard-coding the maximum depth, but I won't get into that now...
Oh, and as the other poster said, what you want is property overloading (__get and __set).
You need to take a look at property overloading, not method overloading as you've already figured out in the question's title yourself.