I am currently refactoring a project which has been halfheartedly ported to Yii. There are some classes in the components folder which are included in a controller with the PHP "use"-keyword. This gives me a "include(protected/components/classes/SClass.php): failed to open stream: No such file or directory" error though.
What's really strange about it is, that changing the name (used by "use") to a non-existent file gives me a fatal error. Any ideas?
The use keyword by itself in PHP does not do any including of other files. It merely tells PHP that the namespace defined in the use statement may be referenced by code further down in the current PHP file.
However, what is likely happening here is that your system has an autoload function defined. If there is an autoload function, PHP will call this function whenever it encounters a class name that it doesn't recognise. The autoload function searches for the class file to load and includes it if it can find it. This is probably where your errors are occurring.
In the first case, this is the sequence of events:
the use statement is referencing a valid namespace, but this is ignored until a class in that namespace is referenced in the code.
When the class is referenced, PHP says "I don't know this class yet, lets autoload it".
The autoloader function is run, and builds a path to include. This usually comes from the namespace and classname of the class, but can be whatever the autoload function is written to expect.
In this case, it sounds like the autoloader is building the path and running include() on that path, but the class doesn't exist where the autoloader expects. Hence the "file not found" error.
In the second case, where you change the use statement:
The use statement now references a different namespace, but you probably haven't changed the code later in the program that actually references the class.
The program reaches the code that calls the class, but it doesn't recognise the namespace because it doesn't match the use statement any more, so instant fatal error.
You might want to check the file ownership and permissions, and check for PHP security as is mentioned HERE
Related
In my current lib, I have classes with static code outside the class definition, that I would like to execute when browsing a index.php file that has autoloading (with properly putting the class files into a PSR-4 folder structure, and calling composer install before).
It's not such a bad deal. For instance, in my custom Error.php class I could for instance call set_error_handler function outside the class so warnings could be catchable. And putting this file in a PSR-4 autoloading could ease the pain for not having to call any Error.php code in index.php to enable this catching. Every source that just uses my namespace and autoloads my lib would have that for granted.
I tried to include use \MyNamespace\Error; in the index.php file, but the code in Error.php, outside the Error class definition, isn't automatically executed.
The code outside the class is only executed when I call a class method inside my index.php file (the one that has the autoloading).
Can this be done ? Thanks for your time.
use \MyNamespace\Error; does not trigger autoloading, it just allows you to use shorter class name in code - new Error() instead of new \MyNamespace\Error(). If you want to include Error.php file, you need to use this class. Probably the safest way would to use class_exist():
class_exists(Error::class);
But honestly you should rethink your design, implicit registering error handler in file with class declaration is against PSR-1 and may be really annoying in big project.
Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both
https://www.php-fig.org/psr/psr-1/#23-side-effects
It would be less magical if you create separate method for registering error handler and call it explicitly in index.php:
Error::registerErrorHandler();
I try to lookup the declaration of File but PhpStorm says Cannot find declaration to go to.
I also tried it with netbeans, it can't find the declartion too.
I also tried to lookup the alias use File;
I get No usage found in project files.
How does my code even know what it has to do if It can't find any declarations? This makes no sense to me.
How can I find out where File is declared?
How does my code even know what it has to do if It can't find any declarations?
By using an autoloader. This is basically a function which is called whenever an unknown class is referenced, and attempts to define it, usually by including a file according to some naming convention. You will need to find how your particular framework manages this.
Note that it's possible it's including a file from outside the directory you have set up as the "project" in your IDE. Once you've figured out where it is, you may be able to configure your IDE to know about these extra files.
How can I find out where File is declared?
Find a place where the class is used, and using a debugger or just "dump value and die", you can use ReflectionClass::getFilename() to find out about it:
$r = new \ReflectionClass(File::class);
$r->getFilename();
Note that the File::class syntax (available since PHP 5.5) gives you the fully qualified name of the class, ignoring any aliasing or namespace imports.
It's also possible for an extension (a library written in C, not PHP) to define a class. In that case, ReflectionClass::getFilename() will return false, and you'll need to use ReflectionClass::getExtensionName(), then track down the documentation for that extension.
Laravel is quite "opinionated" in the way they use facades.
Apart from the PHPStorm gudelines how to deal with it, I find artisan tinker a simplest IDE-independent way to get familiar with new codebase.
The command provides a REPL shell, so if you are curious of where the File is actually defined, just invoke it, to get some information from the error message:
>>> File::delete()
PHP warning: Missing argument 1 for Illuminate\Filesystem\Filesystem::delete(), called in /path/to/project/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php on line 213 and defined in /path/to/project/app/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php on line 118
PHPStrom scans all files in Project Root folder. Add an external library (framework) you use to Project Root folder. Maybe you should instal dependecies via composer.
I am trying to implement autoloading of my classes. I separate my classes into two different folders:
/classes
/external
In my external folder, I keep everything third-party that I am using within my application. It may include classes, functions, etc. I like to keep that separate from my own code.
I want to include everything in the external directory manually. I only want files in the classes directory to be autoloaded. Here is what I have:
Files:
/classes/cache.class.php
/classes/db.class.php
/classes.mail.class.php
/external/hooks.php
And to autoload:
spl_autoload_register(function ($class_name) {
include dirname(__FILE__).'/classes/'.$class_name.'.class.php';
});
$Cache = new cache();
$Db = new db();
$Mail = new mail();
Somehow, this is attempting to load any class that exists in the external directory. I know this because in hooks.php I have:
class Hooks { ... } // Notice the capital H
And I am getting the error:
"Warning: include(/path/to/classes/Hooks.class.php): failed to open stream: No such file or directory"
How can I get spl_autoload_register to ignore all classes that exist outside of the classes directory?
The autoloader has no way of knowing in which folder a class is defined. The general flow is:
See an undefined class being used in the code being executed
Call all the functions registered with the spl_autoloader, and pass them the undefined class name
Since you didn't have any check for if the file exists, the autoloader function you registered is erroring out whenever you fail to include a non-autoloaded class before it's used. As #Forbs mentioned in his answer, just add a check in your autoloader function to see if the file exists before including the source file.
It's worth noting that Composer does this job for you very well already - unless you have some specific reason for wanting to use your own autoloader, I'd suggest just setting up composer for your project.
https://getcomposer.org/doc/00-intro.md
add this
if (file_exists(dirname(__FILE__).'/classes/.$class_name.'.class.php'))
right before your include
I am having difficulty getting psysh to instantiate classes.
I am trying to use PSR-4 namespaces, and have registered a psr-4 autoload in composer like this:
"autoload": {
"psr-4": {
"System\\": "phpclasses/"
}
},
There is a class in phpclasses\Test.php, class name Test with a static method called hello().
I open a command shell, start psysh, and psysh appears to be working normally.
If I try to run Test::hello(); it will fail, unless I call it like this first: echo System\Test::hello();
This actually fails with the message:
PHP Fatal error: Class 'System\Test' not found in eval()'d code on line 1
but then I can successfully run: echo Test::hello();
echo System\Test::hello(); will never work
I tried Use System; and use System\Test; has no beneficial effect.
Every class I use, I have to go through this routine, which is kind of a drag because some of the classes uses static methods, and each of those will only work if each class has gone through that fail first routine.
Basically the same technique must be used for static or non-static methods.
I am running psysh in a command shell in windows 10, xampp (php 5.6), composer (current) installed.
Any suggestions for what I am doing wrong or need to do differently?
The trouble here is that you're not following PSR-4. With the config you provided, it's expecting to find classes in the System namespace inside your phpclasses folder. So, for example, the file Test.php would have the class System\Test.
To just fix it, either change the prefix in your autoload settings to "", or add namespace System; to your Test.php file. If you want to understand why it's acting like it is, you have to understand a bit about how autoloading works in PHP:
PHP lets you register an autoloader to find classes which haven't been encountered yet. The autoloader is handed a class name, and given a chance to find it. Usually they work by mapping class names to files in some way. When they're asked for an unknown class, they translate the class name to a file name, and try to require the file.
PSR-4 is a standard for setting up such an autoloader, and Composer comes with a PSR-4 compliant autoloader for free. For it to work right, you have to lay out your classes and namespaces like PSR-4 expects. If you don't, you can run into strange issues like you're encountering.
When you first tried calling Test::hello(), the class wasn't defined. Your PSR-4 autoloader translated that to a file name, but per your config, there's nowhere defined for non-namespaced classes to live, so it couldn't find a file to load, and it ended up loading nothing. After the autoloader had a chance, PHP still didn't know about that class, so it threw an error.
When you tried calling System\Test::hello(), your PSR-4 autoloader looked it up in the config and translated it to a filename (phpclasses/Test.php), which did exist this time, so it loaded that file. PHP then tried calling the method, but it didn't know about that class, so it threw an error.
The third time, it had already loaded your file and discovered the non-namespaced Test class. So when you tried calling it again, it didn't even bother with the autoloader, and just executed your method.
This is probably very trivial but because I'm new I just don't get it.
I can see the notion of App::uses at the beginning of every file but how does the file know where App is?
There are no includes anywhere and to my understanding there is one autoloader somewhere in lib, does that mean that one autoloader in one file is responsible for loading all the classes (if instantiated)?
I tried reading this part of the manual but still failed to understand how it works.
Also read some material on the spl_autoload_register function itself but no avail.
I'd really appreciate it if someone could help me understand how the files communicate with eachother.
Look at App::load(), it's doc block explains it:
Method to handle the automatic class loading. It will look for each
class' package defined using App::uses() and with this information it
will resolve the package name to a full path to load the class from.
File name for each class should follow the class name. For instance,
if a class is name MyCustomClass the file name should be
MyCustomClass.php.
And look at the frameworks core file lib/Cake/basics.php:
spl_autoload_register(array('App', 'load'));
It registers that method as auto loader.