I am trying to use Doctrine Common to create my own annotation. http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html does not match https://github.com/doctrine/common because Call to undefined method Doctrine\\Common\\Annotations\\AnnotationReader::setDefaultAnnotationNamespace and PHP Fatal error: Call to undefined method Doctrine\\Common\\Annotations\\AnnotationRegistry::registerAnnotationNamespace. I checked the source, they are not there. According to git log they were removed a year ago.
I have a PSR-0 autoloader going (from Symfony). So I have a file where the PSR-0 loader expects:
namespace My\Test;
/**
* #Annotation
*/
class Foo {
}
Another class
namespace My\Annotated
/**
* #My\Test\Foo
*/
class Test {
}
Reader:
namespace My\Reader
use ReflectionClass;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use My\Test\Foo;
$reader = new AnnotationReader();
$reflectionClass = new ReflectionClass('My\\Annotated\\Test');
$classAnnotations = $reader->getClassAnnotations($reflectionClass);
var_dump($classAnnotations);
Gets: "[Semantical Error] The annotation "#My\\Test\\Foo" in class My\\Annotated\\test was never imported. Did you maybe forget to add a "use" statement for this annotation?" I ilikely did but for my life I can't figure out where to add it. And I really would like to just use #Foo if possible.
I stumbled accross this issue with error messages like:
Fatal error: Uncaught exception
Doctrine\Common\Annotations\AnnotationException' with message '[Semantical Error]
The annotation "#Symfony\Component\Validator\Constraints\NotNull" in property My\Namespace\Model\Foo\Bar::$name does not exist, or could not be auto-loaded.'
in var/www/virtual/hive/current/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php on line 54
while I was trying to get the symfony2 Validator component to work as a standalone in a legacy non-symfony2 project.
I was also under the impression that the annotations should already be autoloaded, whereas one indeed has to specifically realize that one has to use doctrine's autoloader in order to get the annotations working.
I have looked at how symfony2 loads the registry, and they do it like this in the app/autoload.php
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Composer\Autoload\ClassLoader;
/**
* #var ClassLoader $loader
*/
$loader = require __DIR__.'/../vendor/autoload.php';
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
return $loader;
My project uses a bootstrap.inc.php file that always will be loaded and it had:
require_once PROJECT_PATH . '/vendor/autoload.php';
I just refactored it to look similar:
use Doctrine\Common\Annotations\AnnotationRegistry;
...
$loader = require_once PROJECT_PATH . '/vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
And then the Annotation were found.
Autoloading is apparently handled by AnnotationRegistry::registerAutoloadNamespace which is a PSR-0 autoloader. Documentation/source.
I found that you can do a use My\Test\Foo in the annotated file to use #Foo as an annotation. And the reason for that is possible is because Doctrine re-parses the file solely for use statements.
Related
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!
I have installed elasticsearch using composer. This is my AppKernel.php file
new Elasticsearch\Client()
This is my TestController.php file.
<?php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Elasticsearch\Client;
//use Elasticsearch\Common\AbstractFactory;
class TestController extends Controller{
/**
* #Route("/", name="test-homepage")
*/
public function indexAction(){
$client = new Elasticsearch\Client();
dump($client);
die;
}
}
I am using eclipse as my ide and it shows me error like elasticsearch\Client cannot be resolved.Why is this not working?
First of all: If you define use statement then you don't need FQCN. BTW your FQCN is not right it should starts with \ to prevent loading class from current namespace.
Then: in AppKernel.php you need to define bundles, not every library you installed.
If you have small experience in PHP try to use more easy-to-study frameworks. Symfony is mostly for experienced developers.
If you think that you can work with Symfony then I would recommend you bundle for integrating ElasticSearch and Elastica: https://github.com/FriendsOfSymfony/FOSElasticaBundle. It will save your time.
You should try to instanciate the imported class, using the short name (base on the use statements on your class), like this :
$client = new Client();
Or with the FQCN :
$client = new \Elasticsearch\Client();
(note that the new statement begin with a \ which prevent from trying to load the class from the current namespace (ie : AppBundle\Controller : \AppBundle\Controller\Elasticsearch\Client)
I have a Symfony project to which I added some non-symfony php files containing various classes. But for some reason the classes are not loaded when loading the website, even though the IDE sees them properly.
So, I have a class that needs other classes:
namespace rootspace\FrontBundle\Controller;
use rootspace\FrontBundle\Networks\TwitterOAuth;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class TwitterController extends Controller
{
public function connectAction(){
// The TwitterOAuth instance
$connection = new TwitterOAuth('abc', '123');
}
}
And then the class which fails to load (that needs yet another file)
namespace rootspace\FrontBundle\Networks;
/* Load OAuth lib. You can find it at http://oauth.net */
//require_once('OAuth.php'); -- should this be commented out?
/**
* Twitter OAuth class
*/
class TwitterOAuth {
/* Contains the last HTTP status code returned. */
}
Lastly, the third file
namespace rootspace\FrontBundle\Networks;
use Symfony\Component\Config\Definition\Exception\Exception;
class OAuthConsumer
{
public $key;
public $secret;
}
(...)
I assume the actual filenames don't matter, right? Nor their structure? PhpStorm sees all the classes properly, I can right-click through them, but it fails when deployed.
Thanks for help
Edit - the whole error message says
Attempted to load class "TwitterOAuth" from namespace "rootspace\FrontBundle\Networks" in D:\Dropbox\project\src\rootspace\FrontBundle\Controller\TwitterController.php line 15. Do you need to "use" it from another namespace?
This is because Symfony's autoloader follows PSR standards (PSR-0, PSR-4) which says that fully qualified (with namespace) class name translates to file location and name. So in fact file names does matter.
So in your case rootspace\FrontBundle\Networks\TwitterOAuth class should be located in rootspace/FrontBundle/Networks directory in file called TwitterOAuth.php
If classes you are using does not follow PSR standards you can also register them manually in app/autoloader.php file
Check these for more info:
How can I add a namespace to Symfony 2.1?:
How to autoload class
And check this answer
I forgot to add a .php extension to my filename
I am currently using v1.12.2 Twig as a standalone templating engine.
I wrote a Twig extension called Utility_Twig_Extension in a file called UtilityExtension.php
and an index.php
//index.php
require_once '../vendor/twig/twig/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem(OEBPS);
$twig = new Twig_Environment($loader, array(
'cache' => APP . DS . 'cache',
));
require_once '../vendor/twig/twig/ext/utility/UtilityExtension.php';
$twig->addExtension(new Utility_Twig_Extension());
Here is the UtilityExtension.php
//UtilityExtension.php
namespace UtilityTwigExtension;
class Utility_Twig_Extension extends Twig_Extension
{
public function getName()
{
return "utility";
}
}
Here is my directory structure:
src
|__app
| |__ index.php
|__vendor
|__twig
|__twig
|__ext
|__lib
I cannot even load the file properly.
I have traced the issue to the fact that the extension class tries to extend Twig_Extension.php.
So I require_once the Twig_Extension which is the Extension.php file in UtilityExtension.php. However, still not working.
Most documentation talks about adding a custom Twig Extension in the context of Symfony.
I am using Twig standalone, so I have yet to find any documentation on that.
Please advise.
UPDATE1:
By not working, I meant that I get the 500 server error. I ran error_reporting(E_ALL) was to no avail.
The error was relieved the moment I removed the words extends Twig_Extension from the extension class.
UPDATE2:
I realized it was a namespace issue. because I removed the namespace UtilityTwigExtension; from the UtilityExtension.php and the server 500 error was gone.
So I put the namespace UtilityTwigExtension; back and then call
require_once '../vendor/twig/twig/ext/utility/UtilityExtension.php';
$twig->addExtension(new UtilityTwigExtension\Utility_Twig_Extension());
the error came back.
Question: How do I call the TwigExtension if I insist on using the namespace? Is there a better way of using namespace?
UPDATE3:
I still get server 500 after trying Luceos answer.
error_reporting(E_ALL);
require_once 'constants.php';
require_once 'ZipLib.php';
require_once '../vendor/twig/twig/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem(OEBPS);
$twig = new Twig_Environment($loader, array(
'cache' => APP . DS . 'cache',
));
require_once '../vendor/twig/twig/ext/utility/UtilityExtension.php';
use UtilityTwigExtension\Utility_Twig_Extension;
$twig->addExtension(new Utility_Twig_Extension());
the UtilityExtension.php
namespace UtilityTwigExtension;
class Utility_Twig_Extension extends Twig_Extension
{
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName() {
return 'utility';
}
}
So let's put the comments in an answer and move on from there without crowding the comments:
First of call the extension from the correct namespace:
use UtilityTwigExtension\Utility_Twig_Extension;
$twig->addExtension(new Utility_Twig_Extension());
Use and namespaces calls are normally placed at the top of the file.
You can also try calling the namespace + object directly by using:
$twig->addExtension(new UtilityTwigExtension\Utility_Twig_Extension());
Update 3
The Utility_Twig_Extension extends Twig_Extension from the namespace UtilityTwigExtension, which does not exist. I assume Twig_Extension is not in any namespace so you'll use \Twig_Extension:
namespace UtilityTwigExtension;
class Utility_Twig_Extension extends \Twig_Extension
{
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName() {
return 'utility';
}
}
I am attempting to use JMSSerializer as a stand alone library to map JSON responses from an API to my model classes and am running into some issues.
Executing the following code results in an exception:
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use JMS\Serializer\Annotation AS JMS;
class Trii {
/**
* User ID for this session
* #JMS\SerializedName("userID")
* #JMS\Annotation(getter="getUserId")
* #JMS\Type("string")
* #var string
*/
private $userId;
public function getUserId() {
return $this->userId;
}
public function setUserId($userId) {
$this->userId = $userId;
}
}
$serializer = \JMS\Serializer\SerializerBuilder::create()->setDebug(true)->build();
$object = $serializer->deserialize('{"userID":"Trii"}', 'Trii', 'json');
var_dump($object);
?>
Here is the exception
Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The annotation "#JMS\Serializer\Annotation\SerializedName" in property Trii::$userId does not exist, or could not be auto-loaded.
I have the following libraries installed for the project via composer
{
"require": {
"jms/serializer": "1.0.*#dev"
}
}
Is there something obvious I am missing since I am not using the whole Doctrine 2 solution?
EDIT: my final solution was to create a bootstrap file with the following content:
<?php
// standard composer install vendor autoload magic
require dirname(__DIR__) . '/vendor/autoload.php';
// Bootstrap the JMS custom annotations for Object to Json mapping
\Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation',
dirname(__DIR__).'/vendor/jms/serializer/src'
);
?>
Pretty sure this enables silent auto-loading which is much more convenient than registering the namespaces yourself.
AnnotationRegistry::registerLoader('class_exists');
I ran into the same problem and found your question through Google. Unfortunately you hadn't yet received any answers, so I had to dig in myself. :P
The thing is, Doctrine Annotations, which JMSSerializer Annotations uses, does NOT use normal PHP autoloading.
How are these annotations loaded? From looking at the code you could guess that the ORM Mapping, Assert Validation and the fully qualified annotation can just be loaded using the defined PHP autoloaders. This is not the case however: For error handling reasons every check for class existence inside the AnnotationReader sets the second parameter $autoload of class_exists($name, $autoload) to false. To work flawlessly the AnnotationReader requires silent autoloaders which many autoloaders are not. Silent autoloading is NOT part of the PSR-0 specification for autoloading.
This means you have to register the Annotation file(s) yourself:
AnnotationRegistry::registerFile(
<PROJECT ROOT> .
"/vendor/jms/serializer/src/JMS/Serializer/Annotation/SerializedName.php");
... or the whole namespace (preferred method):
AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation',
<PROJECT ROOT> . "/vendor/jms/serializer/src");
Be careful with the path in registerAutoloadNamespace. I first tried to register the whole path to annotations in the same manner with registerFile:
<PROJECT ROOT> . "/vendor/jms/serializer/src/JMS/Serializer/Annotation
but that failed miserably. :D
I hope this gets you a step further. :)
#SirArturio has the correct answer to this Autoloading puzzle, and I just wanted to add a touch more clarity in response to #messified or anyone else struggling to get this working. As he eloquently explained, the automatic PSR-0 handler in composer, or SPL isn't going to cut it for loading these annotations since they use Doctrine's autoloading.
So here is small complete example. Whenever you create your JMS Serializer object to begin serialization is a good time to add the annotation namespace to doctrine's autoloader. For clarity sake I'm assuming no IoC, and fully qualified namespaces (hint hint, use dependency injection):
<?php
Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation',
$your_app_basepath . "/vendor/jms/serializer/src");
$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$json_output = $serializer->serialize('MyProject\MyClass', 'json');
Then in your MyProject\MyClass:
<?php
use JMS\Serializer\Annotation as JMS;
class MyClass{
/** #JMS\Exclude */
private $something_secret;
}
And that should cut it, autoloading the proper annotation file using doctrine instead of composer.
Check the capitalisation of your annotations. I had a similar problem when deploying from a Windows dev environment to an Ubuntu server which was caused by a typo in the case of my annotation. Windows files are case-insensitive so it works there but fails on Linux.
If you use Composer, you can get loader by specifying a path in require:
$loader = require(__DIR__ . '/../vendor/autoload.php');
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
Here is the solution
1.go to php directory then install composer
php composer-setup.php
2. go to project sdk directory
e.g.
cd /Applications/XAMPP/xamppfiles/htdocs/streetreturn/adn_sdk-php-master
update composer to install dependencies
php /Users/zakir/composer.phar update
*Note: /Users/zakir/composer.phar will be located when install composer in step 1