PHP - get all filenames in dir by pattern - php

I found this code to iterate through files in folder:
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
while (($file = readdir($dh)) !== false) {
???????????
}
closedir($dh);
}
Files have names like: project-1-0, project-1-1, project-5-14, project-6-21 ...
Now I need to filter all files and get only these that begins with "project-1-??" How could I do that, should I use blob() function? Is there any simple example? Im using laravel so maybe there is a function in it for that but i couldnt find something useful

glob is very much what you want here. However, being as you appear to be using Laravel (due to the tag), you should look at Laravel's FileSystem class (accessible using the File facade): 4.2 docs. It provides a wrapper for the standard PHP file functions (though doesn't actually add anything to the mix really for your purposes).
You can do stuff like this:
if (File::isDirectory($dir)) {
foreach (File::glob($dir.'/project-1-*') as $projectDir) {
$actualDir = substr($projectDir, strlen($dir) + 1); // trim the base directory
}
}
But if you want a more powerful file-finding system, you can use Symfony's Finder component (docs), which is already included in your project if you're using Laravel (as some Laravel Filesystem methods use it) and you'll be able to do things like this:
$dirs = Finder::create()->in($dir)->depth(0)->directories()->name('project-1-*');
// or, if you want to use regex, this should work
$dirs = Finder::create()->in($dir)->depth(0)->directories()->name('/^project-1-/');
Now, Finder returns an iterator, so you can use PHP's iterator_to_array function to turn it into an array if you need to use it as an array (that said, an iterator is better if you don't need it as an array, for instance foreaching over it).

Related

Laravel: check file existence without extension

I'm saving files in server only with a md5 hashed name without extension.
I noticed laravel Storage::exists() method will return true even if file does not exists. in fact laravel assume file name as a directory name. is there any way to force laravel to check file existence ?
Actually, it's because the way PHPs file_exists function works. Anyways, you may use something like this:
if(is_file('68e109f0f40ca72a15e05cc22786f8e6')) {
// ...
}
Also, you may try glob like this:
$all = glob('*'); // Read everything from current directory in array
if(in_array('68e109f0f40ca72a15e05cc22786f8e6', $all)) {
// ...
}
There are more ways to do this but why not use an extension?

Pimcore custom classes

I wrote custom classes and want to use them in pimcore application.
I took them to /website/lib/Custom directory on server. Afterwards, I wrote recursive script includer for each Class located in the directory and included that script in /index.php file.
It is absolutely not pimcore standard but it works.
In pimcore/config/startup.php exists snippet:
$autoloaderClassMapFiles = [
PIMCORE_CONFIGURATION_DIRECTORY . "/autoload-classmap.php",
PIMCORE_CUSTOM_CONFIGURATION_DIRECTORY . "/autoload-classmap.php",
PIMCORE_PATH . "/config/autoload-classmap.php",
];
$test = PIMCORE_ASSET_DIRECTORY;
foreach ($autoloaderClassMapFiles as $autoloaderClassMapFile) {
if (file_exists($autoloaderClassMapFile)) {
$classMapAutoLoader = new \Pimcore\Loader\ClassMapAutoloader([$autoloaderClassMapFile]);
$classMapAutoLoader->register();
break;
}
}
I guess that this provides inclusion of all those classes put into returning array from autoload-classmap.php.
Having in mind that /pimcore/config/autoload-classmap.php exists, the mentioned loop would break at first iteration so classes that I would put into custom autoload-classmap are not going to be included in project.
My question is can I change files from /pimcore directory and expect that everything would be fine after system update?
No, you should not overwrite anything in the pimcore directory, since the files in there get overwritten by the update mechanism.
You can do what you want by using the /website/config/startup.php which will not get overwritten:
https://www.pimcore.org/wiki/display/PIMCORE4/Hook+into+the+startup-process
But instead of loading all your classes as you did, take advantage of the autoloader by adding this to the /website/config/startup.php:
// The first line is not absolutely necessary, since the $autoloader variable already gets
// set in the /pimcore/config/startup.php, but it is a more future-proof option
$autoloader = \Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Custom');
If you are properly using namespaces and naming your files correctly that's all you need to do.

Laravel - Possible to detect if package is running from workbench or vendor?

