I'm currently working on a Symfony2 project with Phpspec and I'm having problems to extend a Spec class described in a different namespace.
In my project, for instance, I'm having the following class described in spec/Acme/Model/Foo/FooSpec.php :
namespace spec\Acme\Model\Foo;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
abstract class FooSpec extends ObjectBehavior{
//some code here
}
And I have another class in spec/Acme/Model/Bar/BarSpec.php extending FooSpec :
namespace spec\Acme\Model\Bar;
use spec\Acme\Model\Foo\FooSpec;
class BarSpec extends FooSpec{
//some code here
}
When I try to run phpspec, I have the following error :
PHP Fatal error: Class 'spec\Acme\Model\Foo\FooSpec' not found in /home/user/Projects/Acme/spec/Acme/Model/Bar/BarSpec.php on line 9
The only way I found to make it work was to add the following line in spec/Acme/Model/Bar/BarSpec.php:
include('./spec/Acme/Model/Foo/FooSpec.php');
I don't know why I have to include this specific file to make it run, especially when the other classes (like PhpSpec\ObjectBehavior) are correctly found.
Do you have any idea why is this happening?
Edit:
As suggested by #Phil and #Sheikh Heera in the comments, I tried to set up an autoload to register my spec namespace but it's not working neither. Here is what I tried so far :
require_once getcwd() . '/vendor/composer/ClassLoader.php';
$loader = new \Composer\Autoload\ClassLoader();
// register classes with namespaces
$loader->add('spec', getcwd().'/spec');
// activate the autoloader
$loader->register();
I also tried to modify the file vendor/composer/autoload_namespaces.php to add this :
return array(
//some code here
'spec' => array(getcwd() . '/spec'),
//some more code
);
But still the same error. I also tried with 'spec' => array(getcwd()) or $loader->add('spec', getcwd()); just to see what will happen and this time I get a Cannot redeclare class on another spec class.
My php version is PHP 5.4.9-4ubuntu2.4 (cli).
Thank you in advance for your help.
A more generic solution is to put the spec directory into the autoload-dev configuration of your composer.json:
"autoload-dev": {
"psr-0": {
"spec\\":""
}
},
This way, composer will generate the namespace also for the specs, which should be on the root of the repository:
return array(
'spec\\' => array($baseDir . '/'),
...
);
Finally, what I actually did to "solve" my "problem", as suggested by Phil and Sheikh Heera in the comments, is to autoload my namespace so it is recognized by spec.
Here is what I did to make it work :
I added the following line to the file `vendor/composer/autoload_namespaces.php` :
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
//some code
'spec\\Acme\\Behavior' => array(getcwd()),
//some code
);
Then I created the following folder structure :
│── spec
│ └── Acme
│ └── Behavior
│ └── FooBehavior.php
I declared the namespace in `spec/Acme/Behavior/FooBehavior.php` this way :
namespace spec\Acme\Behavior;
And used it in `spec/Acme/Model/Bar/BarSpec.php` as following :
use spec\Acme\Behavior\FooBehavior;
class BarSpec extends FooBehavior{
//...
}
I know this is not the best practice because everytime someone will be working on this project it will have to reproduce manually the first step. So if you have any better idea, please feel free to comment or post an answer.
Related
I just started using CakePHP 3. I'm trying to get up and running but doing the simplest of things is proving to be a headache.
I have a class, MySimpleClass, in src/App/MySimpleClass.php
<?php
namespace MyApp\MyNamespace;
class MySimpleClass {
public function aSimpleFunction() {
return 1;
}
}
And in my controller:
<?php
namespace App\Controller;
use Cake\Controller\Controller;
use MyApp\MyNamespace\MySimpleClass;
class MyFirstController extends Controller {
public function display() {
$mySimpleClass = new MySimpleClass();
echo $mySimpleClass->aSimpleFunction();
}
}
But this always gives me:
Error in: ROOT/src/Controller/TestController.php, line 10 Class 'MyApp\MyNamespace\MySimpleClass' not found
I use bin/cake server to run the HTTP server
I added App::className('MyApp\MyNamespace\MySimpleClass'); to bootstrap.php to see if that'd make a difference but it doesn't.
I've run composer dump-autoload on several occasions.
I tried putting MySimpleClass into global namespace but it still gave me the error.
PHPStorm isn't giving me any syntax or naming errors.
You should place your controllers in:
src/Controller
and use:
namespace App\Controller;
Nevermind, I finally found the solution...
I just had to add src to the classmap in composer.json:
"autoload": {
"classmap": [
"src"
],
...,
}
Edit:
Instead of abusing classmap, I just moved my class files to a directory outside of src
If your class is in the file src/App/MySimpleClass.php, the namespace has to be App\App. The first App refers to the root namespace of every CakePHP 3.x application, the second App refers to the subdirectory within the src directory you put your class file into.
If the namespace of the class should be App\MyNamespace, your classfile has to be located in src/MyNamespace.
Also: according to the error message you quoted, your MyFirstController is in a file called TestController.php. Instead, it should be MyFirstController.php. I recommend giving https://book.cakephp.org/3.0/en/intro/conventions.html#file-and-class-name-conventions a read regarding this topic.
I have a problem with Codeception/AspectMock.
When using custom autoloader and try to create an instance of a class which has parent form the same custom namespace I have this error:
PHP Fatal error: Uncaught InvalidArgumentException: Class [parent
class name] was not found by locator in
vendor/goaop/parser-reflection/src/ReflectionEngine.php:112
I have very simple setup:
<?php
require_once __DIR__ . '/vendor/autoload.php';
$kernel = AspectMock\Kernel::getInstance();
$kernel->init([
'debug' => true,
'includePaths' => [__DIR__. '/lib'],
]);
$kernel->loadFile(__DIR__ . '/autoload.php'); // custom autoloader
$b = new \lib\B();
Class \lib\B:
namespace lib;
class B extends A {}
Class \lib\A:
namespace lib;
class A
{
public function getName()
{
return static::class;
}
}
Class B is loaded via my custom autoloader, but then the locator tries to load parent class A via composer autoloader and returns this error. Is this a bug, or I'm doing something wrong?
The topic starter has already got an answer on GitHub.
In order to use custom autoloader you should re-init ReflectionEngine with composite class locator that will be able to locate your classes or you can use CallableLocator with closure for resolving paths.
Or, even better you could switch your code base to the PSR0/PSR-4
For example:
$kernel->loadFile(__DIR__ . '/autoload.php'); // custom autoloader
\Go\ParserReflection\ReflectionEngine::init(
new class implements \Go\ParserReflection\LocatorInterface {
public function locateClass($className) {
return (new ReflectionClass($className))->getFileName();
}
}
);
$b = new \lib\B(); // here you go
If you can easily do a find and replace on your codebase, maybe you could refactor your code to PSR-4 autoloading standards and do away with the need for a custom autoloader altogether.
This is the spec https://www.php-fig.org/psr/psr-4/. I'll try and explain it as simply as possible.
Imagine changing your lowercase namespace lib to Lib, and setting that namespace to the src/ directory in your composer.json:
"autoload": {
"psr-4": {
"Lib\\": "src/"
}
}
After setting that, run composer dumpautoload. Then all you need to do is search and replace namespace lib;, replacing with namespace Lib;.
An example class located in src/Form.php would have namespace Lib; at the top, followed by class Form.
<?php
namepace Lib;
class Form
{
// code
}
Namespaces use the folder naming convention. All classes directly in src/ have namespace Lib;. If there are subdirectories, the directory name becomes part of the namespace. For example a file in src/Form/Field/Text.php would have namespace Lib\Form\Field; class Text {}.
<?php
namepace Lib\Form\Field;
class Text
{
// code
}
You can see the full convention in the link above, but the general rule is make any folders begin with a capital letter, as with your classname, and the autoloader should be able to find all of your classes.
This is probably the best practice solution for you, and again as I said, only requires a little bit of file renaming and namespace tweaking. Good luck!
so i have the following structure for what i'm trying to target but what i'm getting is the Fatal error class MainController not found , i'm new to autoload and namespace thing (but getting into them fast) i just need to know why this is happening , hope you guys have bit explained situation for me? i did saw few of answer around stackoverflow but nuthing helped, i know i'm doing something really wrong, but thats how i will learn :). structure:
composer.json
src/controllers/MainController.php
the following is my autoload inside composer.json file:
"autoload": {
"psr-4": {
"controllers\\": "src/controllers/"
}
}
and this is how my MainController.php looks alike :
namespace MainController;
class MainController{
function test($name){
echo 'holaaaa'.$name;
}
}
controller call inside: app/loads/loadController.php :::
use MainController;
$MainController = new MainController();
more info on vendor/autoload.php
it's included inside : index.php and inside index.php i have included mainapp.php and inside mainapp.php i have included loadcontroller.php vich calls the controller
structure screenshot:
Okay, in your Composer file you say the namespace is controllers. In your PHP file you say the namespace is MainController. They need to be the same for the autoloading to work.
If we are to go by your Composer file then the PHP should look like this:
namespace controllers;
class MainController {}
And the class should be called like this:
$MainController = new \controllers\MainController;
Or like this:
use controllers\MainController;
$MainController = new MainController;
Or, if you want a nicer-looking class name:
use controllers\MainController as Controller;
$MainController = new Controller;
In my case I only managed to correct it after deleting my folder from the composer (vendor) and rerunning the command composer dump-autoload
I created a new directory at root 'components'. Then I put a file 'ClassName.php' into this folder. Declare a namespace namespace components; and the class named ClassName Now I try to use it like
$c = new app\components\ClassName()
But there's an error. It says that Class 'components\ClassName' not found.
Where am I missing? I suppose that I should add folder components in include_path or something like that. Please help to understand.
I found the solution.
Just add
Yii::setAlias('components', dirname(dirname(\__DIR__)) . '/components');
In className.php:
namespace components;
Then usage:
$c = new components\ClassName();
This is how you can create custom components in Yii2 (basic application)
Create a folder named "components" in the root of your application.
Then create a class for your component with proper namespace and extend Component class:
namespace app\components;
use yii\base\Component;
class MyComponent extends Component {
public function testMethod() {
return 'test...';
}
}
Add component inside the config/web.php file:
'components' => [
// ...
'mycomponent' => [
'class' => 'app\components\MyComponent'
]
]
Now you can access your component like this:
Yii::$app->mycomponent->testMethod();
In ClassName.php:
namespace app\components;
Added
When you create new ClassName instance, don't forget the leading backward slash for namespace (AKA fully qualified namespace), if your current namespace is not global, because in that case namespace will be treated as relative (Like UNIX paths), use:
$c = new \app\components\ClassName(); //If your current namespace is app\controllers or app\models etc.
$c = new app\components\ClassName(); //If your current namespace is global namespace
You can read more about namespaces basics in PHP documentation
It should be late but I guest my solution may help some one later. I had the same issue and the resolved it the way bellow:
If you want to autoload (import) a customer class in your app, you to do:
create your class where ever you want e.g in my case, i created common/core/Utilities.php
then you have to create an alias (alias is a short cut name you give to your folder path). In my case in create an alias for my folder core (note i should also create an alias for my component folder) e.g
Yii::setAlias('core', dirname(DIR).'/core');
this snippet i put it in my common/config/boostrap.php file. because yii2 load this file at running time.
Now you are ready to use your customize class where ever you want. Just do
$utilities = new \core\Utilities();
Hopefully this may !!!!!!!
I previously had a pretty simple autoload script working nicely, but as I've noticed that Doctrine2 is using Composer for this, I thought it might be nice to streamline everything. Unfortunately, Composer does not seem to be working as I understood it to.
Here is the relevant part of my composer.json
"autoload": {
"psr-0": {
"": "models/",
"Catalog2\\Config": "class/"
}
}
Note that the "": "models/" line used by Doctrine2 has been working just fine. After I ran composer update , the bottom part of my vendor/composer/autoload_namespaces.php looks like so:
'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib'),
'Catalog2\\Config' => array($baseDir . '/class'),
'' => array($baseDir . '/models'),
So far so good, I think. In my routes.php file (basically a front-controller) I have the following:
<?php
use Catalog2\Config;
//autoload classes
require_once __DIR__.'/vendor/autoload.php';
try {
$router = new Router;
} catch(Exception $e ) {
echo "<strong>Can't create router object</strong><br/>";
}
Here Catalog2\Config\Router should be calling my class/Router.php, which begins as follows:
<?php
namespace Catalog2\Config;
class Router {
protected $resource; //what are we manipulating? A product? An order?
protected $action; //what are we doing with that resource?
When I go to the page I get this:
Fatal error: Class 'Router' not found in /home/tom/Code/productCatalog2/routes.php on line 14
What is going wrong here? I repeat that Doctrine2 was able to autoload my model code from /models, so why aren't my changes working?
According to PSR-0 the namespace prefix will be included to the path.
So the complete filename for your class must be:
class/Catalog2/Config/Router.php
Meanwhile PSR-4 would behave like you expected: it will just match the namespace prefix and will not append it additionally to the given path.
References:
https://getcomposer.org/doc/04-schema.md#autoload
PS: you probably want the namespace prefix to be "Catalog2\\Config\\" (see the trailing slash)