Is it good practice to include namespaces for classes in #param annotations? I know that phpdoc does not support namespaces, but how will other tools like phpdox or Doxygen act?
Which way is better / more common?
namespace foo\someNamespace;
use foo\someOtherNamespace\MyOtherClass;
--- with namespace ---
/**
* #param \foo\someOtherNamespace\MyOtherClass $otherClass
*/
class myClass(MyOtherClass $otherClass)
{
// do something
}
--- without namespace ---
/**
* #param MyOtherClass $otherClass
*/
class myClass(MyOtherClass $otherClass)
{
// do something
}
The namespace is a part of the complete name of the class. So if you wouldn't add the namespace to the classname in #param you would give a wrong type.
Personally I think namespaces will become very soon the main criteria for the organization of classes in PHP. So the documentation tools will all have to use them.
Related
In a Symfony 5 project we're using the APi Platform to generate a REST API.
One of the entity classes is called FarmMetadata.
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource()
* #ORM\Table(... some settings ...)
* #ORM\Entity
*/
class FarmMetadata
{
// properties and methods
}
When I run php bin/console debug:router it shows the following routes for this resource:
api_farm_metadatas_get_collection GET ANY ANY /api/farm_metadatas.{_format}
api_farm_metadatas_post_collection POST ANY ANY /api/farm_metadatas.{_format}
api_farm_metadatas_get_item GET ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_delete_item DELETE ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_put_item PUT ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_patch_item PATCH ANY ANY /api/farm_metadatas/{id}.{_format}
However the word "metadata" is already plural. There's no such thing as metadatas. How can I turn off the pluralisation for this endpoint?
I tried using shortName:
* #ApiResource(
* shortName="FarmMetadata" // also "farm_metadata"
* )
but it doesn't change the output.
If I use:
* #ApiResource(
* shortName="Metadata"
* )
then the route names and paths are changed:
api_metadata_get_collection GET ANY ANY /api/metadata.{_format}
api_metadata_post_collection POST ANY ANY /api/metadata.{_format}
api_metadata_get_item GET ANY ANY /api/metadata/{id}.{_format}
api_metadata_delete_item DELETE ANY ANY /api/metadata/{id}.{_format}
api_metadata_put_item PUT ANY ANY /api/metadata/{id}.{_format}
api_metadata_patch_item PATCH ANY ANY /api/metadata/{id}.{_format}
but that's not what I want.
I know that I can declare a path for every operation, but that would hurt the DRY principle.
How can I achieve the desired behaviour?
You could use "path" option on each operation.
Cf https://api-platform.com/docs/core/operations/#configuring-operations
For example
* shortName="Metadata",
* itemOperations={
* "get"={
* "path"="/metadata/{id}"
I don't think this is possible by configuration: these routes are built in the private method ApiPlatform\Core\Bridge\Symfony\Routing\ApiLoader::addRoute (at least in v2.6 which I'm using), and this uses a static call to a pluralizer - so: decorating the ApiLoader is not easily possible (as the addRoute method is private), and exchanging the ways of generating the route is not possible (due to the usage of a static method call).
Looks like you need to open a feature request ticket in their bug tracker...
you can do easily as you want
api_platform:
...
path_segment_name_generator: App\InfraStructure\ApiPlatform\Core\SingularPathSegmentNameGenerator
create SingularPathSegmentNameGenerator
<?php
declare(strict_types=1);
namespace App\InfraStructure\ApiPlatform\Core;
use ApiPlatform\Core\Operation\PathSegmentNameGeneratorInterface;
use ApiPlatform\Core\Util\Inflector;
final class SingularPathSegmentNameGenerator implements PathSegmentNameGeneratorInterface
{
public function getSegmentName(string $name, bool $collection = true): string
{
return Inflector::tableize($name);
}
}
I am looking to extend a trait by using it in another trait. However the trait is using a method that looks like it isn't extending. The trait works, so I am wondering how.
Why does this trait have access to the markEntityForCleanup method?
The code is in this repo for Drupal Test Traits
<?php
namespace weitzman\DrupalTestTraits\Entity;
use Drupal\Tests\node\Traits\NodeCreationTrait as CoreNodeCreationTrait;
/**
* Wraps the node creation trait to track entities for deletion.
*/
trait NodeCreationTrait
{
use CoreNodeCreationTrait {
createNode as coreCreateNode;
}
/**
* Creates a node and marks it for automatic cleanup.
*
* #param array $settings
* #return \Drupal\node\NodeInterface
*/
protected function createNode(array $settings = [])
{
$entity = $this->coreCreateNode($settings);
$this->markEntityForCleanup($entity);
return $entity;
}
}
I found the issue.
When using the Drupal Test Traits package you are expected to use your own custom php-unit bootstrap.php and manually load the required packages.
Adding this line to the bottom of the bootstrap script will gain access to the namespace in php.
// <?php is needed for SO to do the syntax highlighting.
<?php
// Register more namespaces, as needed.
$class_loader->addPsr4('weitzman\DrupalTestTraits\Entity\\', "$root/vendor/weitzman\drupal-test-triats\src\Entity");
How do I enable code completion for clases and methods I create in code for in netbeans 8.
Thanks,
Dion
You really should google this kinda thing, but I'm in the mood to answer a question :-)
Anyway, most IDE's read your code in pretty much the same way. I can't attest to NetBeans specifically (I use PHPStorm), but the general idea is to make sure you add docblocks to your classes, methods etc. The IDE reads these and can then provide decent code-completion.
<?php
namespace App;
/**
* Class MyClass
* Does some stuff
* #package App
*/
class MyClass extends SomeOtherClass
{
/**
* This is my var
* #var string
*/
public $myVar = 'some val';
/**
* This is my method
* #param string $yourString
* #return SomethingElse
*/
public function myMethod ($yourString)
{
$this->myVar = $yourString;
return new SomethingElse($this->myVar);
}
}
Have a look at the PHPdoc site for the tag syntax. Most IDE's will also have a way of generating this for you as well.
I am writing an annotation parser for php (I cannot used any 3rd party ones due to specific needs) and I took sympfony2 code as an inspiration
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="product")
*/
class Product
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
As I can see here we define ORM in use statement and then somehow the annotation parser knows that ORM is an alias to Doctrine\ORM\Mapping.
My ultimate goal is to be able to pass class names as aliases into my annotations
use some/namespace/Enum;
use another/ns/PropEnum;
class Abc
{
/**
* #Enum(PropEnum)
**/
protected $prop;
}
Could you please point me towards the right direction as I do not even know where to start?
Thanks
Let's take a look at how doctrine/annotations (the package used by Symfony) solves this problem.
The PhpParser parses the PHP file of the class (which you can get by ReflectionClass#getFilename()). Let's look at the parseClass() method line by line:
if (method_exists($class, 'getUseStatements')) {
return $class->getUseStatements();
}
Doctrine has a custom StaticReflectionClass class which already has a getUseStatements() method to get the use statements in the class file. I assume you don't use that class, so let's move on!
if (false === $filename = $class->getFilename()) {
return array();
}
$content = $this->getFileContent($filename, $class->getStartLine());
if (null === $content) {
return array();
}
These statements retrieve the file content of the class. If there was no content or no file, there are also no use statements.
$namespace = preg_quote($class->getNamespaceName());
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
Here they are looking for the line that defines the namespace for the class in the file. It'll be followed by the use statements, so the regex just extracts the namespace and everything that follows it.
$tokenizer = new TokenParser('<?php ' . $content);
$statements = $tokenizer->parseUseStatements($class->getNamespaceName());
return $statements;
Then it creates a TokenParser class with only that content in it and let it find the use statements in it.
If you look at the TokenParser, you'll find that it uses token_get_all() to transform the file into PHP tokens used by the PHP engine and then just moves through that token tree looking for T_USE statements, which it extracts and saves.
I'm using PHP Storm as my IDE, but I believe that other IDE's such as Netbeans will have the same issue as I'll explain below.
When using a framework like Symfony2, we have the wonderful world of Dependency Injection added. So objects can simply be instantiated using code like the following snippet:
$myThingy = $this->get('some_cool_service');
This is very handy, as objects are already configured beforehand. The one problem is, that auto-completion breaks entirely in basically any PHP IDE, as the IDE does not know what type the get() method is returning.
Is there a way to preserve auto-completion? Would creating for example an extension of Controller be the answer? For example:
class MyController extends Controller {
/**
* #return \MyNamespace\CoolService
*/
public getSomeCoolService() {
return new CoolService();
}
}
and then for application controllers, specify MyController as the base class instead of Controller?
What about using a Factory class, or any other possible methods?
It is more involving, but you can still do this with eclipse PDT:
$myThingy = $this->get('some_cool_service');
/* #var $myThingy \MyNamespace\CoolService */
UPDATE:
The example on this page shows you may also use the other way round with phpStorm:
$myThingy = $this->get('some_cool_service');
/* #var \MyNamespace\CoolService $myThingy */
You could define private properties in your controllers
class MyController extends Controller
{
/**
* #var \Namespace\To\SomeCoolService;
*/
private $my_service;
public function myAction()
{
$this->my_service = $this->get('some_cool_service');
/**
* enjoy your autocompletion :)
*/
}
}
I use base Controller class for bundle. You need to annotate the return in method. At least that works on Eclipse.
/**
* Gets SomeCoolService
*
* #return \Namespace\To\SomeCoolService
*/
protected function getSomeCoolService()
{
return $this->get('some_cool_service');
}
I don't like /*var ... */, because it gets too much into code.
I don't like private properties, because you can wrongly assume that services are already loaded.
I use Komodo Studio, and tagging variables with #var, even inside methods, preserves auto completion for me.
namespace MyProject\MyBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request;
class WelcomeController extends ContainerAware
{
public function indexAction()
{
/*#var Request*/$request = $this->container->get('request');
$request->[autocomplete hint list appears here]
}
}
working with netbeans IDE 7.1.2 PHP