cakephp - scandir failed to open dir error - php

I'm tryin to get a list of controller files
$files = scandir(APP_DIR . '/Controller/');
...but I get this error:
Warning (2): scandir(src/Controller/): failed to open dir: No such file or directory [APP/Controller/HomeController.php, line 13]
... when i try to remove some file names from the list:
$files = array_diff( $files, array('.','..','Component','AppController.php','HomeController.php'));
... i get this error:
Warning (2): array_diff() [function.array-diff]: Argument #1 is not an array [APP/Controller/HomeController.php, line 23]
my entire class looks this:
<?php
namespace App\Controller;
use ReflectionClass;
use ReflectionMethod;
class HomeController extends AppController{
public function index(){
/**
* http://stackoverflow.com/questions/20963786/how-do-i-get-a-list-of-all-functions-inside-a-controller-in-cakephp
* http://stackoverflow.com/questions/25892594/list-all-controllers-actions-in-cakephp-3
*/
$files = scandir(APP_DIR . '/Controller/');
$controllers = [];
$ignoreFilesList = [
'.',
'..',
'Component',
'AppController.php',
'HomeController.php'
];
$files = array_diff( $files, array('.','..','Component','AppController.php','HomeController.php'));
$className = '';
$class = null;
$ignoreList2 = ['beforeFilter', 'afterFilter', 'initialize', 'delete'];
echo is_array ( (array)$files );// returns 0 without cast
foreach( (array)$files as $file){
$controller = str_replace('Controller', '', explode('.', $file)[0]);
array_push($controllers, $controller); //str_replace('Controller', '', $controller));
//dump( $controller );
/* controller methods */
$this->$className = 'App\\Controller\\'.$controller.'Controller';
$this->$class = new ReflectionClass($this->$className);
$methods = $this->$class->getMethods(ReflectionMethod::IS_PUBLIC);
echo "<ul><li>$controller</li>";
echo "<ol>";
foreach($methods as $method){
if ( isset( $method->name )){
if($method->class == $className && !in_array($method->name, $ignoreList2))
{
$controllers[$controller][]= $method->name;
echo "<li>$method->name</li>";
}
}
}
echo "</ol>";
echo "</ul>";
}
$this->set('allControllers', $controllers );
}
}

Only use PHP: scandir with a full path.
While the PHP documentation says nothing about it, scandir generally does not play nice with relative paths. (Though there are cases where relative paths can work perfectly fine.)
Path arg is a relative path here
As APP_DIR stores a single directory name, and not a path, scandir will attempt to access src/Controller/, which may not relative to the script currently running.
$files = scandir(APP_DIR . '/Controller/');
Solution 1: Use CakePhp: constant APP to build the full path
Thanks to ndm for reminding us of the APP constant in his comment on the OP. APP stores an absolute path to your application directory, including a trailing slash.
$path = APP .'Controller/';
$files = scandir($path);
Solution 2: Use PHP: $_SERVER['DOCUMENT_ROOT'] to build a full path.
$path = $_SERVER['DOCUMENT_ROOT'] . '/'. APP_DIR .'/Controller/';
$files = scandir($path);
Alternatively, you may be able to use PHP: Realpath to retrieve the full path. *reminder: working directory will vary depending on the script running.

Related

PHP autoloader - $classname includes the entire folder path and not just the class name itself?

I am following WordPress's naming convention where a class My_Class should reside in the file named class-my-class.php. I used this autoloader for WordPress, written by Rarst. If I print out the $class_name variable, I see that the prefix class is appended to the folder name and not the class file. I had the same issue with some other autoloader I used earlier. I can do a little bit of string manipulation and get what I want but I want to know what is the exact issue.
What could be wrong?
I just had a look at this autoloader you linked to, I would say it should be on line 21 something like this :
$class_path = $this->dir . '/class-' . strtolower( str_replace( '_', '-', basename( $class_name ) ) ) . '.php';
basename takes only the file part + file extension of the path.
You need also to check where this autoloader file is, because $this->dir is set to DIR, which is the directory where the autoloader file is.
Use a flexible loader.
try this one.
function TR_Autoloader($className)
{
$assetList = array(
get_stylesheet_directory() . '/vendor/log4php/Logger.php',
// added to fix woocommerce wp_email class not found issue
WP_PLUGIN_DIR . '/woocommerce/includes/libraries/class-emogrifier.php'
// add more paths if needed.
);
// normalized classes first.
$path = get_stylesheet_directory() . '/classes/class-';
$fullPath = $path . $className . '.php';
if (file_exists($fullPath)) {
include_once $fullPath;
}
if (class_exists($className)) {
return;
} else { // read the rest of the asset locations.
foreach ($assetList as $currentAsset) {
if (is_dir($currentAsset)) {
foreach (new DirectoryIterator($currentAsset) as $currentFile) {
if (!($currentFile->isDot() || ($currentFile->getExtension() <> "php")))
require_once $currentAsset . $currentFile->getFilename();
}
} elseif (is_file($currentAsset)) {
require_once $currentAsset;
}
}
}
}
spl_autoload_register('TR_Autoloader');