I am sure with some hacking I could find a way to do this but I am hoping there is an elegant solution. I need to be able to detect when my package is running from the workbench of if the package is installed and running as a 3rd party.
You can create a method in your main package class to do the check, like this:
MyClass {
protected function getPackageLocation()
{
// Check if the path to this file contains 'workbench'
if (strpos(realpath(__FILE__), 'workbench') !== false) {
return 'workbench';
}
// If not, it's in vendor folder
return 'vendor';
}
}
If you need to check it from outside your package, you can always make the function public.
To make it more reliable, you can check for workbench/your_vendor/your_package/in the conditional, or even make it dynamic with something like:
// untested: translate namespace to path format
$needle = 'workspace/' . strtolower(str_replace("_", "/", __NAMESPACE__));
if (strpos(realpath(__FILE__), $needle) !== false) {
...

Zipping files in a folder without a parent folder in the zipped file

This maybe a silly question to most, but I have the following folder, which I want to zip.
FolderI
Folder1
Folder2
Folder3
..
..
FolderN
Now, zipping up FolderI into a zipFile.zip is pretty easy because of so many resources online, specially here! But, I want to create zipFile.zip, containing Folder1...FolderN, such that when the file is unzipped, it would directly populate the current directory with Folder1...FolderN,instead of creating FolderI, which has Folder1...FolderN in it.
Some of the things I've been trying are:
$zip = new ZipArchive();
// open archive
if ($zip->open('my-archive.zip', ZIPARCHIVE::CREATE) !== TRUE) {
die ("Could not open archive");
}
// initialize an iterator
// pass it the directory to be processed
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator("FolderI/"));
// iterate over the directory
// add each file found to the archive
foreach ($iterator as $key=>$value) {
$zip->addFile(realpath($key), $key) or die ("ERROR: Could not add file: $key");
}
// close and save archive
$zip->close();
This was picked up from compress/archive folder using php script and I tried to change it around. Didn't work though - The FolderI is still zipped. But, when I unzip the my-archive.zip, I get a FolderI and then Folder1..FolderN inside it. I also saw code from #Alix somewhere here, which did the same thing. But, didn't really follow much.
Any kind of help would be great. If not the code, at least point me to the right direction.
Thanks
****Anyone here knows how to do this? ****
#Mods and Admins: Is the solution too simple or PHP can't do this at all?
The problem that is occuring here is that the $key in the iterator is the full path (since the RecursiveDirectoryIterator::KEY_AS_PATHNAME flag is used by default), which includes the relative part specified in the RecursiveDirectoryIterator constructor.
So for example we have the following hierarchy:
folderI
├─ folder1
│ ├─ item.php
│ └─ so.php
└─ folder2
└─ item.html
And an iterator created with new RecursiveDirectoryIterator("folderI").
When you echo out the $key you will get the following for item.php
folderI/folder1/item.php
Similarly, if we did new RecursiveDirectoryIterator("../somewhere/folderI"), then $key would look like:
../somewhere/folderI/folder1/item.php
You wish to get the path to the file, without including the path used to create the iterator. Thankfully, the RecursiveDirectoryIterator has a method whose sole purpose in life is to do exactly this.
RecursiveDirectoryIterator::getSubPathname()
You can use the following which will be easier
$newKey = $iterator->getSubPathname();
This will return the following for item.php, given the same folder structure shown above:
folder1/item.php
The method can be used like so:
foreach ($iterator as $key=>$value) {
$zip->addFile(realpath($key), $iterator->getSubPathname()) or die ("ERROR: Could not add file: $key");
}
Aside: we can use $iterator->getSubPathname() here because the RecursiveIteratorIterator passes along any calls to unknown methods to the "inner iterator", which in this case is the RecursiveDirectoryIterator. Equally, you could do $iterator->getInnerIterator()->getSubPathname(), both do the same job.
As far as directions go, I can offer this:
This looks a bit overcomplicated to me as it is, but you can pile on by adding a foreach loop to create those iterators like so:
foreach (glob('./*', GLOB_ONLYDIR) as $dir_name) {
// iterator
// add file
}
Glob could probably do a lot more for you, possibly eliminate those iterator objects in favour of just iterating over glob output.

using PHP.scandir() to scan files and then require_once them

