Completed
Push — master ( 7b9f4b...1cd743 )
by Andreas
13s queued 10s
created

ODM/MongoDB/Tools/Console/Command/QueryCommand.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Tools\Console\Command;
6
7
use LogicException;
8
use Symfony\Component\Console;
9
use Symfony\Component\Console\Input\InputArgument;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\VarDumper\Cloner\VarCloner;
12
use Symfony\Component\VarDumper\Dumper\CliDumper;
13
use function assert;
14
use function is_numeric;
15
use function is_string;
16
use function json_decode;
17
18
/**
19
 * Command to query mongodb and inspect the outputted results from your document classes.
20
 */
21
class QueryCommand extends Console\Command\Command
22
{
23
    /**
24
     * @see Console\Command\Command
25
     */
26
    protected function configure()
27
    {
28
        $this
29
        ->setName('odm:query')
30
        ->setDescription('Query mongodb and inspect the outputted results from your document classes.')
31
        ->setDefinition([
32
            new InputArgument(
33
                'class',
34
                InputArgument::REQUIRED,
35
                'The class to query.'
36
            ),
37
            new InputArgument(
38
                'query',
39
                InputArgument::REQUIRED,
40
                'The query to execute and output the results for.'
41
            ),
42
            new InputOption(
43
                'hydrate',
44
                null,
45
                InputOption::VALUE_NONE,
46
                'Whether or not to hydrate the results in to document objects.'
47
            ),
48
            new InputOption(
49
                'skip',
50
                null,
51
                InputOption::VALUE_REQUIRED,
52
                'The number of documents to skip in the cursor.'
53
            ),
54
            new InputOption(
55
                'limit',
56
                null,
57
                InputOption::VALUE_REQUIRED,
58
                'The number of documents to return.'
59
            ),
60
        ])
61
        ->setHelp(<<<EOT
62
Execute a query and output the results.
63
EOT
64
        );
65
    }
66
67
    /**
68
     * @see Console\Command\Command
69
     */
70
    protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
71
    {
72
        $query = $input->getArgument('query');
73
        assert(is_string($query));
74
75
        $dm = $this->getHelper('documentManager')->getDocumentManager();
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method getDocumentManager() does only exist in the following implementations of said interface: Doctrine\ODM\MongoDB\Too...r\DocumentManagerHelper.

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...
76
        $qb = $dm->getRepository($input->getArgument('class'))->createQueryBuilder();
77
        $qb->setQueryArray((array) json_decode($query));
78
        $qb->hydrate((bool) $input->getOption('hydrate'));
79
80
        $skip = $input->getOption('skip');
81 View Code Duplication
        if ($skip !== null) {
82
            if (! is_numeric($skip)) {
83
                throw new LogicException("Option 'skip' must contain an integer value");
84
            }
85
86
            $qb->skip((int) $skip);
87
        }
88
89
        $limit = $input->getOption('limit');
90 View Code Duplication
        if ($limit !== null) {
91
            if (! is_numeric($limit)) {
92
                throw new LogicException("Option 'limit' must contain an integer value");
93
            }
94
95
            $qb->limit((int) $limit);
96
        }
97
98
        $cloner = new VarCloner();
99
        $dumper = new CliDumper(static function (string $payload) use ($output) : void {
100
            $output->write($payload);
101
        });
102
103
        foreach ($qb->getQuery() as $result) {
104
            $dumper->dump($cloner->cloneVar($result));
105
        }
106
    }
107
}
108