I am trying to create a new instance of Imagick:
$original = new Imagick($array);
I do this in two different instances. One way always works, and the other way always fails, but they use the same exact array (the array isn't always identical, but for ease of explanation, I am showing an example that happens to use identical arrays). Here is a var_dump of each array:
Working Array:
array(3) { [0]=> string(20) "image_files/bbb0.jpg" [1]=> string(20) "image_files/bbb1.jpg" [2]=> string(20) "image_files/bbb2.jpg" }
Failing Array:
array(3) { [0]=> string(20) "image_files/bbb0.jpg" [1]=> string(20) "image_files/bbb1.jpg" [2]=> string(20) "image_files/bbb2.jpg" }
As you can see, they are identical, so why is my PHP dying at
$new = new Imagick($array);
Is there something different about the second array that I'm not seeing?
EDIT: Here's the code that constructs the array that is failing:
$n = $_GET["n"];
$city = preg_replace("/[0-9]/", "", $n);
$num = preg_replace("/".$city."/","",$n);
// create an array to hold directory list
$results = array();
// create a handler for the directory
$directory = '../image_files';
$handler = opendir($directory);
while ($file = readdir($handler)) {
// if file isn't this directory or its parent, add it to the results
if ($file != "." && $file != "..") {
// check with regex that the file format is what we're expecting and not something else
if (preg_match("/^".$city."[1-9][0-9]\.jpg$/i",$file)) {
if (preg_match("/^".$city.$num."\.jpg$/i",$file)) {
unlink("../image_files/".$file);
} else {
$results[] = "../image_files/" . $file;
}
} else if (preg_match("/^".$city."[0-9]\.jpg$/i",$file)) {
if (preg_match("/^".$city.$num."\.jpg$/i",$file)) {
unlink("../image_files/".$file);
} else {
$results[] = "../image_files/" . $file;
}
}
}
}
sort($results);
$i = 0;
$newResults = array();
foreach( $results as $key => $value ) {
$old = $value;
//echo "old: " . $old . " ";
if (preg_match("/[1-9][0-9]/",$value)) {
$newstr = preg_replace("/[1-9][0-9]/", $i."temp", $value);
} else if (preg_match("/[0-9]/",$value)) {
$newstr = preg_replace("/[0-9]/", $i."temp", $value);
}
$newResults[] = $newstr;
//echo "new: " . $newstr . "<br>";
rename($old,$newstr);
$i++;
}
// create an array to hold directory list
$results = array();
// create a handler for the directory
$directory = '../image_files';
$handler = opendir($directory);
while ($file = readdir($handler)) {
// if file isn't this directory or its parent, add it to the results
if ($file != "." && $file != "..") {
$old = $file;
$new = preg_replace("/temp/", "", $file);
rename("../image_files/".$old,"../image_files/".$new);
}
}
$finalResults = array();
foreach( $newResults as $key => $value ) {
$newstr = str_replace("../", "", $value);
$newstr = str_replace("temp","",$newstr);
$finalResults[] = $newstr;
}
sort($finalResults);
createMontage($finalResults,"-a",$city);
The problem was in the directory structure of the PHP files.
The "working array" is being created from the base directory.
The "failing array" is being created from a directory named actions.
It doesn't matter where the script you're running exists, it matters what loaded that script (ie. Was it loaded from the root dir via include or require? Or was is called directly www.example.com/scripts/script.php). This will determine how to construct your path to other files.
Related
I have set up a basic script that is posting an array of paths to find template files inside them; currently it's only searching two levels deep and I'm having some troubles getting my head around the logic for an extensive loop to iterate all child directories until the length is 0.
So if I have a structure like this:
./components
./components/template.html
./components/template2.html
./components/side/template.html
./components/side/template2.html
./components/side/second/template.html
./components/side/second/template2.html
./components/side/second/third/template.html
./components/side/second/third/template2.html
It's only searching up to the "side" directory for .html files when ideally I want it to check all child directories and the passed directory for .html files. Here is my working code so far:
<?php
function getFiles($path){
$dh = opendir($path);
foreach(glob($path.'/*.html') as $filename){
$files[] = $filename;
}
if (isset($files)) {
return $files;
}
}
foreach ($_POST as $path) {
foreach (glob($path . '/*' , GLOB_ONLYDIR) as $secondLevel) {
$files[] = getFiles($secondLevel);
}
$files[] = getFiles($path);
}
sort($files);
print_r(json_encode($files));
?>
PHP has the perfect solution for you built in.
Example
// Construct the iterator
$it = new RecursiveDirectoryIterator("/components");
// Loop through files
foreach(new RecursiveIteratorIterator($it) as $file) {
if ($file->getExtension() == 'html') {
echo $file;
}
}
Resources
DirectoryIterator - Manual
PHP 5 introduces iterators to iterate quickly through many elements.
You can use RecursiveDirectoryIterator to iterate recursively through directories.
You can use RecursiveIteratorIterator on the result to have a flatten view of the result.
You can use RegexIterator on the result to filter based on a regular expression.
$directory_iterator = new RecursiveDirectoryIterator('.');
$iterator = new RecursiveIteratorIterator($directory_iterator);
$regex_iterator = new RegexIterator($iterator, '/\.php$/');
$regex_iterator->setFlags(RegexIterator::USE_KEY);
foreach ($regex_iterator as $file) {
echo $file->getPathname() . PHP_EOL;
}
With iterators, there are lot of ways to do the same thing, you can also use a FilterIterator (see the example on the page)
For instance, if you want to select the files modified this week, you can use the following:
$directory_iterator = new RecursiveDirectoryIterator('.');
$iterator = new RecursiveIteratorIterator($directory_iterator);
class CustomFilterIterator extends FilterIterator {
function accept() {
$current=$this->getInnerIterator()->current();
return ($current->isFile()&&$current->getMTime()>time()-3600*24*7);
}
}
$filter_iterator=new CustomFilterIterator($iterator);
foreach ($filter_iterator as $file) {
echo $file->getPathname() . PHP_EOL;
}
Another solution is using Finder component from Symfony. It's tested and proven code. Take a look at it here: http://symfony.com/doc/current/components/finder.html
Here is working code which scans given directory $dir and all of its sub directories to any levels and return list of html files and folders containing them.
<?php
function find_all_files($dir)
{
if(!is_dir($dir)) return false;
$pathinfo = '';
$root = scandir($dir);
foreach($root as $value)
{
if($value === '.' || $value === '..') {continue;}
if(is_file("$dir/$value")) {
$pathinfo = pathinfo($dir.'/'.$value);
if($pathinfo['extension'] == 'html') {
$result[]="$dir/$value";
}
continue;
}
foreach(find_all_files("$dir/$value") as $value)
{
$result[]=$value;
}
}
return $result;
}
//call function
$res = find_all_files('physical path to folder');
print_r($res);
?>
Universal File search in folder, sub-folder :
function dirToArray($dir,$file_name) {
$result = array();
$cdir = scandir($dir);
foreach ($cdir as $key => $value)
{
if (!in_array($value,array(".","..")))
{
if (is_dir($dir . DIRECTORY_SEPARATOR . $value))
{
$result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value,$file_name);
}
else
{
if($value == $file_name){
$result[] = $value;
}
}
}
}
return $result;
}
define('ROOT', dirname(__FILE__));
$file_name = 'template.html';
$tree = dirToArray(ROOT,$file_name);
echo "<pre>".print_r($tree,1)."</pre>";
OUTPUT :
Array
(
[components] => Array
(
[side] => Array
(
[second] => Array
(
[0] => template.html
[third] => Array
(
[0] => template.html
)
)
[0] => template.html
)
[0] => template.html
)
[0] => template.html
)
Alternative of my previous answer :
$dir = 'components';
function getFiles($dir, &$results = array(), $filename = 'template.html'){
$files = scandir($dir);
foreach($files as $key => $value){
$path = realpath($dir.'/'.$value);
if(!is_dir($path)) {
if($value == $filename)
$results[] = $path;
} else if($value != "." && $value != "..") {
getFiles($path, $results);
}
}
return $results;
}
echo '<pre>'.print_r(getFiles($dir), 1).'</pre>';
OUTPUT :
Array
(
[0] => /web/practise/php/others/test7/components/side/second/template.html
[1] => /web/practise/php/others/test7/components/side/second/third/template.html
[2] => /web/practise/php/others/test7/components/side/template.html
[3] => /web/practise/php/others/test7/components/template.html
)
Here is a generic solution:
function parseDir($dir, &$files=array(), $extension=false){
if(!is_dir($dir)){
$info = pathinfo($dir);
// add all files if extension is set to false
if($extension === false || (isset($info['extension']) && $info['extension'] === $extension)){
$files[] = $dir;
}
}else{
if(substr($dir, -1) !== '.' && $dh = opendir($dir)){
while($file = readdir($dh)){
parseDir("$dir/$file", $files, $extension);
}
}
}
}
$files = array();
parseDir('components', $files, 'html');
var_dump($files);
OUTPUT
php parseDir.php
array(7) {
[0]=>
string(25) "components/template2.html"
[1]=>
string(29) "components/side/template.html"
[2]=>
string(30) "components/side/template2.html"
[3]=>
string(36) "components/side/second/template.html"
[4]=>
string(37) "components/side/second/template2.html"
[5]=>
string(42) "components/side/second/third/template.html"
[6]=>
string(43) "components/side/second/third/template2.html"
}
I have a really broken/weird INI file that looks like this.
RowID=11668
Name=SCNA DaCe
PPA
Relation=Family
RowID=31999
Name=PCA
RowID=11593
Name=CRMLEVEL
Relation=Family
If possible, end up as
array("11668" => array("name"=> "SCNA DaCe", "relation"=>"Family", "ppa"=>true));
Linebreaks are separated based on RowID instead of a proper [section] and I have no idea how I can read this, any tips on where to start?
There is inconsistent casing, certain things don't have values (like PPA by itself on a line), not all key=vals are defined for each case.
For something like this, an array of objects comes in handy.
http://www.laprbass.com/RAY_temp_dylan.php
<?php // RAY_temp_dylan.php
error_reporting(E_ALL);
echo '<pre>';
$str = <<<END
RowID=11668
Name=SCNA DaCe
PPA
Relation=Family
RowID=31999
Name=PCA
RowID=11593
Name=CRMLEVEL
Relation=Family
END;
// SIMULATE READING WITH file()
$arr = explode(PHP_EOL, $str);
// COLLECT THE NORMALIZED DATA HERE
$out = array();
$obj = new stdClass;
// USE AN ITERATOR ON EACH ROW
foreach ($arr as $row)
{
// SKIP BLANK LINES
$row = trim($row);
if (empty($row)) continue;
// FOR EACH ROWID CREATE A NEW OBJECT
if (FALSE !== strpos($row, 'RowID'))
{
// SAVE THE OLD OBJECT
$out[] = $obj;
$obj = new stdClass;
$obj->RowID = end(explode('=', $row));
}
// FOR REMAINING ELEMENTS THAT ARE KEY-VALUE PAIRS
if (FALSE !== strpos($row, '='))
{
$key = current(explode('=', $row));
$val = end(explode('=', $row));
$obj->$key = $val;
}
// FOR REMAINING ELEMENTS THAT ARE NOT KEY-VALUE PAIRS
else
{
$obj->$row = TRUE;
}
}
// SAVE LAST ELEMENT AT EOF
$out[] = $obj;
// DISCARD THE ZERO "STARTER" ELEMENT
unset($out[0]);
var_dump($out);
You would have to roll your own custom solution to parse this INI file as PHP's built in parse_ini_file is based on PHP's own php.ini format constraints. So it has to be valid PHP.
For your purposes if the requirements are as simple as key/value pairs on each line with the exception of the one-off stranded value as a default boolean true then you could do something like the following.
function my_parse_ini($ini_file_name) {
$ini = file($ini_file_name, FILE_IGNORE_NEW_LINES);
$return = array();
$row = null;
foreach ($ini as $key => $value) {
if ($value == '') {
$row = null;
continue;
}
#list($k, $v) = explode('=',$value);
if ($v === null) {
$v = true;
}
if ($row === null) {
$row = $v;
$return[$row] = array();
continue;
}
$return[$row][$k] = $v;
}
return $return;
}
/* usage */
var_dump(my_parse_ini('test.ini'));
This would output the following for your sample ini file....
array(2) {
[11668]=>
array(4) {
["Name"]=>
string(3) "PCA"
["PPA"]=>
bool(true)
["Relation"]=>
string(6) "Family"
["RowID"]=>
string(5) "31999"
}
[11593]=>
array(2) {
["Name"]=>
string(8) "CRMLEVEL"
["Relation"]=>
string(6) "Family"
}
}
I've created this code to cycle through the folders in the current directory and echo out a link to the folder, it all works fine. How would I go about using the $blacklist array as an array to hold the directory names of directories I dont want to show?
$blacklist = array('dropdown');
$results = array();
$dir = opendir("./");
while($file = readdir($dir)) {
if($file != "." && $file != "..") {
$results[] = $file;
}
}
closedir($dir);
foreach($results as $file) {
if($blocked != true) {
$fileUrl = $file;
$fileExplodedName = explode("_", $file);
$fileName = "";
$fileNameCount = count($fileExplodedName);
echo "<a href='".$fileUrl."'>";
$i = 1;
foreach($fileExplodedName as $name) {
$fileName .= $name." ";
}
echo trim($fileName);
echo "</a><br/>";
}
}
array_diff is the best tool for this job -- it's the shortest to write, very clear to read, and I would expect also the fastest.
$filesToShow = array_diff($results, $blacklist);
foreach($filesToShow as $file) {
// display the file
}
Use in_array for this.
$blocked = in_array($file, $blacklist);
Note that this is rather expensive. The runtime complexity of in_array is O(n) so don't make a large blacklist. This is actually faster, but with more "clumsy" code:
$blacklist = array('dropdown' => true);
/* ... */
$blocked = isset($blacklist[$file]);
The runtime complexity of the block check is then reduced to O(1) since the array (hashmap) is constant time on key lookup.
I am using PHP and I need to script something like below:
I have to compare two folder structure
and with reference of source folder I
want to delete all the files/folders
present in other destination folder
which do not exist in reference source
folder, how could i do this?
EDITED:
$original = scan_dir_recursive('/var/www/html/copy2');
$mirror = scan_dir_recursive('/var/www/html/copy1');
function scan_dir_recursive($dir) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $path;
if (is_dir($path)) {
$all_paths = array_merge($all_paths, scan_dir_recursive($path));
} else {
$all_paths[] = $path;
}
}
return $all_paths;
}
foreach($mirror as $mirr)
{
if($mirr != '.' && $mirr != '..')
{
if(!in_array($mirr, $original))
{
unlink($mirr);
// delete the file
}
}
}
The above code shows what i did..
Here My copy1 folder contains extra files than copy2 folders hence i need these extra files to be deleted.
EDITED:
Below given output is are arrays of original Mirror and of difference of both..
Original Array
(
[0] => /var/www/html/copy2/Copy (5) of New Text Document.txt
[1] => /var/www/html/copy2/Copy of New Text Document.txt
)
Mirror Array
(
[0] => /var/www/html/copy1/Copy (2) of New Text Document.txt
[1] => /var/www/html/copy1/Copy (3) of New Text Document.txt
[2] => /var/www/html/copy1/Copy (5) of New Text Document.txt
)
Difference Array
(
[0] => /var/www/html/copy1/Copy (2) of New Text Document.txt
[1] => /var/www/html/copy1/Copy (3) of New Text Document.txt
[2] => /var/www/html/copy1/Copy (5) of New Text Document.txt
)
when i iterate a loop to delete on difference array all files has to be deleted as per displayed output.. how can i rectify this.. the loop for deletion is given below.
$dirs_to_delete = array();
foreach ($diff_path as $path) {
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
unlink($path);
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
First you need a recursive listing of both directories. A simple function like this will work:
function scan_dir_recursive($dir, $rel = null) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
if ($rel === null) {
$path_with_rel = $path;
} else {
$path_with_rel = $rel . DIRECTORY_SEPARATOR . $path;
}
$full_path = $dir . DIRECTORY_SEPARATOR . $path;
$all_paths[] = $path_with_rel;
if (is_dir($full_path)) {
$all_paths = array_merge(
$all_paths, scan_dir_recursive($full_path, $path_with_rel)
);
}
}
return $all_paths;
}
Then you can compute their difference with array_diff.
$diff_paths = array_diff(
scan_dir_recursive('/foo/bar/mirror'),
scan_dir_recursive('/qux/baz/source')
);
Iterating over this array, you will be able to start deleting files. Directories are a bit trickier because they must be empty first.
// warning: test this code yourself before using on real data!
$dirs_to_delete = array();
foreach ($diff_paths as $path) {
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
unlink($path);
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
I've tested things and it should be working well now. Of course, don't take my word for it. Make sure to setup your own safe test before deleting real data.
For recursive directories please use:
$modified_directory = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('path/to/modified'), true
);
$modified_files = array();
foreach ($modified_directory as $file)
{
$modified_files []= $file->getPathname();
}
You can do other things like $file->isDot(), or $file->isFile(). For more file commands with SPLFileInfo visit http://www.php.net/manual/en/class.splfileinfo.php
Thanks all for the precious time given to my work, Special Thanks to erisco for his dedication for my problem, Below Code is the perfect code to acomplish the task I was supposed to do, with a little change in the erisco's last edited reply...
$source = '/var/www/html/copy1';
$mirror = '/var/www/html/copy2';
function scan_dir_recursive($dir, $rel = null) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
if ($rel === null) {
$path_with_rel = $path;
} else {
$path_with_rel = $rel . DIRECTORY_SEPARATOR . $path;
}
$full_path = $dir . DIRECTORY_SEPARATOR . $path;
$all_paths[] = $path_with_rel;
if (is_dir($full_path)) {
$all_paths = array_merge(
$all_paths, scan_dir_recursive($full_path, $path_with_rel)
);
}
}
return $all_paths;
}
$diff_paths = array_diff(
scan_dir_recursive($mirror),
scan_dir_recursive($source)
);
echo "<pre>Difference ";print_r($diff_paths);
$dirs_to_delete = array();
foreach ($diff_paths as $path) {
$path = $mirror."/".$path;//added code to unlink.
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
if(unlink($path))
{
echo "File ".$path. "Deleted.";
}
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
First do a scandir() of the original folder, then do a scandir on mirror folder. start traversing the mirror folder array and check if that file is present in the scandir() of original folder. something like this
$original = scandir('path/to/original/folder');
$mirror = scandir('path/to/mirror/folder');
foreach($mirror as $mirr)
{
if($mirr != '.' && $mirr != '..')
{
if(in_array($mirr, $original))
{
// do not delete the file
}
else
{
// delete the file
unlink($mirr);
}
}
}
this should solve your problem. you will need to modify the above code accordingly (include some recursion in the above code to check if the folder that you are trying to delete is empty or not, if it is not empty then you will first need to delete all the file/folders in it and then delete the parent folder).
I want to use a function to recursively scan a folder, and assign the contents of each scan to an array.
It's simple enough to recurse through each successive index in the array using either next() or foreach - but how to dynamically add a layer of depth to the array (without hard coding it into the function) is giving me problems. Here's some pseudo:
function myScanner($start){
static $files = array();
$files = scandir($start);
//do some filtering here to omit unwanted types
$next = next($files);
//recurse scan
//PROBLEM: how to increment position in array to store results
//$next_position = $files[][][].... ad infinitum
//myScanner($start.DIRECTORY_SEPARATOR.$next);
}
any ideas?
Try something like this:
// $array is a pointer to your array
// $start is a directory to start the scan
function myScanner($start, &$array){
// opening $start directory handle
$handle = opendir($start);
// now we try to read the directory contents
while (false !== ($file = readdir($handle))) {
// filtering . and .. "folders"
if ($file != "." && $file != "..") {
// a variable to test if this file is a directory
$dirtest = $start . DIRECTORY_SEPARATOR . $file;
// check it
if (is_dir($dirtest)) {
// if it is the directory then run the function again
// DIRECTORY_SEPARATOR here to not mix files and directories with the same name
myScanner($dirtest, $array[$file . DIRECTORY_SEPARATOR]);
} else {
// else we just add this file to an array
$array[$file] = '';
}
}
}
// closing directory handle
closedir($handle);
}
// test it
$mytree = array();
myScanner('/var/www', $mytree);
print "<pre>";
print_r($mytree);
print "</pre>";
Try to use this function (and edit it for your demands):
function getDirTree($dir,$p=true) {
$d = dir($dir);$x=array();
while (false !== ($r = $d->read())) {
if($r!="."&&$r!=".."&&(($p==false&&is_dir($dir.$r))||$p==true)) {
$x[$r] = (is_dir($dir.$r)?array():(is_file($dir.$r)?true:false));
}
}
foreach ($x as $key => $value) {
if (is_dir($dir.$key."/")) {
$x[$key] = getDirTree($dir.$key."/",$p);
}
}
ksort($x);
return $x;
}
It returns sorted array of directories.