this is my third question so far on stackoverflow :D
i am defining files and their location on my first_run.php files,
the files that i define here is those files containing classes, helper functions
and any other files required
at early development, this first_run.php contains only a few lines of codes
but the line is increasing gradually as i add some new classes or new files to be included
and since i group the file's location inside a particular folder, i figure that maybe i can scan the folder, put the name of the files retrieved into an array and then loop the require_once, so that i dont have to edit first_run.php every time i add a new file inside the folder.
my fisrt approach is using scandir()
before:
defined('INC_PATH') ? null : define('INC_PATH', SITE_ROOT.DS.'includes');
defined('MEMBERS') ? null : define('MEMBERS', INC_PATH.DS.'models'.DS.'members');
require_once(MEMBERS.DS.'member.php');
require_once(MEMBERS.DS.'phone.php');
require_once(MEMBERS.DS.'profile.php');
require_once(MEMBERS.DS.'talent.php');
require_once(MEMBERS.DS.'profile_picture.php');
require_once(MEMBERS.DS.'audio.php');
require_once(MEMBERS.DS.'video.php');
require_once(MEMBERS.DS.'gallery.php');
require_once(MEMBERS.DS.'statistik.php');
require_once(MEMBERS.DS.'inbox.php');
require_once(MEMBERS.DS.'comment.php');
require_once(MEMBERS.DS.'picked_stat.php');
require_once(MEMBERS.DS.'log.php');
after is something like:
$member_files = scandir(MEMBERS);
foreach($member_files as $member_file):
require_once(MEMBERS.DS.$member_file);
endforeach;
i havent try the 'after' code though.
is this possible?? or is there any other approach?? or should i just leave it that way (keep adding the lines without scanning the files)
thanks in advance
Consider using Autoloading instead.
With autoloading, you do not have to bother with including files at all. Whenever you instantiate a new class that is not known to PHP at that point, PHP will trigger the registered autoload function. The function includes the required files then. This way, you only load what you need when you need it, which should increase performance.
Simple example with PHP5.3
spl_autoload_register(function($className) {
include "/path/to/lib/and/$className.php";
});
$foo = new Foo;
When you call new Foo, the registered autoload function will try to include the class from /path/to/lib/and/Foo.php. It is advisable to use a classname convention, like f.i. PEAR, to make finding files easier and to cut down on the amount of include_paths.
For additional security and speed, you can provide a more sophisticated Autoloader that uses an array to map from classname to filename, thus making sure only files that actually are part of your application can get included.
Further reading:
http://weierophinney.net/matthew/archives/245-Autoloading-Benchmarks.html
It's possible, but not recommended, like what if somebody could create a php file on that directory, you'll end up including it, besides, you can't predict the inclusion order.
Try this instead:
$includes=array(
'member',
'phone',
'profile',
'talent',
);
foreach($includes as $fname) {
require_once(MEMBERS.DS.$fname. '.php');
}
If you were using classes, consider using autoloading, as #Gordon suggested. And if you werent using classes, consider using them :)
At a first glance your code could work, although you have to ignore "." and ".." in the foreach loop. Plus I'd check, if the file ends with ".php":
$member_files = scandir(MEMBERS.DS);
foreach($member_files as $member_file) {
// Ignore non php files and thus ".." & "."
if (!preg_match('/\.php$/', $member_file) {
continue;
}
require_once(MEMBERS.DS.$member_file);
}
create 2 functions
function GetFiles($directory,$exempt = array('.','..','.ds_store','.svn'),&$files = array()) {
$handle = opendir($directory);
while(false !== ($resource = readdir($handle))){
if(!in_array(strtolower($resource),$exempt)){
if(is_dir($directory.$resource.'/'))
array_merge($files, self::GetFiles($directory.$resource.'/',$exempt,$files));
else
$files[] = $directory.$resource;
}
}
closedir($handle);
return $files;
}
function Autoload($includes = array()){
$files = array();
foreach($includes as $directory)
$files[] = self::GetFiles($directory);
foreach($files as $k=>$v){
foreach($v as $k1=>$v1)
require_once($v1);
}
}
to use it:
$includes = array(
'C:WWW/project/helpers/',
'C:WWW/project/lang/',
'C:WWW/project/lib/',
'C:WWW/project/mod/',
);
Autoload($includes);

Categories