Search code examples
api-platform.comsymfony5php-8

Why do I get an error associated with PHP8 #[ApiResource()] attribute when using "php bin/console cache:clear"command in Symfony project?


I'm a rookie in development and it's my first time to use PHP8 attributes so I don't have a clue to guess the problem's orgin. :s

I'm trying to configure APIPlatform to determine which properties users can see associated with basic HTTP operations in my API project. For this, I'm using PHP8 attributes for ApiPlatform : #[ApiResource()] and #[Groups()].

I'm using PHP8 - Symfony 5.3 - VSCode 1.58.2 - Window 10

Problems :

  1. It seems in the documentation that the HTTP Operations configure with normalizationContext are ok (as expected, PUT is no longer visible in my api documentation) but the properties are not affected (all the properties are still send when I'm doing a request). I assumed that serialization groups aren't working properly...

  2. I tried to clear symfony cache with the command "php bin/console cache:clear". I received a message to tell me that I have a parse error in my #[ApiResource()] attribute but I can't find it.

Here my code :

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use App\Repository\ClientRepository;
use Doctrine\Common\Collections\Collection;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ORM\Entity(repositoryClass=ClientRepository::class)
 */
#[ApiResource(
    collectionOperations: [
        'get' => [
            'normalizationContext' => [
                'groups' => [
                    'read_Client_collection',
                ],
            ],
        ],
        'post' => [
            'denormalizationContext' => [
                'groups' => [
                    'write_Client_item',
                ],
            ],
        ],
    ],
    itemOperations: [
        'get' => [
            'normalizationContext' => [
                'groups' => [
                    'read_Client_item',
                ],
            ],
        ],
        'delete',
        'patch' => [
            'denormalizationContext' => [
                'groups' => [
                    'write_Client_item',
                ],
            ],
        ],
    ],
)]
class Client
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    #[Groups(['read_Client_collection', 'read_User_collection', 'read_User_item'])]
    private $id;

    /**
     * @ORM\Column(type="string", length=75)
     */
    #[Groups(['read_Client_collection', 'read_Client_item', 'read_User_collection', 'read_User_item', 'write_Client_item'])]
    private $companyName;

    /**
     * @ORM\Column(type="string", length=20, nullable=true)
     */
    #[Groups(['read_Client_item', 'write_Client_item'])]
    private $phoneNumber;

    /**
     * @ORM\Column(type="string", length=255)
     */
    #[Groups(['read_Client_item', 'write_Client_item'])]
    private $address;

    /**
     * @ORM\Column(type="string", length=45)
     */
    #[Groups(['read_Client_item', 'write_Client_item'])]
    private $SiretNumber;

    /**
     * @ORM\OneToMany(targetEntity=User::class, mappedBy="client")
     */
    private $users;

    public function __construct()
    {
        $this->users = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCompanyName(): ?string
    {
        return $this->companyName;
    }

    public function setCompanyName(string $companyName): self
    {
        $this->companyName = $companyName;

        return $this;
    }

    public function getAddress(): ?string
    {
        return $this->address;
    }

    public function setAddress(string $address): self
    {
        $this->address = $address;

        return $this;
    }

    public function getSiretNumber(): ?string
    {
        return $this->SiretNumber;
    }

    public function setSiretNumber(string $SiretNumber): self
    {
        $this->SiretNumber = $SiretNumber;

        return $this;
    }

    public function getPhoneNumber(): ?string
    {
        return $this->phoneNumber;
    }

    public function setPhoneNumber(?string $phoneNumber): self
    {
        $this->phoneNumber = $phoneNumber;

        return $this;
    }

    /**
     * @return Collection|User[]
     */
    public function getUsers(): Collection
    {
        return $this->users;
    }

    public function addUser(User $user): self
    {
        if (!$this->users->contains($user)) {
            $this->users[] = $user;
            $user->setClient($this);
        }

        return $this;
    }

    public function removeUser(User $user): self
    {
        if ($this->users->removeElement($user)) {
            // set the owning side to null (unless already changed)
            if ($user->getClient() === $this) {
                $user->setClient(null);
            }
        }

        return $this;
    }
}

Here the error in my terminal when I'm trying "php bin/console cache:clear" command (the line 32 is the one juste before "itemOperations: [") :

