PHP function GLOB: get and modify the results - php

I'm trying to import many xml files that I do not know the name.
I use this code:
foreach(glob('OLD/*.xml') as $file) {
$url= basename($file) . ', ';
$all_urls = array($url);
foreach ($all_urls as $url) {
$xml = simplexml_load_file($url);
I have a lot of files like agency.xml, annunci_324.xml, annunci_321.xml, ecc...
I only need the files that begin for annunci and end .xml. I also need to delete last value's comma and put it in the last foreach. how can i do it?

I think you can check if name contains annunci with strstr function (documentation here)
if(strstr($file, 'annunci')
{
//we found a file with name we are interessed in.
Now you can build directly your array without caring about commas
$all_urls = array();
foreach(glob('OLD/*.xml') as $file)
{
if(strstr($file, 'annunci')
{
$all_urls[] = array(basename($file));
}
}
This way we have all_urls as array of all the files starting with annunci and you can loop in it to simple_load them all.

Related

PHP Validate Value $_GET from url against lines of a txt file

i have a problem i couldn't figure out since im self-taught and still exploring the php world
so i have a text file that looks like this:
951753159
456787541
123156488
748651651
and i got an url with a variable
http://example.com/mypage.php?variable=951753159
what i want is to check if the url variable matches one of the txt file lines in order to execute a code
i already tried this
$search = $_GET["variable"];
$file = "variables.txt";
if (preg_match('/^' . $search . '$/m', file_get_contents($file))) {
THE CODE THAT I WANT TO EXECUTE
}
but for some reason it matches the whole content of the file
any help is highly appreciated
Thanks in advance :)
Try with an array from file():
$lines = file("variables.txt", FILE_IGNORE_NEW_LINES);
if(in_array($_GET["variable"], $lines)) {
// YES FOUND
} else {
// NOT FOUND
}
From the documentation on `file_get_contents', the entire contents of the file are read as a string. So that is why it is matching against the entire file.
The command that you want to use is file, this reads the file into an array of each line.
I would
Use file to read the file into an array.
Then array_flip the array so that it's values are now the keys
Which allows me to isset($array[$key])
You can do this.
<?php
#$search = $_GET["variable"];
$search = '123156488';
$file_txt = "content.txt";
$file = file($file_txt);//convert the txt in array
foreach ($file as $key => $value) {
if (trim($search) == trim($value)) {
print "DO something! " . $value;
}
}?>
Regards.
Nelson.

PHP - SplFileObject - Wrong output for second line when using seek() method

Go to UPDATE to read what's the actual problem now. Old question was already resolved with the first answer submitted by Bert Peters.
OLD QUESTION:
I have few files named as file.1.txt, file.2.txt, file.3.txt, ... I'm reading first file with SplFileObject and using foreach loop to iterate through its content:
$file = new SplFileObject("file.1.txt");
foreach ($file as $row) {
// ...
}
Other files may be or may not be read, depending on the contents of the first file I'm reading. In all cases there should be only one file of others (file.2.txt or file.3.txt) which may be used in the next step. So somewhere inside foreach loop there is if statement which handles this.
All files have the same structure, so there comes the problem. I wouldn't like to create new foreach for reading next file - as I wrote it may not be needed at all, so I would like to use existing foreach instead of writing new one. Is there any possibility to overwrite $file variable with the contents of other file and iterate over it with using only one foreach or any other loop? For example:
foreach ($file as $row) {
// ...
if ($contentContainsSomething) {
$file = new SplFileObject("file.2.txt");
// somehow reset foreach to read file.2.txt from start
}
}
I wouldn't like to use goto statement to solve this problem. The recursion seems to be appropriate solution, but if there's a way to change object in loop on the fly, I would prefer this solution.
UPDATE:
As mentioned in "old question" all used files (file.1.txt, file.2.txt, ...) have the same structure, so that's why I wouldn't like to write more same loops and copy code. Instead I used code from #Danack (suggested by him on SO chat) which is already a part of solution. Here's the basic code for reading more files without any upgrade I need:
$path = "file.1.txt";
$whileCounter = 0;
while ($path != null) {
$file = new SplFileObject($path);
$file->setFlags(SplFileObject::READ_CSV);
$file->setCsvControl("\t");
$path = null;
foreach ($file as $rowKey => $row) {
// echo row }
$path = "file.2.txt";
if ($whileCounter > 0) {
break; // solution to stop loop, just for now
}
$whileCounter++;
}
So this code is working without any problem and outputs the file's lines as expected. The problem is when I would like to read next line of file with seek() method, because I would like to make decision on some information which is appended to each next line. So if I use seek($rowKey + 1) which helps me to get next line data (I use $file->current() when line is changed) and after that I call seek($rowKey) to get to previous line, then next file will output first line twice and second line will be missed. The third line and all after then are printed well. This is the problem achieved with the code below:
$path = "file.1.txt";
$whileCounter = 0;
while ($path != null) {
$file = new SplFileObject($path);
$file->setFlags(SplFileObject::READ_CSV);
$file->setCsvControl("\t");
$path = null;
foreach ($file as $rowKey => $row) {
if ($whileCounter > 0) {
var_dump($row);
echo "<br>";
}
$file->seek($rowKey + 1);
if ($file->valid()) {
$file->seek($rowKey);
} else {
var_dump($row);
echo "<br>";
$path = "file.2.txt";
}
}
$whileCounter++;
}
If you apply custom .csv files (with at least five non-empty lines) instead of file.1.txt and file.2.txt, you will see that second and third output are the same (second and third output are first and "second" lines of file.2.txt). What could be wrong here?
There is not. Foreach uses an iterator over your $file variable, and that iterator continues to be valid even though you changed the value of $file.
Or, to put this in another way, foreach will continue to look at the previous contents of $file, regardless of what you do with it afterwards. This is because $file is not actually the SplFileObject, but rather a reference to it, and the reference is used by foreach.

Convert multiple XML files to one CSV with SimpleXML

I have some xml files, which have the same elements but only with different information.
First file test.xml
<?xml version="1.0" encoding="UTF-8"?>
<phones>
<phone>
<title>"Apple iPhone 5S"</title>
<price>
<regularprice>500</regularprice>
<saleprice>480</saleprice>
</price>
<color>black</color>
</phone>
</phones>
Second file test1.xml
<?xml version="1.0" encoding="UTF-8"?>
<phones>
<phone>
<title>Nokia Lumia 830</title>
<price>
<regularprice>400</regularprice>
<saleprice>370</saleprice>
</price>
<color>black</color>
</phone>
</phones>
I need to convert some values from these xml files into 1 test.csv file
So I am using this php code
<?php
$filexml1='test.xml';
$filexml2='test1.xml';
//File 1
if (file_exists($filexml1)) {
$xml = simplexml_load_file($filexml1);
$f = fopen('test.csv', 'w');
$headers = array('title', 'color');
$converted_array = array_map("strtoupper", $headers);
fputcsv($f, $converted_array, ',', '"');
foreach ($xml->phone as $phone) {
//$phone->title = trim($phone->title, " ");
// Array of just the components you need...
$values = array(
"title" => (string)$phone->title = trim(str_replace ( "\"", """, $phone->title ), " "),
"color" => (string)$phone->color
);
fputcsv($f, $values,',','"');
}
fclose($f);
echo "<p>File 1 coverted to .csv sucessfully</p>";
} else {
exit('Failed to open test.xml.');
}
//File 2
if (file_exists($filexml2)) {
$xml = simplexml_load_file($filexml2);
$f = fopen('test.csv', 'a');
//the same code for second file like for the first file
echo "<p>File 2 coverted to .csv sucessfully</p>";
} else {
exit('Failed to open test1.xml.');
}
?>
The output of the test.csv looks this way
TITLE COLOR
Apple iPhone 5S black
Nokia Lumia 830 black
As you can see I only managed to load each file into a variable and for each file I have to write if statement which makes the script too big, so I am wondering if it is possible to load all files into array, process them with one code block because xml elements are the same and output to one .csv file? Essentially I need the same test.csv output only with less php code.
Thanks in advance.
Next to using an array, there is more in PHP which can make it even more simple. Like an array could represent a list of your files, other constructs in PHP can that, too.
For example, as the XML files you have most likely are inside a specific directory and follow some pattern with their filename, those could be easily represented with a GlobIterator:
$inputFiles = new GlobIterator(__DIR__ . '/*.xml');
You could then foreach over them which I'll show in a moment with another example.
Such a list allows you to streamline your processing. That is important because there is some kind of a generic formular for many programs: Input, Process, Output. This is also called IPO or IPO+S Model. The S stands for storing. In your case while you process the input data, you also store into a new file CSV file which is also the output (after processing is fully done).
When you follow such a generic model, it's easier to structure your code and with a better structure you most often have less code. Even if not, each part of your code is more self-contained and smaller which is most often what you're looking for.
Next to the said list of XML-files I showed at the beginning of the answer with the GlobIterator there are other Iterators that can help to process the XML data.
For example, you've got 1-n XML files that contain 0-n <phone> elements. You know that you want to process any of these <phone> elements, you already exactly know what you want to do with them (extract some data from it). So wouldn't it be great to have a list of all <phone> elements within all XML-files first?
This can be easily done in PHP with the help of a Generator. That is a function that can return values multiple times while it's still "running". This is a simplification, better show some code to illustrate that. Let's say we've got the list of XML files as input and we want all <phone> elements out of it. For sure, you could create an array of all these <phone> elements and process that array later. However, a Generator is able to offer all these <phone> elements directly to be used within a foreach loop:
function extract_phones(Traversable $files) {
foreach ($files as $file) {
$xml = simplexml_load_file($file);
if ($xml === false) {
continue;
}
foreach ($xml->phone as $phone) {
yield $phone;
}
}
}
As this exemplary Generator function shows, it goes over all $files, tries to load them as a SimpleXMLElement and if successfull, iterates over all <phone> elements and yields them.
That means, if the function extract_phones is called within a foreach, that loop will have every <phone> element as SimpleXMLElement:
foreach(extract_phones($inputFiles) as $phone) {
# $phone is a SimpleXMLElement here
}
So now your question asks about creating the CSV file as output. This could be done creating an SplFileObject to pass the output around and access it while processing. It basically works the same like passing the file-handle around like you do in your question but it has better semantics that do allow to change the code more easily later on (you could replace it with another object that behaves the same).
Additionally I've seen a little detail in your code that is worth for some discussion first. You're encoding the quotes as HTML entities:
trim(str_replace( "\"", """, $phone->title ), " ")
You most likely do that because you want to have HTML-Entities inside the CSV file. However, the CSV file does not need such. You also want to have the data in the CSV file as generic as possible. Whether the CSV file is used inside a HTML context later on or within a spreadsheet application should not be your concern when you convert the file-format. My suggestion is here to leave that out and deal at another place with it. A place this more belongs to, and that is later on, e.g. if you use the data from the CSV creating some HTML.
That keeps your conversion and the data clean and it also removes detailed places in your processing which not only make the code more complicate but are very often a place where we introduce flaws into our programs.
I for myself will just remove it from my example.
So let's put this all together: Get all phones from all XML files and store the fields interested in into the output CSV file:
$files = new GlobIterator(__DIR__ . '/*.xml');
$phones = extract_phones($files);
$output = new SplFileObject('file.csv', 'w');
$output->fputcsv($header = ["title", "color"]);
foreach ($phones as $phone) {
$output->fputcsv(
[
$phone->title,
$phone->color,
]
);
}
This then creates the output file you're looking for (without the HTML-entities):
title,color
"""Apple iPhone 5S""",black
"Nokia Lumia 830",black
All this needs is the generator-function I've showed above already that in itself has also straight-forward code. Everything else ships with PHP already. Here is the example code in full:
<?php
/**
* #link http://stackoverflow.com/questions/26074850/convert-multiple-xml-files-to-csv-with-simplexml
*/
function extract_phones(Traversable $files)
{
foreach ($files as $file) {
$xml = simplexml_load_file($file);
if ($xml === false) {
continue;
}
foreach ($xml->phone as $phone) {
yield $phone;
}
}
}
$files = new GlobIterator(__DIR__ . '/*.xml');
$phones = extract_phones($files);
$output = new SplFileObject('file.csv', 'w');
$output->fputcsv($header = ["title", "color"]);
foreach ($phones as $phone) {
$output->fputcsv(
[
$phone->title,
$phone->color,
]
);
}
echo file_get_contents($output->getFilename());
Thanks #Ghost for pointing me to the right direction. So here is my solution.
<?php
$filexml = array ('test.xml', 'test1.xml');
//Headers
$fp = fopen('file.csv', 'w');
$headers = array('title', 'color');
$converted_array = array_map("strtoupper", $headers);
fputcsv($fp, $converted_array, ',', '"');
//XML
foreach ($filexml as $file) {
if (file_exists($file)) {
$xml = simplexml_load_file($file);
foreach ($xml->phone as $phone) {
$values = array(
"title" => (string)$phone->title = trim(str_replace ( "\"", """, $phone->title ), " "),
"color" => (string)$phone->color
);
fputcsv($fp, $values, ',', '"');
}
echo $file . ' converted to .csv sucessfully' . '<br>';
} else {
echo $file . ' was not found' . '<br>';
}
}
fclose($fp);
?>

How do I get only unique values from CSV file array

I am building a small application that does some simple reporting based on CSV files, the CSV files are in the following format:
DATE+TIME,CLIENTNAME1,HAS REQUEST BLABLA1,UNIQUE ID
DATE+TIME,CLIENTNAME2,HAS REQUEST BLABLA2,UNIQUE ID
DATE+TIME,CLIENTNAME1,HAS REQUEST BLABLA1,UNIQUE ID
DATE+TIME,CLIENTNAME2,HAS REQUEST BLABLA2,UNIQUE ID
Now I am processing this using the following function:
function GetClientNames(){
$file = "backend/AllAlarms.csv";
$lines = file($file);
arsort($lines);
foreach ($lines as $line_num => $line) {
$line_as_array = explode(",", $line);
echo '<li><i class="icon-pencil"></i>' . $line_as_array[1] . '</li>';
}
}
I am trying to retrieve only the Clientname values, but I only want the unique values.
I have tried to create several different manners of approaching this, I understand I need to use the unique_array function, but I have no clue on exactly how to use this function.
I've tried this:
function GetClientNames(){
$file = "backend/AllAlarms.csv";
$lines = file($file);
arsort($lines);
foreach ($lines as $line_num => $line) {
$line_as_array = explode(",", $line);
$line_as_array[1] = unique_array($line_as_array[1]);
echo '<li><i class="icon-pencil"></i>' . $line_as_array[1] . '</li>';
}
}
But this gives me a very very dirty result with 100's of spaces instead of the correct data.
I would recommend you to use the fgetcsv() function when reading in csv files. In the wild csv files can be quite complicated handle by naive explode() approach:
// this array will hold the results
$unique_ids = array();
// open the csv file for reading
$fd = fopen('t.csv', 'r');
// read the rows of the csv file, every row returned as an array
while ($row = fgetcsv($fd)) {
// change the 3 to the column you want
// using the keys of arrays to make final values unique since php
// arrays cant contain duplicate keys
$unique_ids[$row[3]] = true;
}
var_dump(array_keys($unique_ids));
You can also collect values and use array_unique() on them later. You probably want to split the "reading in" and the "writing out" part of your code too.
Try using array_unique()
Docs:
http://php.net/manual/en/function.array-unique.php

How to reference the current directory name, filename and file contents with RecursiveDirectoryIterator loop?

In the script below, I'm attempting to iterate over the folders and files inside of the $base folder. I expect it to contain a single level of child folders, each containing a number of .txt files (and no subfolders).
I'm just needing to understand how to reference the elements in comments below...
Any help much appreciated. I'm really close to wrapping this up :-)
$base = dirname(__FILE__).'/widgets/';
$rdi = new RecursiveDirectoryIterator($base);
foreach(new RecursiveIteratorIterator($rdi) as $files_widgets)
{
if ($files_widgets->isFile())
{
$file_name_widget = $files_widgets->getFilename(); //what is the filename of the current el?
$widget_text = file_get_contents(???); //How do I reference the file here to obtain its contents?
$sidebar_id = $files_widgets->getBasename(); //what is the file's parent directory name?
}
}
//How do I reference the file here to obtain its contents?
$widget_text = file_get_contents(???);
$files_widgets is a SplFileInfo, so you have a few options to get the contents of the file.
The easiest way is to use file_get_contents, just like you are now. You can concatenate together the path and the filename:
$filename = $files_widgets->getPathname() . '/' . $files_widgets->getFilename();
$widget_text = file_get_contents($filename);
If you want to do something funny, you can also use openFile to get a SplFileObject. Annoyingly, SplFileObject doesn't have a quick way to get all of the file contents, so we have to build a loop:
$fo = $files_widgets->openFile('r');
$widget_text = '';
foreach($fo as $line)
$widget_text .= $line;
unset($fo);
This is a bit more verbose, as we have to loop over the SplFileObject to get the contents line-by-line. While this is an option, it'll be easier for you just to use file_get_contents.

Categories