ContainerAwareManager::createData()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 3
crap 2
1
<?php
2
3
namespace Paymaxi\FractalBundle;
4
5
use Doctrine\Common\Util\ClassUtils;
6
use League\Fractal\Manager;
7
use League\Fractal\Resource\Collection;
8
use League\Fractal\Resource\Item;
9
use League\Fractal\Resource\ResourceInterface;
10
use League\Fractal\Scope;
11
use League\Fractal\TransformerAbstract;
12
use Paymaxi\FractalBundle\Resolver\ResolverInterface;
13
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
14
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
15
16
/**
17
 * Class ContainerAwareManager
18
 *
19
 * @package Paymaxi\FractalBundle
20
 */
21
class ContainerAwareManager extends Manager implements ContainerAwareInterface
22
{
23
    use ContainerAwareTrait;
24
25
    /**
26
     * Create Data.
27
     *
28
     * Main method to kick this all off. Make a resource then pass it over, and use toArray()
29
     *
30
     * @param ResourceInterface $resource
31
     * @param string $scopeIdentifier
32
     * @param Scope $parentScopeInstance
33
     *
34
     * @return Scope
35
     */
36 4
    public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null)
37
    {
38 4
        $this->resolveTransformer($resource);
39 4
        $scopeInstance = new Scope($this, $resource, $scopeIdentifier);
40
41
        // Update scope history
42 4
        if ($parentScopeInstance !== null) {
43
            // This will be the new children list of parents (parents parents, plus the parent)
44 2
            $scopeArray = $parentScopeInstance->getParentScopes();
45 2
            $scopeArray[] = $parentScopeInstance->getScopeIdentifier();
46
47 2
            $scopeInstance->setParentScopes($scopeArray);
48
        }
49
50 4
        return $scopeInstance;
51
    }
52
53
    /**
54
     * @param ResourceInterface $resource
55
     */
56 4
    private function resolveTransformer(ResourceInterface $resource)
57
    {
58 4
        $resourceTransformer = $resource->getTransformer();
59
60 4
        if ($resourceTransformer instanceof TransformerAbstract) {
61
            return;
62
        }
63
64 4
        $serviceRegistry = $this->container->get('fractal.transformer.resolvers');
65
66 4
        if ($resource instanceof Item) {
67 1
            $instance = ClassUtils::getRealClass(\get_class($resource->getData()));
68
        } elseif ($resource instanceof Collection) {
69 4
            $data = $resource->getData();
70
71 4
            if (!\is_array($data) && !($data instanceof \Traversable)) {
72
                throw new \InvalidArgumentException(
73
                    sprintf('Expected array or array iterator. Given %s', \gettype($data))
74
                );
75
            }
76
77 4
            $data = ($data instanceof \IteratorAggregate) ? $data->getIterator() : $data;
78
79 4
            $element = \is_array($data) ? array_values($data)[0] : $data->current();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Traversable as the method current() does only exist in the following implementations of said interface: APCUIterator, AppendIterator, ArrayIterator, CachingIterator, CallbackFilterIterator, DirectoryIterator, EmptyIterator, File_Iterator, FilesystemIterator, FilterIterator, Generator, GlobIterator, HttpMessage, HttpRequestPool, Imagick, ImagickPixelIterator, InfiniteIterator, Issue523, IteratorIterator, LimitIterator, MongoCommandCursor, MongoCursor, MongoGridFSCursor, MultipleIterator, NoRewindIterator, PHPUnit_Runner_Filter_GroupFilterIterator, PHPUnit_Runner_Filter_Group_Exclude, PHPUnit_Runner_Filter_Group_Include, PHPUnit_Runner_Filter_Test, PHPUnit_Util_TestSuiteIterator, PHP_Token_Stream, ParentIterator, Phar, PharData, RecursiveArrayIterator, RecursiveCachingIterator, RecursiveCallbackFilterIterator, RecursiveDirectoryIterator, RecursiveFilterIterator, RecursiveIteratorIterator, RecursiveRegexIterator, RecursiveTreeIterator, RegexIterator, SQLiteResult, SebastianBergmann\CodeCoverage\Node\Iterator, SimpleXMLIterator, SplDoublyLinkedList, SplFileObject, SplFixedArray, SplHeap, SplMaxHeap, SplMinHeap, SplObjectStorage, SplPriorityQueue, SplQueue, SplStack, SplTempFileObject, Symfony\Component\Finder...or\CustomFilterIterator, Symfony\Component\Finder...DateRangeFilterIterator, Symfony\Component\Finder...epthRangeFilterIterator, Symfony\Component\Finder...DirectoryFilterIterator, Symfony\Component\Finder...\FileTypeFilterIterator, Symfony\Component\Finder...lecontentFilterIterator, Symfony\Component\Finder...\FilenameFilterIterator, Symfony\Component\Finder...tiplePcreFilterIterator, Symfony\Component\Finder...ator\PathFilterIterator, Symfony\Component\Finder...ursiveDirectoryIterator, Symfony\Component\Finder...SizeRangeFilterIterator, Symfony\Component\Finder...rator\InnerNameIterator, Symfony\Component\Finder...rator\InnerSizeIterator, Symfony\Component\Finder...rator\InnerTypeIterator, Symfony\Component\Finder\Tests\Iterator\Iterator, Symfony\Component\Finder...or\MockFileListIterator, Symfony\Component\Finder...tiplePcreFilterIterator, TestIterator, TestIterator2, Zend\Stdlib\FastPriorityQueue, Zend\Stdlib\PriorityList, Zend\Stdlib\SplPriorityQueue, Zend\Stdlib\SplQueue, Zend\Stdlib\SplStack.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
80
81 4
            if (!\is_object($element)) {
82
                throw new \InvalidArgumentException(
83
                    sprintf('Element expected to be object. Given %s', \gettype($element))
84
                );
85
            }
86
87 4
            $instance = ClassUtils::getRealClass(\get_class($element));
88
        } else {
89
            return;
90
        }
91
92
        /** @var ResolverInterface[] $resolvers */
93 4
        $resolvers = $serviceRegistry->all();
94
95 4
        foreach ($resolvers as $resolver) {
96 4
            if ($resolver instanceof ContainerAwareInterface) {
97 4
                $resolver->setContainer($this->container);
98
            }
99
100 4
            if ($resolver->supports($instance, $resourceTransformer)) {
101 4
                $transformer = $resolver->resolve($instance, $resourceTransformer);
102 4
                $resource->setTransformer($transformer);
0 ignored issues
show
Documentation introduced by
$transformer is of type object<League\Fractal\TransformerAbstract>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
103 4
                continue;
104
            }
105
        }
106
    }
107
}