Difficulty instantiating classes in psysh - php

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.

Related

How to resolve class ambiguity when importing libraries to classmap?

I have an old library I need in my project that doesn't utilize PSR-0/4 or any namespacing at all. It has loads of PHP files with different classes. Let's say I add this library to my project via 'classmap' autoloading (as Composer documentation suggests):
"autoload": {
"classmap": [
"libraries/some-lib"
]
}
The problem is this library doesn't use inheritance/polymorphism - it has base classes with the same names and same methods (but different implementations of those methods) in different PHP files.
libraries/some-lib/Foo.php:
class Foo
{
public function bar()
{
...
}
}
libraries/some-lib/Foo-alternate.php:
class Foo
{
public function bar()
{
...
}
}
So when composer is generating it's classmap Ambiguous class resolution warnings are generated for them and only one version of Foo will be available to me in my project's code via \Foo because of name collisions.
My question is what is the way to go about this problem here without modifying library code? Is there any options Composer provides for situations like this so that I can import both versions of Foo class into composer's classmap and distinguish between the two in my project as a result?
PHP does not allow two classes to have the same name, so even if you will find workaround for this (like manually loading class from specified file depending on context) this will be more like brittle hack than real solution and it will fail when you try to load these two classes in the same request.
Although you want to avoid modifying library code, this is actually the simplest and most reliable solution. It should be easy to make these changes and I doubt that library which still not uses PSR-0/4 is actively developed, so maintenance of fork should not require any additional effort.
Is there any options Composer provides for situations like this so that I can import both versions of Foo class into composer's classmap and distinguish between the two in my project as a result?
Fully qualified name (which for non-namespaced classes is just class name) should be unique, this is PHP "limitation" and Composer autoloader relies on it. The best what you could get is to ignore one of these class using exclude-from-classmap setting:
"autoload": {
"exclude-from-classmap": ["libraries/some-lib/Foo-alternate.php"]
}
Then Composer will autoload only class from libraries/some-lib/Foo.php. You may still load manually second class (require __DIR__ . '/libraries/some-lib/Foo-alternate.php'), but you should be really careful with this (this will result fatal error if libraries/some-lib/Foo.php is already loaded).

Codeception unit tests - error Class 'Dog' not found

I'm using Codeception to TDD the development of a simple application in PHP. I've created my first unit test called DogTest.php with a simple assertion, but it's complaining about not being able to find the Dog class.
I have created a Dog.php file in the root directory, and also placed it under /src, but neither is working. I think this is either a namespace issue or an autoloader issue, but the Codeception documents (and the various TDD guides I've looked at) have this important detail missing.
Can someone please advise on how to get my DogTest to detect the Dog class?
In your composer.json, make sure you have added autoloading configuration. As an example:
"autoload":{
"psr-4":{
"Del\\":"src/"
}
}
Every file in src should have namespace Del. For instance, src/Blank.php would look like:
<?php
namespace Del;
class Blank
{
}
Whereas src/Http/Client.php would have namespace Del\Http.
Once added, run composer dumpautoload to generate the class maps. Your classes should now autoload without problems.
See my Blank starter project with codeception test for more info.
https://github.com/delboy1978uk/blank

autoloading nested classes with composer

I have created a very basic validator class.
My base code is in a my src/ folder, which gets autoloader with
"kevdotbadger\\Validator\\": "src/"
this works fine, so that when I instantiate a new "kevdotbadger\Validator\ Validator is gives me src/Validator.php
My Validator.php class then loads a bunch of sub-classes in my src/Rules directory. These are magically loaded by using the __call, so ->between() should look for src/Rules/between.php. However, for some reason it won't usual load despite it being setup in my composer.json file.
My whole codebase is available at https://github.com/kevdotbadger/validator/
Have I setup my namespace correctly? I think the problem might be with php version 5.3, however I need to use version 5.3.
Thanks.
Well you need to keep the guidelines of psr-4 as you are using it to autoload.
change the folder name "rules" to "Rules"
Uppercase all your file names of classes like:
between.php --> Between.php
that should do the job

How does CakePHP connect files in different directories?

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.

PHP's "use" keyword not properly including

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

Categories