Cannot redeclare class: how to autoload a class if exists already in a folder?

How can I check if a class exists already in a folder then do not load this class again from another folder?
I have this folder structure for instance,
index.php
code/
local/
And I have these two identical classes in code/ and local/
from local/
class Article
{
public function getArticle()
{
echo 'class from local';
}
}
from core,
class Article
{
public function getArticle()
{
echo 'class from core';
}
}
So I need a script that can detects the class of Article in local/ - if it exits already in that folder than don't load the class again from core/ folder. Is it possible?
This is my autoload function in index.php for loading classes,
define ('WEBSITE_DOCROOT', str_replace('\\', '/', dirname(__FILE__)).'/');
function autoloadMultipleDirectory($class_name)
{
// List all the class directories in the array.
$main_directories = array(
'core/',
'local/'
);
// Set other vars and arrays.
$sub_directories = array();
// 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);
// Set the class file name.
$file_name = end($parts).'.php';
// List any sub dirs in the main dirs above and store them in an array.
foreach($main_directories as $path_directory)
{
$iterator = new RecursiveIteratorIterator
(
new RecursiveDirectoryIterator(WEBSITE_DOCROOT.$path_directory), // Must use absolute path to get the files when ajax is used.
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileObject)
{
if ($fileObject->isDir())
{
// Replace any backslash to '/'.
$pathnameReplace = str_replace('\\', '/', $fileObject->getPathname());
//print_r($pathnameReplace);
// Explode the folder path.
$array = explode("/",$pathnameReplace);
// Get the actual folder.
$folder = end($array);
//print_r($folder);
// Stop proccessing if the folder is a dot or double dots.
if($folder === '.' || $folder === '..') {continue;}
//var_dump($fileObject->getPathname());
// Must trim off the WEBSITE_DOCROOT.
$sub_directories[] = preg_replace('~.*?(?=core|local)~i', '', str_replace('\\', '/', $fileObject->getPathname())) .'/';
}
}
}
// Mearge the main dirs with any sub dirs in them.
$merged_directories = array_merge($main_directories,$sub_directories);
// Loop the merge array and include the classes in them.
foreach($merged_directories as $path_directory)
{
if(file_exists(WEBSITE_DOCROOT.$path_directory.$file_name))
{
// There is no need to use include/require_once. Autoload is a fallback when the system can't find the class you are instantiating.
// If you've already included it once via an autoload then the system knows about it and won't run your autoload method again anyway.
// So, just use the regular include/require - they are faster.
include WEBSITE_DOCROOT.$path_directory.$file_name;
}
}
}
// Register all the classes.
spl_autoload_register('autoloadMultipleDirectory');
$article = new Article();
echo $article->getArticle();
of course I get this error,
Fatal error: Cannot redeclare class Article in C:\wamp\...\local\Article.php on line 3
class_exists seems to be the answer I should look into, but how can I use it with the function above, especially with spl_autoload_register. Or if you have any better ideas?
Okay, I misunderstood your question. This should do the trick.
<?php
function __autoload($class_name) {
static $core = WEBSITE_DOCROOT . DIRECTORY_SEPARATOR . "core";
static $local = WEBSITE_DOCROOT . DIRECTORY_SEPARATOR . "local";
$file_name = strtr($class_name, "\\", DIRECTORY_SEPARATOR):
$file_local = "{$local}{$file_name}.php";
require is_file($file_local) ? $file_local : "{$core}{$file_name}.php";
}
This is easily solved by using namespaces.
Your core file goes to /Core/Article.php:
namespace Core;
class Article {}
Your local file goes to /Local/Article.php:
namespace Local;
class Article {}
And then use a very simple autoloader, e.g.:
function __autoload($class_name) {
$file_name = strtr($class_name, "\\", DIRECTORY_SEPARATOR);
require "/var/www{$file_name}.php";
}
PHP loads your classes on demand, there's no need to load the files up front!
If you want to use an article simply do:
<?php
$coreArticle = new \Core\Article();
$localArticle = new \Local\Article();

PHPUnit Dry Run or Discover Test without executing