ParseError {#6596
#message: "syntax error, unexpected ','"
#code: 0
#file: "D:\...\project\src\Entity\Client.php"
#line: 32
trace: {
D:\...\project\src\Entity\Client.php:32 {
    Symfony\Component\ErrorHandler\DebugClassLoader->loadClass(string $class): void^
    ›     ],
    › ],
    › itemOperations: [
}
Symfony\Component\ErrorHandler\DebugClassLoader->loadClass() {}
spl_autoload_call() {}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:1123 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:1245 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:1300 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:1220 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:1053 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:1035 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:869 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:719 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\DocParser.php:376 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\AnnotationReader.php:178 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\PsrCachedReader.php:155 { …}
D:\...\project\vendor\doctrine\annotations\lib\Doctrine\Common\Annotations\PsrCachedReader.php:88 { …}
D:\...\project\vendor\api-platform\core\src\Util\AnnotationFilterExtractorTrait.php:47 { …}
D:\...\project\vendor\api-platform\core\src\Util\AnnotationFilterExtractorTrait.php:112 { …}
D:\...\project\vendor\api-platform\core\src\Bridge\Symfony\Bundle\DependencyInjection\Compiler\AnnotationFilterPass.php:65 { …}
D:\...\project\vendor\symfony\dependency-injection\Compiler\Compiler.php:91 { …}
D:\...\project\vendor\symfony\dependency-injection\ContainerBuilder.php:749 { …}
D:\...\project\vendor\symfony\http-kernel\Kernel.php:545 { …}
D:\...\project\vendor\symfony\http-kernel\Kernel.php:786 { …}
D:\...\project\vendor\symfony\http-kernel\Kernel.php:125 { …}
D:\...\project\vendor\symfony\framework-bundle\Console\Application.php:168 { …}
D:\...\project\vendor\symfony\framework-bundle\Console\Application.php:74 { …}
D:\...\project\vendor\symfony\console\Application.php:167 { …}
D:\...\project\vendor\symfony\runtime\Runner\Symfony\ConsoleApplicationRunner.php:56 { …}
D:\...\project\vendor\autoload_runtime.php:35 { …}
D:\...\project\bin\console:11 { …}
}
}

I tried without success to :

  • change my groups name from "write:Client:collection" to "write_Client_Collection"

  • add an attributes:[] before CollectionOperations

    #[ApiResource( attributes: [ 'collectionOperations' => [ // ... ], 'itemOperations' => [ // ... ] ]) ]

  • put all the attributes specifications on a single line to see like this :

    #[ApiResource(collectionOperations: ['get' => ['normalizationContext' => ['groups' => ['read_Client_collection',],],],'post' => ['denormalizationContext' => ['groups' => ['write_Client_item',],],],],itemOperations: ['get' => ['normalizationContext' => ['groups' => ['read_Client_item',],],],'delete','patch' => ['denormalizationContext' => ['groups' => ['write_Client_item',],],],],)]

It works for the parse error... but my API documentation was totally empty.

  • I tried to change my windows end of line CRLF to linux LF in VS Code but it doesn't work.

I'm running out of ideas... Thank you for your help :)

Notice : The problem is not specific to this entity file : when I suppressed the ApiResource attribute in the Client entity, the problem switch to an other resource entity of the project.


Solution

  • I manage to fix my problems... I don't know if somenone else could be interrested in the solution but just in case...

    • Problem 1 : normalization groups doesn't work :

    By writing

    'normalizationContext' => [
    

    I mixed 2 different syntaxes :

    #[ApiResource(
        normalizationContext: ['groups' => ['read']],
        denormalizationContext: ['groups' => ['write']],
    )]
    

    with

    #[ApiResource(attributes: [
        'normalization_context' => ['groups' => ['read']],
        'denormalization_context' => ['groups' => ['write']],
    ])]
    

    I replaced 'normalizationContext' by 'normalization_context' and groups works fine now...

    • Problem 2 : parse error in #[ApiAttributes()] when I'm using "symfony cache:clear" command :

    My PHP 8.0.3 is determined by .php-version file at the root of the folder where my symfony project is but the rest of my windows system run PHP 7.3.12. I change my system PHP version for 8.0.3 too and the problem is now solved.

    (On window 10, for the rookie like me :

    • right click on windows icon in bottom left screen corner
    • click on "System"
    • click on "advanced system parameters" (right part of the screen)
    • click on "environnement variables" button
    • In system variables, select "paths" and click on "modify"
    • If the php version you want is not in the list, add the path.
    • select it and move it to the top of the php version list.)

    I presume the problem is due to my composer installation : I made a global installation for it, so when I change composer.json file to indicate php >=8.0, this action create a conflict with the PHP version running on the system. A message tell me so when I tried a "composer update" command.

    Perhaps I'm wrong so if someone could confirm my interpretations, it could be great. Thank you all.

    EDIT : I figure it out how to have composer with the same PHP version choosen for the project instead of the system PHP version.

    After the creation of .php-version file in the project parent folder (where you note the php version to specify to Symfony client which one you want for this project specifically), you have to run 'symfony composer diagnose' command to change composer PHP version accordingly.