Passed
Push — master ( b6ddb2...6e9ccf )
by Kévin
18:24 queued 13:31
created

src/EventListener/WriteListener.php (1 issue)

Severity
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\EventListener;
15
16
use ApiPlatform\Core\Api\IriConverterInterface;
17
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
18
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
19
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
20
use ApiPlatform\Core\Metadata\Resource\ToggleableOperationAttributeTrait;
21
use ApiPlatform\Core\Util\RequestAttributesExtractor;
22
use ApiPlatform\Core\Util\ResourceClassInfoTrait;
23
use Symfony\Component\HttpFoundation\Response;
24
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
25
26
/**
27
 * Bridges persistence and the API system.
28
 *
29
 * @author Kévin Dunglas <[email protected]>
30
 * @author Baptiste Meyer <[email protected]>
31
 */
32
final class WriteListener
33
{
34
    use ResourceClassInfoTrait;
35
    use ToggleableOperationAttributeTrait;
36
37
    public const OPERATION_ATTRIBUTE_KEY = 'write';
38
39
    private $dataPersister;
40
    private $iriConverter;
41
42
    public function __construct(DataPersisterInterface $dataPersister, IriConverterInterface $iriConverter = null, ResourceMetadataFactoryInterface $resourceMetadataFactory = null, ResourceClassResolverInterface $resourceClassResolver = null)
43
    {
44
        $this->dataPersister = $dataPersister;
45
        $this->iriConverter = $iriConverter;
46
        $this->resourceMetadataFactory = $resourceMetadataFactory;
47
        $this->resourceClassResolver = $resourceClassResolver;
48
    }
49
50
    /**
51
     * Persists, updates or delete data return by the controller if applicable.
52
     */
53
    public function onKernelView(GetResponseForControllerResultEvent $event): void
54
    {
55
        $controllerResult = $event->getControllerResult();
56
        $request = $event->getRequest();
57
58
        if (
59
            $controllerResult instanceof Response
60
            || $request->isMethodSafe(false)
61
            || !($attributes = RequestAttributesExtractor::extractAttributes($request))
62
            || !$attributes['persist']
63
            || $this->isOperationAttributeDisabled($attributes, self::OPERATION_ATTRIBUTE_KEY)
64
        ) {
65
            return;
66
        }
67
68
        if (!$this->dataPersister->supports($controllerResult, $attributes)) {
69
            return;
70
        }
71
72
        switch ($request->getMethod()) {
73
            case 'PUT':
74
            case 'PATCH':
75
            case 'POST':
76
                $persistResult = $this->dataPersister->persist($controllerResult, $attributes);
77
78
                if (!\is_object($persistResult)) {
79
                    @trigger_error(sprintf('Not returning an object from %s::persist() is deprecated since API Platform 2.3 and will not be supported in API Platform 3.', DataPersisterInterface::class), E_USER_DEPRECATED);
80
                } else {
81
                    $controllerResult = $persistResult;
82
                    $event->setControllerResult($controllerResult);
83
                }
84
85
                if ($controllerResult instanceof Response) {
86
                    break;
87
                }
88
89
                $hasOutput = true;
90
                if ($this->resourceMetadataFactory instanceof ResourceMetadataFactoryInterface) {
91
                    $resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']);
92
                    $outputMetadata = $resourceMetadata->getOperationAttribute($attributes, 'output', [
93
                        'class' => $attributes['resource_class'],
94
                    ], true);
95
96
                    $hasOutput = \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class'];
97
                }
98
99
                if (!$hasOutput) {
100
                    break;
101
                }
102
103
                if ($this->iriConverter instanceof IriConverterInterface && $this->isResourceClass($this->getObjectClass($controllerResult))) {
104
                    $request->attributes->set('_api_write_item_iri', $this->iriConverter->getIriFromItem($controllerResult));
105
                }
106
107
                break;
108
            case 'DELETE':
109
                $this->dataPersister->remove($controllerResult, $attributes);
0 ignored issues
show
The call to ApiPlatform\Core\DataPer...sterInterface::remove() has too many arguments starting with $attributes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

109
                $this->dataPersister->/** @scrutinizer ignore-call */ 
110
                                      remove($controllerResult, $attributes);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
110
                $event->setControllerResult(null);
111
                break;
112
        }
113
    }
114
}
115