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

src/Command/StatusesHomeTimelineCommand.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
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