Passed
Push — master ( 092a24...2d756b )
by Alexis
01:25 queued 11s
created

src/Command/StatusesHomeTimelineCommand.php (1 issue)

Labels
Severity

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
namespace AlexisLefebvre\Bundle\AsyncTweetsBundle\Command;
4
5
use Abraham\TwitterOAuth\TwitterOAuth;
6
use AlexisLefebvre\Bundle\AsyncTweetsBundle\Utils\PersistTweet;
7
use Symfony\Component\Console\Helper\ProgressBar;
8
use Symfony\Component\Console\Helper\Table;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
13
class StatusesHomeTimelineCommand extends BaseCommand
14
{
15
    private $displayTable;
16
    private $table;
17
    private $progress;
18
19
    /** @see https://dev.twitter.com/rest/reference/get/statuses/home_timeline */
20
    private $parameters = [
21
        'count'           => 200,
22
        'exclude_replies' => true,
23
    ];
24
25 9
    protected function configure()
26
    {
27 9
        parent::configure();
28
29
        $this
30 9
            ->setName('statuses:hometimeline')
31 9
            ->setDescription('Fetch home timeline')
32
            // http://symfony.com/doc/2.3/cookbook/console/console_command.html#automatically-registering-commands
33 9
            ->addOption(
34 9
                'table',
35 9
                null,
36 9
                InputOption::VALUE_NONE,
37 9
                'Display a table with tweets'
38
            );
39 9
    }
40
41
    /**
42
     * @param InputInterface  $input
43
     * @param OutputInterface $output
44
     *
45
     * @return int|void
46
     */
47 7
    protected function execute(InputInterface $input, OutputInterface $output)
48
    {
49 7
        $this->setAndDisplayLastTweet($output);
50
51 7
        $content = $this->getContent($input);
52
53 7
        if (!is_array($content)) {
54 2
            $this->displayContentNotArrayError($output, $content);
55
56 2
            return 1;
57
        }
58
59 5
        $numberOfTweets = count($content);
60
61 5
        if ($numberOfTweets == 0) {
62 2
            $output->writeln('<comment>No new tweet.</comment>');
63
64 2
            return 0;
65
        }
66
67 3
        $this->addAndDisplayTweets($input, $output, $content, $numberOfTweets);
68 3
    }
69
70
    /**
71
     * @param OutputInterface $output
72
     */
73 7
    protected function setAndDisplayLastTweet(OutputInterface $output)
74
    {
75
        // Get the last tweet
76 7
        $lastTweet = $this->em
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method getLastTweet() does only exist in the following implementations of said interface: AlexisLefebvre\Bundle\As...\Entity\TweetRepository.

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...
77 7
            ->getRepository('AsyncTweetsBundle:Tweet')
78 7
            ->getLastTweet();
79
80
        // And use it in the request if it exists
81 7
        if ($lastTweet) {
82 1
            $this->parameters['since_id'] = $lastTweet->getId();
83
84 1
            $comment = 'last tweet = '.$this->parameters['since_id'];
85
        } else {
86 6
            $comment = 'no last tweet';
87
        }
88
89 7
        $output->writeln('<comment>'.$comment.'</comment>');
90 7
    }
91
92
    /**
93
     * @param InputInterface $input
94
     */
95 1
    protected function getContent(InputInterface $input)
96
    {
97 1
        $connection = new TwitterOAuth(
98 1
            $this->container->getParameter('twitter_consumer_key'),
99 1
            $this->container->getParameter('twitter_consumer_secret'),
100 1
            $this->container->getParameter('twitter_token'),
101 1
            $this->container->getParameter('twitter_token_secret')
102
        );
103
104 1
        return $connection->get(
105 1
            'statuses/home_timeline',
106 1
            $this->parameters
107
        );
108
    }
109
110
    /**
111
     * @param OutputInterface $output
112
     * @param null|object     $content
113
     */
114 2
    protected function displayContentNotArrayError(
115
        OutputInterface $output,
116
        $content
117
    ) {
118 2
        $formatter = $this->getHelper('formatter');
119
120 2
        $errorMessages = ['Error!', 'Something went wrong, $content is not an array.'];
121 2
        $formattedBlock = $formatter->formatBlock($errorMessages, 'error');
122 2
        $output->writeln($formattedBlock);
123 2
        $output->writeln(print_r($content, true));
124 2
    }
125
126
    /**
127
     * @param InputInterface  $input
128
     * @param OutputInterface $output
129
     * @param array           $content
130
     * @param int             $numberOfTweets
131
     */
132 3
    protected function addAndDisplayTweets(
133
        InputInterface $input,
134
        OutputInterface $output,
135
        $content,
136
        $numberOfTweets
137
    ) {
138 3
        $output->writeln('<comment>Number of tweets: '.$numberOfTweets.'</comment>');
139
140
        // Iterate through $content in order to add the oldest tweet first,
141
        //  if there is an error the oldest tweet will still be saved
142
        //  and newer tweets can be saved next time the command is launched
143 3
        $tweets = array_reverse($content);
144
145 3
        $this->setProgressBar($output, $numberOfTweets);
146 3
        $this->setTable($input, $output);
147 3
        $this->iterateTweets($tweets);
148
149 3
        $this->progress->finish();
150 3
        $output->writeln('');
151
152 3
        if ($this->displayTable) {
153 2
            $this->table->render();
154
        }
155 3
    }
156
157
    /**
158
     * @param OutputInterface $output
159
     * @param int             $numberOfTweets
160
     */
161 3
    protected function setProgressBar(
162
        OutputInterface $output,
163
        $numberOfTweets
164
    ) {
165 3
        $this->progress = new ProgressBar($output, $numberOfTweets);
166 3
        $this->progress->setBarCharacter('<comment>=</comment>');
167 3
        $this->progress->start();
168 3
    }
169
170
    /**
171
     * @param InputInterface  $input
172
     * @param OutputInterface $output
173
     */
174 3
    protected function setTable(
175
        InputInterface $input,
176
        OutputInterface $output
177
    ) {
178 3
        $this->displayTable = $input->getOption('table');
179
180
        // Display
181 3
        if ($this->displayTable) {
182 2
            $this->table = new Table($output);
183 2
            $this->table
184 2
                ->setHeaders(['Datetime', 'Text excerpt', 'Name']);
185
        }
186 3
    }
187
188
    /**
189
     * @param array $tweets
190
     */
191 3
    protected function iterateTweets($tweets)
192
    {
193 3
        $persistTweet = new PersistTweet(
194 3
            $this->em,
195 3
            $this->displayTable,
196 3
            $this->table
197
        );
198
199 3
        foreach ($tweets as $tweetTmp) {
200 3
            $persistTweet->addTweet($tweetTmp, true);
201
202 3
            $this->progress->advance();
203
        }
204 3
    }
205
}
206