Exclude files and folders when copying - php

This question has already been asked here, but it doesn't get me anywhere. I would like to be able to exclude certain files with relative paths from being copied, but the whole thing no longer works with subdirectories, so far I have only shown what subdirectories cannot be excluded without copying:
class MyRecursiveFilterIterator extends RecursiveFilterIterator {
//exluded Files and dirs
public static $FILTERS = array(
'config' => '',
);
public function accept() {
return !in_array(
$this->current()->getFilename(),
self::$FILTERS,
true
);
}
}
$dirItr = new RecursiveDirectoryIterator('../dir1');
$filterItr = new MyRecursiveFilterIterator($dirItr);
$itr = new RecursiveIteratorIterator($filterItr, RecursiveIteratorIterator::SELF_FIRST);
foreach ($itr as $filePath => $fileInfo) {
echo $fileInfo->getFilename() . "<br>";
}

Related

How do I get a list of all models in Laravel?

I would like to find a list of all models, database tables as a backup, in a Laravel project.
I want to do this to build a dashboard that displays the way data in all of the models has changed over time, I.E. if there is a user model, how many users have been added or modified each day for the last 90 days.
I would like to suggest a different approach by using PHP reflection instead of relying that all models will reside in a namespace or directory called Model.
The code sample below collects all the application classes, verifies if they actually extend the Eloquent Model class and are not abstract.
<?php
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
function getModels(): Collection
{
$models = collect(File::allFiles(app_path()))
->map(function ($item) {
$path = $item->getRelativePathName();
$class = sprintf('\%s%s',
Container::getInstance()->getNamespace(),
strtr(substr($path, 0, strrpos($path, '.')), '/', '\\'));
return $class;
})
->filter(function ($class) {
$valid = false;
if (class_exists($class)) {
$reflection = new \ReflectionClass($class);
$valid = $reflection->isSubclassOf(Model::class) &&
!$reflection->isAbstract();
}
return $valid;
});
return $models->values();
}
I would navigate through your filesystem and pull out all of the php files from your models folder. I keep my models in the app/Models folder so something like this:
$path = app_path() . "/Models";
function getModels($path){
$out = [];
$results = scandir($path);
foreach ($results as $result) {
if ($result === '.' or $result === '..') continue;
$filename = $path . '/' . $result;
if (is_dir($filename)) {
$out = array_merge($out, getModels($filename));
}else{
$out[] = substr($filename,0,-4);
}
}
return $out;
}
dd(getModels($path));
I just tried this and it spit out the full filepath of all of my models. You could strip the strings to make it only show the namespace and model name if thats what you are looking for.
Please be aware that this might miss models that have not been in scope during bootstrapping. Please see the edit below.
There is a way to load the declared models without iterating the file system. Since most of the models are declared after bootstrapping, you may call get_declared_classes and filter the return for your models' namespaces like this (my classes are in \App\Models namespace):
$models = collect(get_declared_classes())->filter(function ($item) {
return (substr($item, 0, 11) === 'App\Models\\');
});
As #ChronoFish mentioned, this method does not pull up all models in all cases.
For example, this does not work at all in early stages of the bootstrap lifecycle like in service providers. But even when it is working, it might miss a few models, depending on your project's structure, as not all classes are always in scope. For my test project, all models were loaded, but in a more complex application, a significant number of classes may be missed by this.
Thanks for your comment!
I am currently using something like this:
$appNamespace = Illuminate\Container\Container::getInstance()->getNamespace();
$modelNamespace = 'Models';
$models = collect(File::allFiles(app_path($modelNamespace)))->map(function ($item) use ($appNamespace, $modelNamespace) {
$rel = $item->getRelativePathName();
$class = sprintf('\%s%s%s', $appNamespace, $modelNamespace ? $modelNamespace . '\\' : '',
implode('\\', explode('/', substr($rel, 0, strrpos($rel, '.')))));
return class_exists($class) ? $class : null;
})->filter();
The $modelNamespace is for those who have a distinct folder and namespace for their models, which is highly recommended, or who are using Laravel 8+ where this is the new default. Those who just go with the defaults of Laravel 7 or lower can leave this empty, but will then pull in all classes in the app directory, not just Eloquent models.
In that case you can make sure you only get Models by filtering the list like this:
$models->filter(
function ($model) {
return !is_subclass_of($model, Illuminate\Database\Eloquent\Model::class);
}
);
According of topic answer's:
<?php
use Illuminate\Support\Facades\File;
/**
* Get Available models in app.
*
* #return array $models
*/
public static function getAvailableModels()
{
$models = [];
$modelsPath = app_path('Models');
$modelFiles = File::allFiles($modelsPath);
foreach ($modelFiles as $modelFile) {
$models[] = '\App\\' . $modelFile->getFilenameWithoutExtension();
}
return $models;
}
I was searching for something readable, so, I made this code that get all models names and you can format how you want.
<?php
function getModelNames(): array
{
$path = app_path('Models') . '/*.php';
return collect(glob($path))->map(fn ($file) => basename($file, '.php'))->toArray();
}
One way could be to use the Standard PHP Library (SPL) to recursively list all files inside a directory you can make a function like below.
function getModels($path, $namespace){
$out = [];
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator(
$path
), \RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
/**
* #var \SplFileInfo $item
*/
if($item->isReadable() && $item->isFile() && mb_strtolower($item->getExtension()) === 'php'){
$out[] = $namespace .
str_replace("/", "\\", mb_substr($item->getRealPath(), mb_strlen($path), -4));
}
}
return $out;
}
getModels(app_path("Models/"), "App\\Models\\");
You can also try this to solve the problem:
https://gist.github.com/mohammad425/231242958edb640601108bdea7bcf9ac
This is my solution. Not intended to search for models in subfolders.
<?php
use Illuminate\Database\Eloquent\Model;
function getModels(?string $path = null): array
{
$path = $path ?? app_path();
// Se obtienen los archivos en el directorio, sin subdirectorios.
$files = File::files($path);
// Se obtienen los nombres sin rutas ni extensiones.
$filenames = array_map(function ($file): string {
$pathinfo = pathinfo($file);
return $pathinfo['filename'];
}, $files);
$namespace = "\App";
// Se conservan aquellos que sean modelos.
$mapped = array_map(
function ($filename) use ($namespace): ?string {
$class = "{$namespace}\\{$filename}";
if (!class_exists($class)) {
return null;
}
$object = new $class;
if (!$object instanceof Model) {
return null;
}
return $class;
},
$filenames
);
// Se filtran los que no son modelos.
$models = array_filter($mapped);
return array_values($models);
}

