DOMDocument::save XML(): Memory allocation failed : growing buffer - php

I receive this error while i am trying to combine my xml files.I read other questions and answers put i could not find any solution for my code. I cannot increase ram of computer. Here is my code
public function mergeXml ($filename,$source){
$events = array();
// open each xml file in this directory
foreach(glob("$source/*.xml") as $files) {
// get the contents of the the current file
$events[] =$files; // throw all files into an array .
}
// Replace the strings below with the actual filenames, add or decrease as fit
$out = new \DOMDocument();
$root = $out->createElement("documents");
foreach ($events as $file) { //get each file from array
$obj = new \DOMDocument();
$obj->load($file); //load files to obj.
$xpath = new \DOMXPath($obj);
foreach ($xpath->query("/*/node()") as $node)
$root->appendChild($out->importNode($node, true)); }
$out->appendChild($root);
file_put_contents("$source/$filename.xml",$out->saveXML());

Related

How to get element count in multiple xml files in a folder using PHP?

The following php script gives count of elements in a single xml file in the folder uploads. But I have number of xml files in the folder. What to modify in the following script so that I get result in tabular format with the file name and element count for all the xml files in the folder.
<?php
$doc = new DOMDocument;
$xml = simplexml_load_file("uploads/test.xml");
//file to SimpleXMLElement
$xml = simplexml_import_dom($xml);
print("Number of elements: ".$xml->count());
?>
You're first loading the XML file into a SimpleXMLElement then import it into a DOMElement and call the method count() on it. This method does not exists on DOMElement - only on SimpleXMLElement. So the import would not be necessary.
You can use a GlobIterator to iterate the files:
$directory = __DIR__.'/uploads';
// get an iterator for the XML files
$files = new GlobIterator(
$directory.'/*.xml', FilesystemIterator::CURRENT_AS_FILEINFO
);
$results = [];
foreach ($files as $file) {
// load file using absolute file path
// the returned SimpleXMLElement wraps the document element node
$documentElement = simplexml_load_file($file->getRealPath());
$results[] = [
// file name without path
'file' => $file->getFilename(),
// "SimpleXMLElement::count()" returns the number of children of an element
'item-count' => $documentElement->count(),
];
}
var_dump($results);
With DOM you can use Xpath to fetch specific values from the XML.
$directory = __DIR__.'/uploads';
// get an iterator for the XML files
$files = new GlobIterator(
$directory.'/*.xml', FilesystemIterator::CURRENT_AS_FILEINFO
);
// only one document instance is needed
$document = new DOMDocument();
$results = [];
foreach ($files as $file) {
// load the file into the DOM document
$document->load($file->getRealPath());
// create an Xpath processor for the loaded document
$xpath = new DOMXpath($document);
$results[] = [
'file' => $file->getFilename(),
// use an Xpath expression to fetch the value
'item-count' => $xpath->evaluate('count(/*/*)'),
];
}
var_dump($results);
The Xpath Expression
Get the document element /*
Get the child elements of the document element /*/*
Count them count(/*/*)
* is an universal selector for any element node. If you can you should be more specific and use the actual element names (e.g. /list/item).
First, create a function with the logic you have:
function getXML($path) {
$doc = new DOMDocument;
$xml = simplexml_load_file($path);
//file to SimpleXMLElement
$xml = simplexml_import_dom($xml);
return $xml;
}
Note that I:
have converted the path into a parameter, so you can reuse the same logic for your files
separated the parsing of XML from showing it
returned the XML itself, so you can get the count or you can do whatever else you may want with it
This is how you can get the files of a given path:
$files = array_diff(scandir('uploads'), array('.', '..'));
we get all files except for . and .., which are surely not of interest here. Read more about scandir here: https://www.php.net/manual/en/function.scandir.php
You received an array of filenames on success, so, let's loop it and perform the logic you need:
$xmls = [];
foreach ($files as $file) {
if (str_ends_with($file, '.xml')) {
$xmls[] = $file . "\t" . getXML('uploads/' . $file)->count();
}
}
echo implode("\n", $xmls);
EDIT
As #Juan kindly explained in the comment section, one can use
$files = glob("./uploads/*.xml");
instead of scandir and that would ensure that we no longer need a call for array_diff and later we can avoid the if inside the loop:
$xmls = [];
foreach ($files as $file) {
$xmls[] = $file . "\t" . getXML('uploads/' . $file)->count();
}
echo implode("\n", $xmls);

Delete files that is not in my XML document with PHP

I have an XML file that contains the SKU of products. I also have a folder that corresponds to this XML file.
Snippet from XML:
<Feed>
<Product>
<ItemCode>ALT-AAB-BL</ItemCode>
<BaseItemCode>ALT-AAB</BaseItemCode>
<StockCheckCode>ALT-AAB-BL</StockCheckCode>
</Product>
<Product>
<ItemCode>ALT-AAB-L</ItemCode>
<BaseItemCode>ALT-AAB</BaseItemCode>
<StockCheckCode>ALT-AAB-L</StockCheckCode>
</Product>
<Product>
<ItemCode>ALT-AAB-N</ItemCode>
<BaseItemCode>ALT-AAB</BaseItemCode>
<StockCheckCode>ALT-AAB-N</StockCheckCode>
</Product>
</Feed>
I have been trying it with php but I am a junior and dont know where to start so I will give you some pseudo code.
if $domelement->ItemCode != filename.jpg{
delte.jpg;
}
Yes this pseudo code is terrible. I basically am able to pull in the .xml file and was able to manipulate data.
I basically want to delete the files that is not present in the xml file and preserve the rest. I know how to appedn the ItemCode with .png if I need to.
<?php
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('altitude.xml');
$xpath = new DOMXPath($dom);
$query = sprintf('/Feed/Product/BaseItemCode');
foreach($xpath->query($query) as $record) {
//delete file that is not present in BaseItemCode
}
I just want the files not present in xml->BaseItemCode (which I will append with .png or .jpg) to be deleted from the folder.
You need two lists: whitelist from XML and all items list from system.
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('altitude.xml');
$xpath = new DOMXPath($dom);
$query = sprintf('/Feed/Product/BaseItemCode');
$xmlList = [];
foreach($xpath->query($query) as $record) {
$xmlList[] = $record->ItemCode . '.jpg';
$xmlList[] = $record->ItemCode . '.png'; // If you can, use smarter way
}
$directory = '/full/path/to/dir';
$dirList = array_diff(scandir($directory), array('..', '.'));
$filesToDelete = array_diff($dirList, $xmlList);
foreach ($filesToDelete as $file) {
unlink($directory . DIRECTORY_SEPARATOR . $file);
}
#justinas 's method worked. I had this weird space at the end of every single array element imported from my CSV file. I converted my XML to a CSV file and used it as an array.
<?php
$csv = file('convertcsv.csv');
function test_alter(&$item1, $key, $prefix)
{
$item1 = "$item1$prefix";
}
array_walk($csv, 'test_alter', '.png');
//var_dump($csv);
$directory = 'img';
$dirList = array_diff(scandir($directory), array('..', '.'));
$filesToDelete = array_diff($dirList, $csv);
foreach ($filesToDelete as $file) {
unlink($directory . DIRECTORY_SEPARATOR . $file);
}
echo "klaar"
?>
Can anyone tell me why there is a blank space after every single element in the array if you use:
$csv = file('convertcsv.csv');
as an array?

Download and merge multiple XML files with PHP, Foreach and Dom

I'm breaking my tooths since a week with this problem : i'm trying to download and merge dynamicly multiples xml files with an API. I can download all the files but i can't merge them without having multiple roots elements... It's frustrating and i don t find any suggestion. Here is my code :
<?php
$fileout = 'file.xml';
unlink($fileout);
$baseurl="https://websitewithapi.com/";
$topcategories=array("COOL","DRIVE","FUN");
foreach ($topcategories as $topcategory) {
$url_cata_test="https://websitewithapi.com/&filters=topcategory:$topcategory&limit=1";
$jsontest = file_get_contents($url_cata_test);
$arrtest=json_decode($jsontest);
$items=$arrtest->pagination->count;
$pagemax=ceil($items/250);
$pagetest= range(0,$pagemax);
foreach ($pagetest as $page) {
$url_cata="$baseurl&filters=topcategory:$topcategory&offset=$page&limit=250";
echo "Cat en cours d import: ".$topcategory."\n";
echo "Page en cours d import: ".$page."\n";
echo "URL Cata: $url_cata \n";
};
$dom = new DOMDocument();
$dom->appendChild($dom->createElement('superdeals'));
$files= array($url_cata);
foreach ($files as $filename) {
$addDom = new DOMDocument();
$addDom->load($filename);
if ($addDom->documentElement->getElementsByTagName('products')) {
foreach ($addDom->documentElement->getElementsByTagName('product') as $node) {
$dom->documentElement->appendChild(
$dom->importNode($node, TRUE)
);
}
}
$dom->formatOutput = true;
file_put_contents($fileout, $dom->saveXML(), FILE_APPEND);
}
};
?>
I got always the same problem with "associate" files in the same file but with multiple roots ! Is there a thing i miss ?
Thank you.
Simply initialize the DOM object and save its file output outside of all four foreach loops. Currently you are using FILE APPEND for each iteration which is not an XML DOM method but simply concatenates text content. Continue to grow your XML tree within the loops and then output the singular XML once without any file appends.
$fileout = 'file.xml';
unlink($fileout);
// INITIALIZE DOM TREE
$dom = new DOMDocument();
$dom->appendChild($dom->createElement('superdeals'));
...
foreach ($topcategories as $topcategory) {
...
foreach ($pagetest as $page) {
...
foreach ($files as $filename) {
...
foreach ($addDom->documentElement->getElementsByTagName('product') as $node) {
$dom->documentElement->appendChild(
$dom->importNode($node, TRUE)
);
}
}
}
}
// OUTPUT DOM TREE
file_put_contents($fileout, $dom->saveXML());
It looks like you are adding root element called "superdeals" to your document, and then adding the contents of each file at the root level.
You need to add the contents of each file as a child of the "superdeals" element, not as a child of the document.
Save the root node:
$root = $dom->appendChild($dom->createElement('superdeals'));
then instead of
$dom->documentElement->appendChild($dom->importNode($node, TRUE))
add child to the root node (not the document node):
$root->appendChild($dom->importNode($node, TRUE))
Document element can contain nodes apart from the root element, such as entity definitions, processing instructions and so on.

how do i edit compressed gz xml file, compress and save it back, with php? [duplicate]

So, I have this code that searches for a particular node in my XML file, unsets an existing node and inserts a brand new child node with the correct data. Is there a way of getting this new data to save within the actual XML file with simpleXML? If not, is there another efficient method for doing this?
public function hint_insert() {
foreach($this->hints as $key => $value) {
$filename = $this->get_qid_filename($key);
echo "$key - $filename - $value[0]<br>";
//insert hint within right node using simplexml
$xml = simplexml_load_file($filename);
foreach ($xml->PrintQuestion as $PrintQuestion) {
unset($xml->PrintQuestion->content->multichoice->feedback->hint->Passage);
$xml->PrintQuestion->content->multichoice->feedback->hint->addChild('Passage', $value[0]);
echo("<pre>" . print_r($PrintQuestion) . "</pre>");
return;
}
}
}
Not sure I understand the issue. The asXML() method accepts an optional filename as param that will save the current structure as XML to a file. So once you have updated your XML with the hints, just save it back to file.
// Load XML with SimpleXml from string
$root = simplexml_load_string('<root><a>foo</a></root>');
// Modify a node
$root->a = 'bar';
// Saving the whole modified XML to a new filename
$root->asXml('updated.xml');
// Save only the modified node
$root->a->asXml('only-a.xml');
If you want to save the same, you can use dom_import_simplexml to convert to a DomElement and save:
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($simpleXml->asXML());
echo $dom->saveXML();

Problems with PHP/XML/XSL

I am trying to figure out what I can do to create a code that appends data in my XML file not rewrite the XML file continuously.
I need to be able to save all the form entries and as of right now every time the form is submitted. it creates a new XML file and erases the old one.
This may be really easy to fix or I am just really dumb but I have looked at DOM syntax and do not see what I could change to change the outcome.
// define configuration file name and path
$configFile = 'abook.xml';
// if form not yet submitted
// display form
if (!isset($_POST['submit'])) {
// set up array with default parameters
$data = array();
$data['name'] = null;
$data['email'] = null;
$data['caddress'] = null;
$data['city'] = null;
$data['state'] = null;
$data['zipcode'] = null;
$data['phone'] = null;
$data['pug'] = null;
$data['comment'] = null;
$data['subscribe'] = null;
// read current configuration values
// use them to pre-fill the form
if (file_exists($configFile)) {
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->load($configFile);
$address = $doc->getElementsByTagName('address');
foreach ($address->item(0)->childNodes as $node) {
$data[$node->nodeName] = $node->nodeValue;
}
}
In between is a PHP form and validation code and at the end I use XML tags again:
// generate new XML document
$doc = new DOMDocument();
// create and attach root element <configuration>
$root = $doc->createElement('addressbook');
$configuration = $doc->appendChild($root);
// create and attach <oven> element under <configuration>
$address = $doc->createElement('address');
$configuration->appendChild($address);
// write each configuration value to the file
foreach ($config as $key => $value) {
if (trim($value) != '') {
$elem = $doc->createElement($key);
$text = $doc->createTextNode($value);
$address->appendChild($elem);
$elem->appendChild($text);
}
}
// format XML output
// save XML file
$doc->formatOutput = true;
$doc->save($configFile) or die('ERROR: Cannot write configuration file');
echo 'Thank you for filling out an application.';
}
I am really new at this so I am sorry if my code is pretty messy.
The second part I am dealing with an XSL file which I have linked to my XML file but no matter what syntax I have used to transform, nothing works to save it in a table.
Again, I don't know if this could be caused by the way I have set up my PHP to write the XML.

Categories