find regex in glob() search filesin directory php - php

I'm serching files in a directory
PROBLEM
I need to retrieve files whose directory matches with the word entered
I need something like: glob (gifs/[like $varSearched]/*.gif)
Directory
gifs/hello/1.gif
gifs/hello/2.gif
gifs/hello/3.gif
gifs/claps/1.gif
gifs/claps/2.gif
gifs/wow/1.gif
gifs/wow/2.gif
PATH PHP (it works but its needed to type all the file directory "hello", "wow", "claps" to retrieve the results). What I need is to type one or two letter only to retrieve the results
$dir="o"; // "o" is the searched term
$mdir = "../gifs/".$dir."/";
$files = glob($mdir.'*.gif');
foreach ($files as $gif){
$title = basename(dirname($gif));
$arr[] = $title." - ".$gif;
}
$arr= implode("",$arr);
echo $arr;
EXPECTED RESULTS
gifs/hello/1.gif
gifs/hello/2.gif
gifs/hello/3.gif
gifs/wow/1.gif
gifs/wow/2.gif

As in case with file name, you can add * as a placeholder for any symbol in directory name:
$mdir = "../gifs/*".$dir."*/"; // see `*` here?
$files = glob($mdir.'*.gif');
// rest of the code here

You can also do this.
<?php
$search = "c";
$dir = "gifs/";
$folder = exec( "ls -d ".$dir.$search."*" );
$files = glob($folder.'/*.gif');
foreach ( $files as $gif ) {
$title = basename( dirname( $gif ) );
$arr[] = $title." - ".$gif;
}
print_r( $arr );
$arr = implode( "", $arr );
//echo $arr."\n";
Replacing the $search = "c"; with whatever directory letter you want and replacing $dir = "gifs/"; with whatever your filepath is.
Print_r with search letter "c"
Array
(
[0] => claps - gifs/claps/1.gif
[1] => claps - gifs/claps/2.gif
)
Print_r with search letter "h"
Array
(
[0] => hello - gifs/hello/1.gif
[1] => hello - gifs/hello/2.gif
[2] => hello - gifs/hello/3.gif
)
You could also wrap this in a function and pass it the two arguments $search and $dir and add a return $arr at the end.

Related

Assistance with RegexIterator and File Filtering

I have the following so far:
function find_missing_webp_images() {
$wp_site_root = get_home_path();
$folder = $wp_site_root . 'wp-content/uploads';
$jpg_pattern = '/\.jpg$/';
$jpeg_pattern = '/\.jpeg$/';
$png_pattern = '/\.png$/';
$gif_pattern = '/\.gif$/';
$webp_pattern = '/\.webp$/';
$dir = new RecursiveDirectoryIterator( $folder );
$ite = new RecursiveIteratorIterator( $dir );
$jpg_files = new RegexIterator( $ite, $jpg_pattern, RegexIterator::MATCH );
$jpeg_files = new RegexIterator( $ite, $jpeg_pattern, RegexIterator::MATCH );
$png_files = new RegexIterator( $ite, $png_pattern, RegexIterator::MATCH );
$gif_files = new RegexIterator( $ite, $gif_pattern, RegexIterator::MATCH );
$webp_files = new RegexIterator( $ite, $webp_pattern, RegexIterator::MATCH );
}
And I have a couple of questions:
I'm not understanding completely how to access the values saved in $jpg_files from the result of the RegexIterator. If I var_dump($jpg_files); I get:
object(RegexIterator)#1490 (1) { ["replacement"]=> NULL }
If I var_dump($jpg_files->RegexIterator); I get NULL too.
Using foreach($jpg_files as $jpg_file) will iterate through all the values, but array_filter won't work. I was trying to do the following:
$jpg_files_scaled = array_filter( $jpg_files, function( $val ) {
foreach ( $jpg_files as $jpg_file ) {
if ( $val === preg_replace( '/\.jpg$/', '', $jpg_file ) . '-scaled.jpg' ) {
return false;
}
}
return true;
});
But after that var_dump($jpg_files_scaled); returns NULL. Even if I just straight up return true it's still NULL.
I can do the following to convert to an array:
$jpg_files_scaled = [];
foreach ($jpg_files as $jpg_file) {
array_push( $jpg_files_scaled, $jpg_file );
}
But there seems like there should be a better way.
My goal with this is to do the following:
• Get a list of all image files in the /wp-content/uploads/ directory
• Filter out any image if the -scaled version also exists. For example if the following 2 images both exist in the same directory:
image.jpg
image-scaled.jpg
Then the image.jpg should be filtered out of the list. (the same is true for the png/jpeg/gif images too).
• After that is done, the goal is to be able to check if any jpg/jpeg/png/gif version of an image exists, but the webp version is missing.
The WebP files follow the naming convention of image.jpg.webp or image.png.webp. So for example, if image.jpg exists, but image.jpg.webp does not, then image.jpg should be a part of the final list.
Basically looking for a way to search through very large lists of images for any missing WebP versions of images. Any help on any of the above would be very much appreciated.
************** EDIT/UPDATE ************
Thank you both! I used some of both of your answers, and here's what I have (seems to be working so far, but I need to test further and let me know if you see any potential issues with what I have):
function find_missing_webp_images() {
$wp_site_root = get_home_path();
$folder = $wp_site_root . "wp-content/uploads";
$img_pattern = "/\.(jpg|jpeg|png|gif)$/";
$webp_pattern = "/\.webp$/";
$dir = new RecursiveDirectoryIterator( $folder );
$ite = new RecursiveIteratorIterator( $dir );
$img_files_obj = new RegexIterator( $ite, $img_pattern, RegexIterator::MATCH );
$webp_files_obj = new RegexIterator( $ite, $webp_pattern, RegexIterator::MATCH );
$img_files = [];
$webp_files = [];
// Convert RegexIterator objects to associative arrays with pathname as key and extensions as value
foreach ( iterator_to_array( $img_files_obj ) as $img_file ) {
$img_files[ $img_file->getPathname() ] = $img_file->getExtension();
}
foreach ( iterator_to_array( $webp_files_obj ) as $webp_file ) {
$webp_files[ $webp_file->getPathname() ] = $webp_file->getExtension();
}
foreach ( $img_files as $img_file => $ext ) {
$ext_length = strlen( $ext ) + 1;
// Remove images that have a WebP version
if ( isset( $webp_files[ $img_file . ".webp" ] ) ) {
unset( $img_files[ $img_file ] );
continue;
}
// Remove images that have a -scaled version (WebP images not created for these)
if ( isset( $img_files[ substr( $img_file , 0, -$ext_length ) . '-scaled.' . $ext ] ) ) {
unset( $img_files[ $img_file ] );
}
}
// Sort images so results are easier to read
ksort( $img_files );
foreach ( $img_files as $file => $type ) {
echo "<pre>", $file, "</pre>";
}
}
find_missing_webp_images();
If $jpg_files is a RegexIterator, I suspect array_filter() is not the right option since it expects the first parameter to be an array instead of an object. The definition is type strict as below:
array_filter ( array $array , callable|null $callback = null , int $mode = 0 ) : array
foreach works ok because it internally calls current(),key(),next(),valid() methods to run as a loop. So, my suggestion is to use foreach itself to filter out images who do not have a -scaled file for them.
<?php
$scaled = [];
$unscaled = [];
foreach($jpg_files as $jpg_file){
if(preg_match('/(.+)\-scaled\.jpg/', $jpg_file, $matches) === 1){
$scaled[ $matches[1] ] = true;
unset($unscaled[ $matches[1].'.jpg' ]);
}else{
$image_name = substr($jpg_file, 0, -4); // stripping off the extension
if(!isset( $scaled[ $image_name ] )){
$unscaled[ $jpg_file ] = true;
}
}
}
$unscaled = array_keys( $unscaled );
print_r($unscaled);
In the above approach, we use 2 associative arrays to keep track of scaled and unscaled images. If a particular image is scaled, unset its unscaled version from unscaled. If an image is unscaled, add it in unscaled unless we find its cousin
scaled version to unset it in future in the loop.
For the first question, you can make use of iterator_to_array:
$jpg_files_scaled = array_filter(iterator_to_array($jpg_files), function( $val ) {
// ...
});
The variable $val is of type SplFileInfo and you can get the filename using the method getFilename.
For the second question, to get a list of all image files in the /wp-content/uploads/ directory you might use a single pattern to get all the files with extensions in a single run using \.(?:jpe?g|png|gif)$
Then create a list of only files that end with -scaled using \.(?:jpe?g|png|gif)-scaled$ and create a list for files that end with \.webp$
With those 3 collections, you could loop the collection for the regular filenames, and based on the presence of a -scaled and .webp version in the other 2 collections, create a final list of accepted filenames.

Break Path into multiple Paths

I have the following path for example:
/Test1/Test2/Test3
Sometimes this path can be for example:
/Test1/Test2/Test3/Test4/Test5 and so on...
What I would like to do is take this unknown path and translate it into sections which will ultimately result in a navigation URL such as:
/Test1
/Test1/Test2
/Test1/Test2/Test3
and so on...
It's difficult to supply you with any code examples because many of the things I have attempted have resulted in no good results.
I assume I need to explode() the path using / as the delimiter and then splice it together somehow. I'm really at a loss here.
Does anyone have any suggestions I can try?
<?php
$path = '/Test1/Test2/Test3/Test4/Test5';
$explode = explode('/', $path);
$count = count($explode);
$res = '';
for($i = 1; $i < $count; $i++) {
echo $res .= '/' . $explode[$i];
echo '<br/>';
}
Returns:
/Test1
/Test1/Test2
/Test1/Test2/Test3
/Test1/Test2/Test3/Test4
/Test1/Test2/Test3/Test4/Test5
Here is how you get your array segments:
$path = '/Test1/Test2/Test3/Test4/Test5'; // or whatever your path is
$segments = explode('/', ltrim('/',$path));
If I understand you, then what you want to do is to build an array that is like
Array(
[0] => '/Test1'
[1] => '/Test1/Test2'
...
)
So you could just loop through your array and build up this new array
$paths_from_segments = array();
$segment_count = count($sgements);
$path_string = '';
foreach($sgement as $segment) {
$path_string .= '/' . $segment;
$paths_from_segments[] = $path_string;
}
var_dump($paths_from_segments);
Not exactly what you mean by "splice it together", but from the sounds of it you're looking for PHP's implode(), which is explode() in reverse.
explode("/", "test1/test2");
// result:
// Array
// (
// [0] => test1
// [1] => test2
// )
implode("/", Array("test1", "test2"));
// result:
// "test1/test2"

parse string for subdomain in php

How can i find if a string has subdomain existing if there is no scheme / host present.
eg: $url="sub.main.com/images/sample.jpg";
I am trying to parse the url for images, and I am using parse_url for most cases.
But given the url strings can some in different flavors,
eg:
/images/sample.jpg
//main.com/images/sample.jpg
images/sample.jpg
etc, I am trying to address the different cases one by one. Right now, I am finding it hard to detect if a string has subdomain present or not.
so for a string such as $url="sub.main.com/images/sample.jpg";` i would like to extract the subdomain, and for a string such as images/sample.jpg, i would like to find out that there is no subdomain
Interesting problem. I've fiddled around with this for a while; this method inevitably isn't perfect, but it may start you down the right path.
My solution begins with the two source files in this repository: https://github.com/usrflo/registered-domain-libs/tree/master/PHP
First, you may need to modify regDomain.inc.php to change an instance of $signingDomainParts = split('\.', $signingDomain); to $signingDomainParts = preg_split('/\./', $signingDomain); if split is deprecated in your php version.
Once you've got those saved, try this testing code, I put all of the URLs mentioned in the thread here as test cases:
<?php
require_once("effectiveTLDs.inc.php");
require_once("regDomain.inc.php");
$tests = Array("/images/sample.jpg","//main.com/images/sample.jpg","images/sample.jpg", "sub.main.com/images/sample.jpg", "http://www.example.com/www.google.com/sample.jpg", "amazon.co.uk/images/sample.jpg", "amazon.com/images/sample.jpg", "http://sub2.sub.main.co.uk/images/sample.jpg", "sub2.sub.main.co.uk/images/sample.jpg");
foreach($tests as $test)
{
echo "Attempting $test.<BR/>";
$one = parse_url($test);
if(!array_key_exists("host", $one))
{
echo "Converting to: http://$test";
echo "<BR/>";
$one = parse_url("http://$test");
}
if(!$one){echo "<BR/>";continue;}
echo "parse_url parts: ";
print_r($one);
echo "<BR/>";
if($one && array_key_exists("host", $one))
{
$domain = getRegisteredDomain($one["host"], $tldTree);
if(sizeof($domain))
{
$two = explode(".", $domain);
echo "domain parts: ";
print_r($two);
echo "<BR/>";
if(sizeof($two))
{
$three = array_diff(explode(".", $one["host"]), $two);
if(sizeof($three))
{
echo "Hark! A subdomain!: ";
print_r($three);
echo "<BR/>";
}
}
}
}
echo "<BR/>";
}
?>
This code identifies the following of the test-cases as having subdomains:
Attempting sub.main.com/images/sample.jpg.
Hark! A subdomain!: Array ( [0] => sub )
Attempting http://www.example.com/www.google.com/sample.jpg.
Hark! A subdomain!: Array ( [0] => www )
Attempting http://sub2.sub.main.co.uk/images/sample.jpg.
Hark! A subdomain!: Array ( [0] => sub2 [1] => sub )
Attempting sub2.sub.main.co.uk/images/sample.jpg.
Hark! A subdomain!: Array ( [0] => sub2 [1] => sub )
Try this code
<?php
$url = 'sub.main.com/images/sample.jpg';
$arr = explode('/',$url);
$domain = $arr[0];
$string = $arr[1];
$arr2 = explode('.',$domain);
if(count($arr2)>2) {
$subdomain = $arr2[0];
echo $subdomain;
}
?>
<?php
$url = 'http://sub.main.com/images/sample.jpg';
$arr = explode('/',$url);
$pieces = parse_url($url);
$domain = isset($pieces['host']) ? $pieces['host'] : '';
if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs))
{
$main_domain=$regs['domain'];
}
$host=$pieces['host'];
$path=$pieces['path'];
if($host != $main_domain)
{
$arr2 = explode('.',$host);
$subdomain = $arr2[0];
echo $subdomain;
}
$string=substr($path,1,strlen($path));
?>
Try the following:
<?php
$url="sub.main.com/images/sample.jpg";
preg_match('#^(?:http://)?([^.]+).?([^/]+)#i',$url, $hits);
print_r($hits);
?>
This should output something like:
Array ( [0] => sub.main.com [1] => sub [2] => main.com )

PHP batch file renaming

I have a bunch of files named like this...
full-file(1).jpg
full-file(10).jpg
full-file(11).jpg
full-file(12).jpg
full-file(2).jpg
etc...
I'm trying to figure out the most efficient way to rename all these files using PHP so that they get renamed like this...
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
I've got as far as reading all the files from a folder and looping through them but not sure about the best way to remove the brackets and make the number 4 digits with leading 0.
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
echo $file;
}
Use a regular expression to get the digit, and then zero-pad it using sprintf():
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
// Capture \d+ into $matches[1]
preg_match('/\((\d+)\)/', $file, $matches);
// Pad it with %04d in sprintf()
$newfile = sprintf("full-file%04d.jpg", $matches[1]);
}
Example:
php > $file = 'full-file(12).jpg';
php > preg_match('/\((\d+)\)/', $file, $matches);
php > $newfile = sprintf("full-file%04d.jpg", $matches[1]);
php > echo $newfile;
// full-file0012.jpg
Update (for more flexible filenames):
To please the downvoter I can only assume wanted more flexible filenames, expand the regular expression:
$image_files = get_files($thumbpath);
foreach($image_files as $index=>$file) {
preg_match('/([^(]+)\((\d+)\)(.+)/', $file, $matches);
$newfile = sprintf("%s%04d%s", $matches[1], $matches[2], $matches[3]);
// And rename the file
if (!rename($file, $newfile)) {
echo "Could not rename $file.\n";
}
else echo "Successfully renamed $file to $newfile\n";
}
The pattern matches first, everything up to the the first ( with ([^(]+), followed by the number via (\d+), and everything remaining via (.*).
You can use a mixture of REGEXP (remove brackets) and string padding (to force four digits).
Note I use a replacement callback to do both operations in one place.
$files = array(
'full-file(1).jpg',
'full-file(10).jpg',
'full-file(11).jpg',
'full-file(12).jpg',
'full-file(2).jpg'
);
function pr_callback($match) {
return str_pad($match[1], 4, '0', STR_PAD_LEFT);
}
foreach($files as $file)
echo preg_replace_callback('/\((\d+)\)/', pr_callback, $file).'<br />';
Outputs:
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
I haven't seen anyone recommend sscanf() yet.
<?php
$files = array(
"full-file(1).jpg",
"full-file(10).jpg",
"full-file(11).jpg",
"full-file(12).jpg",
"full-file(2).jpg",
);
foreach ($files as $file) {
$n = sscanf($file, "full-file(%d).jpg");
printf("full-file%04d.jpg\n", $n[0]);
}
returns:
full-file0001.jpg
full-file0010.jpg
full-file0011.jpg
full-file0012.jpg
full-file0002.jpg
This only works if "full-file" is the actual name of your file, of course. sscanf() is not a regex parser, it merely extracts data using printf()-style format strings ... though it does do some more advanced format recognition than is documented at http://php.net/sscanf . If you need to handle other filenames, you can extend the format string:
<?php
$files = array(
"this-file(1).jpg",
"full-file(10).jpg",
"foo(11).jpg",
"blarg(12).jpg",
"full-file(2).jpg",
);
foreach ($files as $file) {
$n = sscanf($file, "%[a-z-](%d).jpg");
printf("%s%04d.jpg\n", $n[0], $n[1]);
}
returns:
this-file0001.jpg
full-file0010.jpg
foo0011.jpg
blarg0012.jpg
full-file0002.jpg
Assuming your files aren't actually called full-file(0000).jpg:
<?php
$arr = array('full-file(1).jpg',
'full-file(10).jpg',
'full-file(11).png',
'full-file(12).jpg',
'full-file(2).gif',
'abc(12345).jpg',
'def(99).jpg',
'xzy-file(100).jpg');
function format_filename($matches){
return $matches[1].sprintf("%04d",$matches[3]).'.'.$matches[5];
}
function process_array(&$value){
$value = preg_replace_callback('/^(.*?)(\()(\d+)(\)).(jpg|png|gif)/','format_filename',$value);
}
array_walk($arr,'process_array');
print_r($arr);
/*
Array
(
[0] => full-file0001.jpg
[1] => full-file0010.jpg
[2] => full-file0011.png
[3] => full-file0012.jpg
[4] => full-file0002.gif
[5] => abc12345.jpg
[6] => def0099.jpg
[7] => xzy-file0100.jpg
)
*/
?>
Use code:
preg_match('/^(.*?)\((\d+)\)(.*)$/', $name, $m);
$name = sprintf("%s%04d%s", $m[1], $m[2], $m[3]);
See and test it here.
You will need str-pad(). A sample soon...
EDIT 1: solution using str_pad and preg_replace_callback.
OBS: Anonymous functions only in php5.3+.
foreach ($image_files as $file)
{
$o = preg_replace_callback(
"|\((\d+)\)|", function($matches)
{
$r = str_pad($matches[1], 4, '0', STR_PAD_LEFT);
return $r;
}
, $file);
echo $o . "\n";
}

list all defined constants from a file in php

I have some php files that includes some language constants
define("_SEARCH","Search");
define("_LOGIN","Login");
define("_WRITES","writes");
define("_POSTEDON","Posted on");
define("_NICKNAME","Nickname");
now I need to read each file and list all constants and their values
and to return an output like this :
constant name :
value is :
so I think there should be function to list all defined constants of a given php file.
I'm aware of functions like token_get_all or get_defined_constants but i wasn't able to do it.
If the files do contain nothing but define statements, you can use get_defined_constants:
function getUserDefinedConstants() {
$constants = get_defined_constants(true);
return (isset($constants['user']) ? $constants['user'] : array());
}
$constantsBeforeInclude = getUserDefinedConstants();
include('file.php');
$constantsAfterInclude = getUserDefinedConstants();
$newConstants = array_diff_assoc($constantsAfterInclude, $constantsBeforeInclude);
What it does is basically: get_defined_constants(true) gives us an array of arrays with all available constants, sorted by sections (core, user, ..) - the array under the key 'user' gives us all user-defined constants that we defined in our php code using define, up to that point. array_diff_assoc gives us the difference between this array before and after the file got included.. and that is exactly a list of all constants that got defined in that specific file (as long as there is none of the declarations a duplicate, meaning a constant with that exact name has been defined before - but this would cause an error anyway).
this is the php script you need:
<?php
//remove comments
$Text = php_strip_whitespace("your_constants_file.php");
$Text = str_replace("<?php","",$Text);
$Text = str_replace("<?","",$Text);
$Text = str_replace("?>","",$Text);
$Lines = explode(";",$Text);
$Constants = array();
//extract constants from php code
foreach ($Lines as $Line) {
//skip blank lines
if (strlen(trim($Line))==0) continue;
$Line = trim($Line);
//skip non-definition lines
if (strpos($Line,"define(")!==0) continue;
$Line = str_replace("define(\"","",$Line);
//get definition name & value
$Pos = strpos($Line,"\",\"");
$Left = substr($Line,0,$Pos);
$Right = substr($Line,$Pos+3);
$Right = str_replace("\")","",$Right);
$Constants[$Left] = $Right;
}
echo "<pre>";
var_dump($Constants);
echo "</pre>";
?>
The result will be something similar to this:
array(5) {
["_SEARCH"]=>
string(6) "Search"
["_LOGIN"]=>
string(5) "Login"
["_WRITES"]=>
string(6) "writes"
["_POSTEDON"]=>
string(9) "Posted on"
["_NICKNAME"]=>
string(8) "Nickname"
}
Late to the game here but I had a similar issue. You could use an include() substitute/wrapper function that logs constants in an accessible global array.
<?php
function include_build_page_constants($file) {
global $page_constants ;
$before = get_defined_constants(true);
include_once($file);
$after = get_defined_constants(true);
if ( isset($after['user']) ) {
if ( isset($before['user']) ) {
$current = array_diff_assoc($after['user'],$before['user']);
}else{
$current = $after['user'];
}
$page_constants[basename($file)]=$current;
}
}
include_and_build_page_constants('page1.php');
include_and_build_page_constants('page2.php');
// test the array
echo '<pre>'.print_r($page_constants,true).'</pre>';
?>
This will result in something like:
Array
(
[page1.php] => Array
(
[_SEARCH] => Search
[_LOGIN] => Login
[_WRITES] => writes
[_POSTEDON] => Posted on
[_NICKNAME] => Nickname
)
[page2.php] => Array
(
[_THIS] => Foo
[_THAT] => Bar
)
)
Assuming that you want to do this on runtime, you should take a look at PHP Reflection, specifically at the ReflectionClass::getConstants() which lets you do exactly what you seem to want.
I too had the same problem. I went from jondinham's suggestion, but I prefer to use regex, as it is a bit easier to control and flexible. Here's my version of the solution:
$text = php_strip_whitespace($fileWithConstants);
$text = str_replace(array('<?php', '<?', '?>'), '', $text);
$lines = explode(";", $text);
$constants = array();
//extract constants from php code
foreach ($lines as $line) {
//skip blank lines
if (strlen(trim($line)) == 0)
continue;
preg_match('/^define\((\'.*\'|".*"),( )?(.*)\)$/', trim($line), $matches, PREG_OFFSET_CAPTURE);
if ($matches) {
$constantName = substr($matches[1][0], 1, strlen($matches[1][0]) - 2);
$constantValue = $matches[3][0];
$constants[$constantName] = $constantValue;
}
}
print_r($constants);

Categories