OOP problems with instance variables

I've got some problems with a piece of code in my two classes 'File' and 'Folder'. I've created a page which shows me the content of my server space. Therefore I've wrote the class Folder which contains information about it self like 'name', 'path' and 'children'. The children property contains an Array of 'Files' or 'Folders' within this folder. So it's a kind of recursive class. To get the whole structure of a wanted directory I've wrote some recursive backtracking algorithms that are giving me an array of objects for all children in the same structure as my folder on the server. The second algorithm is taking that array and searches an special folder. If it finds this folder the method will return the root path to it and if the folder isn't a subfolder of this directory the algorithm will return false. I've tested all of that methods for the 'Folder' object and it works just fine but now I've detected an error by using my script more intensive.
/**
* find an subfolder within the given directory (Recursive)
*/
public function findFolder($name) {
// is this object the object you wanted
if ($this->name == $name) {
return $this->getPath();
}
// getting array
$this->bindChildren();
$result = $this->getChildren();
// backtracking part
foreach($result as $r) {
// skip all 'Files'
if(get_class($r) == 'File') {
continue;
} else {
if($search_res = $r->findFolder($name)) {
return $search_res;
}
}
}
// loop runned out
return false;
}
/**
* stores all children of this folder
*/
public function bindChildren() {
$this->resetContent();
$this->dirSearch();
}
/**
* resets children array
*/
private function resetContent() {
$this->children = array();
}
/**
* storing children of this folder
*/
private function dirSearch() {
$dh = opendir($this->path);
while($file = readdir($dh)) {
if($file !== "" && $file !== "." && $file !== "..") {
if(!is_dir($this->path.$file)) {
$this->children[] = new File($this->path.$file);
} else {
$this->children[] = new Folder($this->path.$file.'/');
}
}
}
}
In my website I first create a new folder object and then I'm starting to find a subfolder of 'doc' which is call 'test' for example. The folder 'test' is in '/var/www/media/username/doc/test4/test/' located
$folder = new Folder('/var/www/media/username/doc/');
$dir = $folder->findFolder('test');
If I print out $dir it returns a link as I wanted because the folder 'test' is a subfolder of 'docs' but the returned link is not correct. it should be '/var/www/media/username/doc/test4/test' but the result is '/var/www/media/username/doc/test' I've tried to debugg a bit and found out that the folders list which contains all children is keeping the objects with the right links but in the findFolder method in the first if condition the object $this doesn't have the correct path. I don't know why but the the
// backtracking part
foreach($result as $r) {
seems to change the object properties. I hope someone can help me and thanks in advance
Don't reinvent the wheel. PHP already has a class for that purpose named RecursiveDirectoryIterator.
http://php.net/manual/en/class.recursivedirectoryiterator.php

Filter files in DirectoryIterator output

I'm working on a script that gets file modified times, and takes an optional arg that if filled has an array with a list of files to check rather then every file. How could I go about just getting the data for those files in a script like this:
$filesObject = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
foreach ($filesObject as $key => $object)
{
$checkFile = filemtime($object->getPathname());
$num++;
$alertFiles[$num] = array('name' => $object->getPathname(),
'time' => $checkFile);
}
edit: this code is in a function where $filesArray is the array of file names that can be passed.
You can wrap your Iterator in a custom Filter Iterator (to foster reuse)
class FileFilter extends FilterIterator
{
protected $_files;
public function __construct($iterator, array $files)
{
$this->_files = $files;
parent::__construct($iterator);
}
public function accept()
{
return !in_array($this->current(), $this->_files);
}
}
If accept returns FALSE the current element is not considered in the iteration. Since you give very little information about your $filesArray contents, you might have to change the accept logic to make it work for your code.

find recursive specific file

I'm trying to find all the files that called "testunit.php".
In addition i want to cut the first 23 chars of the string.
I tried this but this is not working.I get all the files.
$it = new RecursiveDirectoryIterator($parent);
$display = Array ( 'testunit.php');
foreach (new RecursiveIteratorIterator($it) as $file=>$cur) {
{
if ( In_Array ( $cur, $display ) == true )
$file = substr($cur, 23)
fwrite($fh,"<file>$file</file>");
}
Thank you!
see if glob helps you
Try
class TestUnitIterator extends FilterIterator
{
public function accept()
{
return (FALSE !== strpos(
$this->getInnerIterator()->current(),
'testunit.php'
));
}
public function current()
{
return sprintf(
'<file>%s</file>',
substr($this->getInnerIterator()->current(), 23)
);
}
}
Usage (codepad (abridged example)):
$iterator = new TestUnitIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
'/path/to/iterate/over',
FilesystemIterator::CURRENT_AS_PATHNAME
)
)
);
foreach ($iterator as $file) {
echo $file, PHP_EOL;
}
Disclaimer: I wasn't in the mood to mock the filesystem or setup the required test files, so the above might need a little tweaking to work with the filesystem. I only tested with an ArrayIterator but there shouldn't be much to do if the above produces errors.

listing all jpg files in dir and subdirs

How can I list all jpg files in a given directory and its subdirectories in PHP5 ?
I thought I could write a glob pattern for that but I can't figure it out.
thanx, b.
You can use a RecursiveDirectoryIterator with a filter:
class ImagesFilter extends FilterIterator
{
public function accept()
{
$file = $this->getInnerIterator()->current();
return preg_match('/\.jpe?g$/i', $file->getFilename());
}
}
$it = new RecursiveDirectoryIterator('/var/images');
$it = new ImagesFilter($it);
foreach ($it as $file)
{
// Use $file here...
}
$file is an SplFileInfo object.
without doing it for you. recursion is the answer here. a function that looks in a dir and gets a list of all files. filters out only th jpg's then calls its self if i finds any sub dirs
Wish I had time to do more & test, but this could be used as a starting point: it should (untested) return an array containing all the jpg/jpeg files in the specified directory.
function load_jpgs($dir){
$return = array();
if(is_dir($dir)){
if($handle = opendir($dir)){
while(readdir($handle)){
echo $file.'<hr>';
if(preg_match('/\.jpg$/',$file) || preg_match('/\.jpeg$/',$file)){
$return[] = $file;
}
}
closedir($handle);
return $return;
}
}
return false;
}

Categories