I would like to write a script that "discovers" PHPUnit test in a given folder.
Currently I can execute:
phpunit .
And all my test names are shown however they are executed which can take quite a bit of time.
What I would like is the ability to view which test I have in a project without actually executing the test. In a format similar to
ExampleTest::testNameHere
Is this possible?
Thank you for your time.
You're looking for:
--list-tests
/* phpunit/php-file-iterator */
$iteratorFactory = new File_Iterator_Factory();
$paths = array(
/* testsuite path */
'/var/www/project/tests/unit/phpUnit/',
);
$suffixes = array(
'Test.php',
);
$iterator = $iteratorFactory->getFileIterator($paths, $suffixes);
foreach ($iterator as $file) {
$fileName = $file->getFileName();
$path = $file->getPathName();
// you should use autoloader
require_once $path;
// build className, according to your file structure
$class = preg_replace('/\.php/', '', $fileName);
$refrlection = new ReflectionClass($class);
$methods = $refrlection->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
// test* is test method
if (preg_match('/^test[A-Z]/', $method->getName())) {
echo $refrlection->getName() . ':' . $method->getName();
echo PHP_EOL;
}
}
}
Code that detects if a file contains:
A class that inherits the PHPUnit base class for test classes (sorry, don't have the name in front of me)
Methods in that class that start with 'test'
should do the trick.

PHP RecursiveIteratorIterator: The system cannot find the path specified?

I have this error when I want to autoload classes with RecursiveIteratorIterator and spl_autoload_register,
uncaught exception 'UnexpectedValueException' with message
'RecursiveDirectoryIterator::__construct(): The system cannot find the
path specified. (code: 3)
What does it mean?
Below is my class autoloader,
function autoload_multiple_directory($class_name){
// List all the class directories in the array.
$array_directories = array(
'core/controller/',
'core/model/',
'core/helper/',
'core/ext/'
);
$parts = explode('\\', $class_name);
// Set the class file name.
$file_name = end($parts).'.php';
foreach($array_directories as $path_directory){
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path_directory),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileObject) {
if ($fileObject->isDir()) {
$files[] = str_replace('\\', '/', $fileObject->getPathname()).'/';
}
}
}
$array_directories = array_merge($array_directories,$files);
// Loop the array.
foreach($array_directories as $path_directory){
if(file_exists(WEBSITE_DOCROOT.$path_directory.$file_name)){
include WEBSITE_DOCROOT.$path_directory.$file_name;
}
}
}
spl_autoload_register('autoload_multiple_directory');
The error line is pointing to new RecursiveDirectoryIterator($path_directory), why?
I must use absolute path in new RecursiveDirectoryIterator(WEBSITE_DOCROOT.$path_directory) as the init.php is called via ajax sometimes.

How to include() all PHP files from a directory?

