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!
Related
hi i have many data files in json format in a folder.
now i want to search a filed in them .my search word maybe not exist in some of them and may be exist in one of them files.
i have read this function and if not exits in a file i call the function to read another file.
when i echo the result show me and works fine but return not working and no data returned.
function get_shenavari_in_files($search,$type)
{
static $counter =1 ;
$darsadi = 0;
$find = false;
$file_name = get_files_in_dir(); // make an array of file names
$file_number = count($file_name)-$counter ;
$file="files/" .$file_name[$file_number];
$file_data = read_json($file);
for($i = 0 ; $i<count($file_data) ; $i++)
{
if($file_data[$i][$type] == $search )
{
$darsadi = $file_data[$i]['darsadi'] ;
$find = true;
echo $darsadi ; //this works and show the data
return $darsadi; // this is my problem no data return.
break;
}
}
if($find == false)
{
$counter ++;
get_shenavari_in_files($search,$type);
}
}
var_dump(get_shenavari_in_files('Euro','symbol')); //return null
Once you recurse into get_shenavari_in_files, any found value is never returned back to the inital caller, i.e. instead of
if($find == false)
{
...
get_shenavari_in_files($search,$type);
}
you simply need to prepend the function call with a returnstatement
if($find == false)
{
...
return get_shenavari_in_files($search,$type);
}
Having said that, I would try a much simpler (and thereby less error-prone) approach, e.g.:
function get_shenavari_in_files($search, $type) {
$files = glob("files/*.json"); // Get names of all JSON files in a given path
$matches = [];
foreach ($files as $file) {
$data = json_decode(file_get_contents($file), true);
foreach ($data as $row) {
if (array_key_exists($type, $row) && $row[$type] == $search) {
$matches[$file] = $search;
}
}
}
return $matches;
}
This way, you would be able to eliminate the need for a recursive call to get_shenavari_in_files. Also, the function itself would become more performant because it doesn't have to scan the file system over and over again.
I am currently using this library https://github.com/php-poppler/php-poppler in addition to Poppler in order to convert PDF files to HTML. I installed the library via composer and unfortunately there is no documentation available except some guides about installation but still incomplete.
Given that this Main API usage:
$file = new Poppler\Process\PdfFile(...);
// Get pdf info
print_r($file->getInfo('test.pdf'));
// Get text content of pdf
echo $file->toText('test.pdf');
// Transform to html
$file->toHtml('test.pdf', '/path/for/html');
I can not even define what parameters should be given in $file = new Poppler\Process\PdfFile(...);
What I have tried:
<?php
include 'vendor/autoload.php';
use Poppler\Processor\PdfFile;
use Poppler\Driver\Pdfinfo;
use Poppler\Driver\Pdftohtml;
use Poppler\Driver\Pdftotext;
$a = new Pdfinfo;
$b = new Pdftohtml;
$c = new Pdftotext;
$file = new PdfFile($a,$b,$c);
print_r($file->getInfo('test.pdf'));
echo $file->toText('test.pdf');
$file->toHtml('test.pdf', 'Results');
?>
This gives an error:
Catchable fatal error: Argument 1 passed to Alchemy\BinaryDriver\AbstractBinary::__construct() must be an instance of Alchemy\BinaryDriver\ProcessBuilderFactoryInterface, none given
Here's PdfFile.php:
<?php
namespace Poppler\Processor;
use Poppler\Driver\Pdfinfo;
use Poppler\Driver\Pdftohtml;
use Poppler\Driver\Pdftotext;
use Poppler\Exception\FileNotFoundException;
class PdfFile
{
private $pdfinfo;
private $pdftotext;
private $pdftohtml;
public function __construct(Pdfinfo $pdfinfo, Pdftotext $pdftotext, Pdftohtml $pdftohtml)
{
$this->pdfinfo = $pdfinfo;
$this->pdftotext = $pdftotext;
$this->pdftohtml = $pdftohtml;
}
public function toText($inputfile, $toEncoding = 'UTF-8')
{
if (!file_exists($inputfile)) {
throw new FileNotFoundException("File $inputfile not found.");
}
$output = $this->pdftotext->command(array('-nopgbrk', $inputfile, '-'));
$fromEncoding = mb_detect_encoding($output);
if ($fromEncoding) {
return mb_convert_encoding($output, $toEncoding, $fromEncoding);
}
return mb_convert_encoding($output, $toEncoding);
}
public function toHtml($inputfile, $outputfile)
{
if (!file_exists($inputfile)) {
throw new FileNotFoundException("File $inputfile not found.");
}
$output = $this->pdftohtml->command(array($inputfile, $outputfile));
return $output;
}
public function getInfo($inputfile)
{
if (!file_exists($inputfile)) {
throw new FileNotFoundException("File $inputfile not found.");
}
$args = array($inputfile);
$output = $this->pdfinfo->command($args);
$info = array();
foreach (explode(PHP_EOL, $output) as $line) {
if (strpos($line, ': ') === false) {
continue;
}
$parts = explode(': ', $line);
$key = trim($parts[0]);
$value = trim($parts[1]);
$info[$key] = $value;
}
return $info;
}
}
I'm trying to come up with a solution that will allow users to upload a mail merge-enabled Word DOCX template file. Ideally, the system will read the DOCX file, extract the XML, find the mail merge fields and save them to a database for mapping down the road. I may go with a SOAP service such as Zend LiveDocX or PHPDOCX or something else entirely -- but for now I need to figure out how to identify the fields in a DOCX file. To do that I've started with this article: http://dfmaxwell.wordpress.com/2012/02/24/using-php-to-process-a-word-document-mail-merge/
I've adapted it a bit to my needs (which may be a problem, though I get the same error with the original code as well.) Specifically I'm not using it to perform the mail merge at this time, I just want to identify the fields. Here's what I've got:
$newFile = '/var/www/mysite.com/public_html/template.docx';
$zip = new ZipArchive();
if( $zip->open( $newFile, ZIPARCHIVE::CHECKCONS ) !== TRUE ) { echo 'failed to open template'; exit; }
$file = 'word/document.xml';
$data = $zip->getFromName( $file );
$zip->close();
$doc = new DOMDocument();
$doc->loadXML( $data );
$wts = $doc->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'fldChar');
$mergefields = array();
function getMailMerge(&$wts, $index) {
$loop = true;
$counter = $index;
$startfield = false;
while ($loop) {
if ($wts->item($counter)->attributes->item(0)->nodeName == 'w:fldCharType') {
$nodeName = '';
$nodeValue = '';
switch ($wts->item($counter)->attributes->item(0)->nodeValue) {
case 'begin':
if ($startfield) {
$counter = getMailMerge($wts, $counter);
}
$startfield = true;
if ($wts->item($counter)->parentNode->nextSibling) {
$nodeName = $wts->item($counter)->parentNode->nextSibling->childNodes->item(1)->nodeName;
$nodeValue = $wts->item($counter)->parentNode->nextSibling->childNodes->item(1)->nodeValue;
}
else {
// No sibling
// check next node
$nodeName = $wts->item($counter + 1)->parentNode->previousSibling->childNodes->item(1)->nodeName;
$nodeValue = $wts->item($counter + 1)->parentNode->previousSibling->childNodes->item(1)->nodeValue;
}
if (substr($nodeValue, 0, 11) == ' MERGEFIELD') {
$mergefields[] = strtolower(str_replace('"', '', trim(substr($nodeValue, 12))));
}
$counter++;
break;
case 'separate':
$counter++;
break;
case 'end':
if ($startfield) {
$startfield = false;
}
$loop = false;
}
}
}
return $counter;
}
for ($x = 0; $x < $wts->length; $x++) {
if ($wts->item($x)->attributes->item(0)->nodeName == 'w:fldCharType' && $wts->item($x)->attributes->item(0)->nodeValue == 'begin') {
$newcount = getMailMerge($wts, $x);
$x = $newcount;
}
}
I have no problem opening the DOCX file with ZipArchive() and if I use print_r($doc->saveHTML()); I see the XML data just fine. The problem is that when I execute my code I get Fatal error: Call to a member function item() on a non-object pointing to this:
$nodeName = $wts->item($counter)->parentNode->nextSibling->childNodes->item(1)->nodeName;
Google has let me down when trying to figure out this error, can anyone point me in the right direction? Thanks in advance!
Found a solution -- it's not quite as elegant as what I was hoping for but here goes.
Using xml_parser_create_ns I can search the DOCX file for the keys I need, specifically "HTTP://SCHEMAS.OPENXMLFORMATS.ORG/WORDPROCESSINGML/2006/MAIN:INSTRTEXT" which identifies all fields marked as "MERGEFIELD". Then I can dump the results into an array and use them to update the database. To wit:
// Word file to be opened
$newFile = '/var/www/mysite.com/public_html/template.docx';
// Extract the document.xml file from the DOCX archive
$zip = new ZipArchive();
if( $zip->open( $newFile, ZIPARCHIVE::CHECKCONS ) !== TRUE ) { echo 'failed to open template'; exit; }
$file = 'word/document.xml';
$data = $zip->getFromName( $file );
$zip->close();
// Create the XML parser and create an array of the results
$parser = xml_parser_create_ns();
xml_parse_into_struct($parser, $data, $vals, $index);
xml_parser_free($parser);
// Cycle the index array looking for the important key and save those items to another array
foreach ($index as $key => $indexitem) {
if ($key == 'HTTP://SCHEMAS.OPENXMLFORMATS.ORG/WORDPROCESSINGML/2006/MAIN:INSTRTEXT') {
$found = $indexitem;
break;
}
}
// Cycle *that* array looking for "MERGEFIELD" and grab the field name to yet another array
// Make sure to check for duplicates since fields may be re-used
if ($found) {
$mergefields = array();
foreach ($found as $field) {
if (!in_array(strtolower(trim(substr($vals[$field]['value'], 12))), $mergefields)) {
$mergefields[] = strtolower(trim(substr($vals[$field]['value'], 12)));
}
}
}
// View the fruits of your labor
print_r($mergefields);
Can i parse a plist file with php and kind of get it into an array, like the $_POST[''] so i could call $_POST['body'] and get the string that has the <key> body ?
CFPropertyList - A PHP Implementation Of Apple's plist (PropertyList)
Googling for "php plist parser" turned up this blog post that seems to be able to do what you are asking for.
Took a look at some of the libraries out there but they have external requirements and seem overkill. Here's a function that simply puts the data in to associative arrays. This worked on a couple of exported itunes plist files I tried.
// pass in the full plist file contents
function parse_plist($plist) {
$result = false;
$depth = [];
$key = false;
$lines = explode("\n", $plist);
foreach ($lines as $line) {
$line = trim($line);
if ($line) {
if ($line == '<dict>') {
if ($result) {
if ($key) {
// adding a new dictionary, the line above this one should've had the key
$depth[count($depth) - 1][$key] = [];
$depth[] =& $depth[count($depth) - 1][$key];
$key = false;
} else {
// adding a dictionary to an array
$depth[] = [];
}
} else {
// starting the first dictionary which doesn't have a key
$result = [];
$depth[] =& $result;
}
} else if ($line == '</dict>' || $line == '</array>') {
array_pop($depth);
} else if ($line == '<array>') {
$depth[] = [];
} else if (preg_match('/^\<key\>(.+)\<\/key\>\<.+\>(.+)\<\/.+\>$/', $line, $matches)) {
// <key>Major Version</key><integer>1</integer>
$depth[count($depth) - 1][$matches[1]] = $matches[2];
} else if (preg_match('/^\<key\>(.+)\<\/key\>\<(true|false)\/\>$/', $line, $matches)) {
// <key>Show Content Ratings</key><true/>
$depth[count($depth) - 1][$matches[1]] = ($matches[2] == 'true' ? 1 : 0);
} else if (preg_match('/^\<key\>(.+)\<\/key\>$/', $line, $matches)) {
// <key>1917</key>
$key = $matches[1];
}
}
}
return $result;
}
I want to create a Zend Controller for ACL management so my problem is: How can I get all Module names, Control names and Action names in a Zend application to build a ACL Control?
I use Zend_Navigation and if the resource don't exist in your ACL Zend_Navigation is thrown a exception. And I want to use a database to deny and allow access. So I must build the database first. And if I must do that by hand it's a pain to do that.
This may be an old question but this is how I am doing this...
// $front = Zend_Controller_Front::getInstance(); // use this line instead on a model class
$front = $this->getFrontController(); // this in controller
$acl = array();
foreach ($front->getControllerDirectory() as $module => $path) {
foreach (scandir($path) as $file) {
if (strstr($file, "Controller.php") !== false) {
include_once $path . DIRECTORY_SEPARATOR . $file;
$class = substr($file,0,strpos($file,".php"));
if (is_subclass_of($class, 'Zend_Controller_Action')) {
$controller = strtolower(substr($file, 0, strpos($file, "Controller")));
$methods = array();
foreach (get_class_methods($class) as $method) {
if (strstr($method,"Action") != false) {
array_push($methods,substr($method,0,strpos($method,"Action")));
}
}
}
$acl[$module][$controller] = $methods;
}
}
}
I have created a function that can get all the actions, controllers and modules from a zend application. Here it is:
$module_dir = substr(str_replace("\\","/",$this->getFrontController()->getModuleDirectory()),0,strrpos(str_replace("\\","/",$this->getFrontController()->getModuleDirectory()),'/'));
$temp = array_diff( scandir( $module_dir), Array( ".", "..", ".svn"));
$modules = array();
$controller_directorys = array();
foreach ($temp as $module) {
if (is_dir($module_dir . "/" . $module)) {
array_push($modules,$module);
array_push($controller_directorys, str_replace("\\","/",$this->getFrontController()->getControllerDirectory($module)));
}
}
foreach ($controller_directorys as $dir) {
foreach (scandir($dir) as $dirstructure) {
if (is_file($dir . "/" . $dirstructure)) {
if (strstr($dirstructure,"Controller.php") != false) {
include_once($dir . "/" . $dirstructure);
}
}
}
}
$default_module = $this->getFrontController()->getDefaultModule();
$db_structure = array();
foreach(get_declared_classes() as $c){
if(is_subclass_of($c, 'Zend_Controller_Action')){
$functions = array();
foreach (get_class_methods($c) as $f) {
if (strstr($f,"Action") != false) {
array_push($functions,substr($f,0,strpos($f,"Action")));
}
}
$c = strtolower(substr($c,0,strpos($c,"Controller")));
if (strstr($c,"_") != false) {
$db_structure[substr($c,0,strpos($c,"_"))][substr($c,strpos($c,"_") + 1)] = $functions;
}else{
$db_structure[$default_module][$c] = $functions;
}
}
}
}
I actually found the best way to have an easily available reflection reference was to recursively tokenise the correct directories and then build an xml document as a result. Caching the xml document for speed and using xpath for retrieving the data.
The plugin builds the reflection xml and caches it for later. I've taken this code out of its original implementation, so its more to give you a feel rather than copy and paste.
Of course, a database works just as well here. But if you're trying to limit your queries per page, a cached xml doc works pretty well.
class My_Reflection_Plugin extends My_Controller_Plugin_Abstract
{
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$cache = $this -> getCacheManager() -> getCache('general');
if (!$xml = $cache->load("Reflection"))
{
$paths = array(
PATH_APPLICATION . "/Core",
PATH_SITE . "/Project"
);
foreach ($paths as $path)
{
$this -> inspectDir($path);
}
$cache -> save($this->getReflectionXML(), "Reflection");
}
else
{
$this -> getReflectionXML($xml);
}
}
private function inspectDir($path)
{
$rdi = new RecursiveDirectoryIterator($path);
$rii = new RecursiveIteratorIterator($rdi);
$filtered = new My_Reflection_Filter($rii);
iterator_apply($filtered, array($this, 'process'), array($filtered));
}
private function process($it = false)
{
$this -> getReflectionXML() -> addItem($it -> current());
return true;
}
}
Tokenisation happens inside the filter:
class My_Reflection_Filter extends FilterIterator
{
public function accept()
{
$file = $this->getInnerIterator()->current();
// If we somehow have something other than an SplFileInfo object, just
// return false
if (!$file instanceof SplFileInfo) {
return false;
}
// If we have a directory, it's not a file, so return false
if (!$file->isFile()) {
return false;
}
// If not a PHP file, skip
if ($file->getBasename('.php') == $file->getBasename()) {
return false;
}
// Resource forks are no good either.
if (substr($file->getBaseName(), 0, 2) == '._')
{
return false;
}
$contents = file_get_contents($file->getRealPath());
$tokens = token_get_all($contents);
$file->className = NULL;
$file->classExtends = NULL;
$file->classImplements = array();
$last = null;
while (count($tokens) > 0)
{
$token = array_shift($tokens);
if (!is_array($token))
{
continue;
}
list($id, $content, $line) = $token;
switch ($id)
{
case T_ABSTRACT:
case T_CLASS:
case T_INTERFACE:
$last = 'object';
break;
case T_EXTENDS:
$last = "extends";
break;
case T_IMPLEMENTS:
$last = "implements";
break;
case T_STRING:
switch ($last)
{
case "object":
$file -> className = $content;
break;
case "extends":
$file -> classExtends = $content;
break;
case "implements":
$file -> classImplements[] = $content;
break;
}
break;
case T_WHITESPACE:
// Do nothing, whitespace should be ignored but it shouldnt reset $last.
break;
default:
// If its not directly following a keyword specified by $last, reset last to nothing.
$last = null;
break;
}
}
return true;
}
}
Once you have your reflection xml populated with whatever information you need out of the class, your acl plugin can come after it and query that information with xpath.
I don't think there is a solution for this in Zend. You will have to do it yourself...
One way to do it, is to list all classes, and check if the classes extend (for example) the Zend_Controller_Action class...
check the php functions get_declared_classes and is_subclass_of
foreach(get_declared_classes() as $c){
if(is_subclass_of($c, 'Zend_Controller_Action')){
...
}
}