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);
Related
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.
I am trying to create a .phar file from my web application. Following the php documentation's example I tried the following for this purpose.
<?php
$srcRoot = __DIR__ . "/../app";
$buildRoot = __DIR__ . "/../build";
$p = new Phar("$buildRoot/build.phar", 0, 'build.phar');
$p->buildFromIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($srcRoot)
),
$srcRoot
);
However I got the following error. I dont have any idea about the error. What is wrong with the code?
PHP Fatal error: Uncaught exception 'UnexpectedValueException' with message
'Iterator RecursiveIteratorIterator returned a path "D:\site\app" that is
not in the base directory "D:\site\app"'
in D:\site\tools\create-phar.php:7
The source of the problem is that RecursiveDirectoryIterator also lists the dot files - . and ...
When iterating over /path/to/foo it also lists /path/to/foo/. and /path/to/foo/.. which goes to the parent directory - outside the base directory.
Thus you have to prevent the inclusion of the ".." files, which is most easily achieved with FilesystemIterator::SKIP_DOTS as second parameter to DirectoryIterator:
new RecursiveDirectoryIterator($srcRoot, FilesystemIterator::SKIP_DOTS)
(#cweiske -- I just realized you beat me to it, I'll make sure to refresh the page next time, my sincerest apologies!)
You need just a slight edit to skip over the unix paths /. and /..:
<?php
$srcRoot = __DIR__ . "/../app";
$buildRoot = __DIR__ . "/../build";
$p = new Phar("$buildRoot/build.phar", 0, 'build.phar');
$p->buildFromIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($srcRoot, FilesystemIterator::SKIP_DOTS)
),
$srcRoot
);
Box makes it very easy to create PHAR archives from your source files. Basically you first add a configuration file and then you can create your PHAR file by simply invoking box build on the command line.
There is a great blog post by Matthieu Moquet describing how he used Box to simplify the distribution of a PHP Cli application. Personally, I've also been using Box for a CLI application, however the official project description is not limited to CLI applications, but rather summarizes the Box project as:
An application for building and managing Phars.
The Box project provides the PHAR build script and takes care of setting all paths correctly, so this might solve your problem.
The solution proposed by #cweiske is brilliant. In some situations, however, you may need the ability to add more directory path exclusions. Have a look at this example that excludes any references to the .git directory, using FilterIterator:
// create phar
$p = new Phar($pharFile, 0, $pharFile);
// produce iteration which excludes any references to values assigned to $excludes
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)
);
$filter = new class($iterator) extends FilterIterator {
public static $excludes = [];
public function accept()
{
$item = $this->current();
$actual = 0;
foreach (self::$excludes as $exclude) {
$actual += (int) boolval(strpos($item->getPath(), $exclude));
}
return ($actual === 0);
}
};
$filter::$excludes = ['.git'];
$p->buildFromIterator($filter, $path);
Have a look at php.ini file to check the value of phar.readonly field.
It must be 0 to create phar archive.
Ref: http://php.net/manual/en/phar.configuration.php#ini.phar.readonly
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).
I want to loop all the sub dirs in the main dirs, where I keeps all my classes, for instance,
core/
model/
page/
class_1.php
class_2.php
menu/
class_3.php
and so on...
So this is my autoload function that I place it in the init.php,
function autoload_multiple_directory($class_name){
// List all the class directories in the array.
$array_directories = array(
'core/controller/',
'core/model/',
'core/helper/'
);
// When you use namespace in a class, you get something like this when you auto load that class \foo\tidy.
// So use explode to split the string and then get the last item in the exloded array.
$parts = explode('\\', $class_name);
//print_r($parts);
// Set the class file name.
$file_name = strtolower(end($parts)).'.php';
// $file_name = 'class_'.strtolower($class_name).'.php';
// Loop the array.
foreach($array_directories as $path_directory){
$recursive_directory = new RecursiveDirectoryIterator($path_directory);
foreach (new RecursiveIteratorIterator($recursive_directory) as $filename => $file) {
if(file_exists(WEBSITE_DOCROOT.$file->getPath().'/'.$file_name)){
include WEBSITE_DOCROOT.$file->getPath().'/'.$file_name;
}
}
/* no problem with this, but I cannot loop the sub dirs...
if(file_exists(WEBSITE_DOCROOT.$path_directory.$file_name)){
include WEBSITE_DOCROOT.$path_directory.$file_name;
}
*
*/
}
}
spl_autoload_register('autoload_multiple_directory');
But then I get this error message,
Fatal error: Cannot redeclare class Common in C:\wamp\www\xxx\core\helper\Common.php on line 6
There is only one Common class in my project. Why does it say more than once or redeclare?
But if you look at the if(file_exists(WEBSITE_DOCROOT.$path_directory.$file_name)) that I comment out - it has no problem to load the classes. The problem of this initial loop is that it does not loop the sub dirs in a main dir, for instance, core/model/
Any ideas why and what should I do to loop the sub dirs of a main dir?
EDIT:
The problem comes from RecursiveDirectoryIterator - it loops the directories and lists all files. But what I want is only the sub directories.
Is there any chance that there are more than one copy of Common.php file exists in those folders?
Because your code does not break after including a class file the autoloader will continue seeking other files with the same name in the folder tree, and would lead to Fatal error: Cannot redeclare class XXX error. Adding break could fix the problem.
// Loop the array.
$isClassFound = false;
foreach($array_directories as $path_directory){
$recursive_directory = new RecursiveDirectoryIterator($path_directory);
foreach (new RecursiveIteratorIterator($recursive_directory) as $filename => $file) {
if(file_exists(WEBSITE_DOCROOT.$file->getPath().'/'.$file_name)){
include WEBSITE_DOCROOT.$file->getPath().'/'.$file_name;
$isClassFound = true;
}
if ($isClassFound) break;
}
if ($isClassFound) break;
}
However, the nature of your autoloader seems like it should not allow any duplicated class file names. Maybe you can write a name duplication checker to guarantee uniqueness.
EDIT:
I removed the class_exists() part from my answer because using it doesn't make much sense. Anyway, since you saw that version of my answer, and you asked me where to put class_exists() via the comment, I'll revive the code sample. you can add the following code at the beginning of the autoloader.
if (class_exists($class_name,false)) // give false to avoid automatic loading
return;
Open the start menu. In the text box write cmd, wait for the cmd program to pop up and then hit Enter.
Once the terminal window opens navigate to your root folder and then do a recursive search (through all files and folders) for the class' name:
cd C:\wamp\www\xxx
findstr /SNIP /C:"class common" *.php
There should be more than one declaration of the class.
I have this function that creates an array of a directories output.
public function getDirContents( $path )
{
$dir = new RecursiveDirectoryIterator( $path, FilesystemIterator::SKIP_DOTS );
// Flatten the recursive iterator, folders come before their files
$it = new RecursiveIteratorIterator( $dir, RecursiveIteratorIterator::SELF_FIRST );
// Maximum depth is 1 level deeper than the base folder
$it->setMaxDepth(1);
$results = array();
// Basic loop displaying different messages based on file or folder
foreach ($it as $contents)
{
$fileInfo = array();
if ( $contents->isFile() )
{
$fileInfo['dir'] = $it->getSubPath();
$fileInfo['file'] = $contents->getFilename();
$results[] = $fileInfo;
}
}
return $results;
}
I have this in a class that I created and it works as expected. I have an older script that I haven't rewritten to use this class. so I placed this function (minus the public) in another file that contains random common functions and include the file into my old script. However, when I call it I get the error
PHP Fatal error: Class 'FilesystemIterator' not found in /aaa/bbb/ccc/ddd/common.php on line 745
If I remove the FilesystemIterator::SKIP_DOTS it works fine except I guess it loses that functionality. I'm curious why this is happening.
Other things that may or may not explain what could be occuring.
The class that I use it in successfully is part of the main site.
The script where it errs is actually a subdomain.
I'm using php 5.3 (single file)
-- This is bluehost's config for picking a version of php and this is how mine is set up.
PHP 5.3 (Single php.ini) Same as PHP 5.3, but all subdirectories will use ~/public_html/php.ini
This is why I was under the impression that my subdirectory (subdomain) would use that file.
The httaccess in the subdomain vs the main are not the same.
I'm not sure if those tidbits help but I'm at lose and I'm fairly new at this.
Any help would be appreciated.