In PHP can I include a directory of scripts?
i.e. Instead of:
include('classes/Class1.php');
include('classes/Class2.php');
is there something like:
include('classes/*');
Couldn't seem to find a good way of including a collection of about 10 sub-classes for a particular class.
foreach (glob("classes/*.php") as $filename)
{
include $filename;
}
Here is the way I include lots of classes from several folders in PHP 5. This will only work if you have classes though.
/*Directories that contain classes*/
$classesDir = array (
ROOT_DIR.'classes/',
ROOT_DIR.'firephp/',
ROOT_DIR.'includes/'
);
function __autoload($class_name) {
global $classesDir;
foreach ($classesDir as $directory) {
if (file_exists($directory . $class_name . '.php')) {
require_once ($directory . $class_name . '.php');
return;
}
}
}
I realize this is an older post BUT... DON'T INCLUDE YOUR CLASSES... instead use __autoload
function __autoload($class_name) {
require_once('classes/'.$class_name.'.class.php');
}
$user = new User();
Then whenever you call a new class that hasn't been included yet php will auto fire __autoload and include it for you
this is just a modification of Karsten's code
function include_all_php($folder){
foreach (glob("{$folder}/*.php") as $filename)
{
include $filename;
}
}
include_all_php("my_classes");
How to do this in 2017:
spl_autoload_register( function ($class_name) {
$CLASSES_DIR = __DIR__ . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR; // or whatever your directory is
$file = $CLASSES_DIR . $class_name . '.php';
if( file_exists( $file ) ) include $file; // only include if file exists, otherwise we might enter some conflicts with other pieces of code which are also using the spl_autoload_register function
} );
Recommended by PHP documentation here: Autoloading classes
You can use set_include_path:
set_include_path('classes/');
http://php.net/manual/en/function.set-include-path.php
If there are NO dependencies between files... here is a recursive function to include_once ALL php files in ALL subdirs:
$paths = [];
function include_recursive( $path, $debug=false){
foreach( glob( "$path/*") as $filename){
if( strpos( $filename, '.php') !== FALSE){
# php files:
include_once $filename;
if( $debug) echo "<!-- included: $filename -->\n";
} elseif( is_dir($filename)) { # dirs
$paths[] = $filename;
}
}
# Time to process the dirs:
for( $i=count($paths)-1; $i>=0; $i--){
$path = $paths[$i];
unset( $paths[$i]);
include_recursive( $path, $debug);
}
}
include_recursive( "tree_to_include");
# or... to view debug in page source:
include_recursive( "tree_to_include", 'debug');
<?php
//Loading all php files into of functions/ folder
$folder = "./functions/";
$files = glob($folder."*.php"); // return array files
foreach($files as $phpFile){   
require_once("$phpFile");
}
If you want include all in a directory AND its subdirectories:
$dir = "classes/";
$dh = opendir($dir);
$dir_list = array($dir);
while (false !== ($filename = readdir($dh))) {
if($filename!="."&&$filename!=".."&&is_dir($dir.$filename))
array_push($dir_list, $dir.$filename."/");
}
foreach ($dir_list as $dir) {
foreach (glob($dir."*.php") as $filename)
require_once $filename;
}
Don't forget that it will use alphabetic order to include your files.
If your looking to include a bunch of classes without having to define each class at once you can use:
$directories = array(
'system/',
'system/db/',
'system/common/'
);
foreach ($directories as $directory) {
foreach(glob($directory . "*.php") as $class) {
include_once $class;
}
}
This way you can just define the class on the php file containing the class and not a whole list of $thisclass = new thisclass();
As for how well it handles all the files? I'm not sure there might be a slight speed decrease with this.
I suggest you use a readdir() function and then loop and include the files (see the 1st example on that page).
Try using a library for that purpose.
That is a simple implementation for the same idea I have build.
It include the specified directory and subdirectories files.
IncludeAll
Install it via terminal [cmd]
composer install php_modules/include-all
Or set it as a dependency in the package.json file
{
"require": {
"php_modules/include-all": "^1.0.5"
}
}
Using
$includeAll = requires ('include-all');
$includeAll->includeAll ('./path/to/directory');
This is a late answer which refers to PHP > 7.2 up to PHP 8.
The OP does not ask about classes in the title, but from his wording we can read that he wants to include classes. (btw. this method also works with namespaces).
By using require_once you kill three mosquitoes with one towel.
first, you get a meaningful punch in the form of an error message in your logfile if the file doesn't exist. which is very useful when debugging.( include would just generate a warning that might not be that detailed)
you include only files that contain classes
you avoid loading a class twice
spl_autoload_register( function ($class_name) {
require_once '/var/www/homepage/classes/' . $class_name . '.class.php';
} );
this will work with classes
new class_name;
or namespaces. e.g. ...
use homepage\classes\class_name;
Answer ported over from another question. Includes additional info on the limits of using a helper function, along with a helper function for loading all variables in included files.
There is no native "include all from folder" in PHP. However, it's not very complicated to accomplish. You can glob the path for .php files and include the files in a loop:
foreach (glob("test/*.php") as $file) {
include_once $file;
}
In this answer, I'm using include_once for including the files. Please feel free to change that to include, require or require_once as necessary.
You can turn this into a simple helper function:
function import_folder(string $dirname) {
foreach (glob("{$dirname}/*.php") as $file) {
include_once $file;
}
}
If your files define classes, functions, constants etc. that are scope-independent, this will work as expected. However, if your file has variables, you have to "collect" them with get_defined_vars() and return them from the function. Otherwise, they'd be "lost" into the function scope, instead of being imported into the original scope.
If you need to import variables from files included within a function, you can:
function load_vars(string $path): array {
include_once $path;
unset($path);
return get_defined_vars();
}
This function, which you can combine with the import_folder, will return an array with all variables defined in the included file. If you want to load variables from multiple files, you can:
function import_folder_vars(string $dirname): array {
$vars = [];
foreach (glob("{$dirname}/*.php") as $file) {
// If you want to combine them into one array:
$vars = array_merge($vars, load_vars($file));
// If you want to group them by file:
// $vars[$file] = load_vars($file);
}
return $vars;
}
The above would, depending on your preference (comment/uncomment as necessary), return all variables defined in included files as a single array, or grouped by the files they were defined in.
On a final note: If all you need to do is load classes, it's a good idea to instead have them autoloaded on demand using spl_autoload_register. Using an autoloader assumes that you have structured your filesystem and named your classes and namespaces consistently.
Do no write a function() to include files in a directory. You may lose the variable scopes, and may have to use "global". Just loop on the files.
Also, you may run into difficulties when an included file has a class name that will extend to the other class defined in the other file - which is not yet included. So, be careful.

Categories