src/Twitter.php
protected function parse($page, $meta)
    {
        $twitter['meta'] = $meta;

        $tweet = '//ol[@id="stream-items-id"]/li[@data-item-type="tweet"]';

        // foo_bar will be converted to foo['bar']
        $tweet_details = [
            'user_avatar'   => './/img[contains(@class, "avatar")]/@src',
            'user_fullname' => './/@data-name',
            'user_name'     => './/@data-screen-name',
            'stats_fav'     => './/span[contains(@class, "ProfileTweet-action--favorite")]//@data-tweet-stat-count',
            'stats_rt'      => './/span[contains(@class, "ProfileTweet-action--retweet")]//@data-tweet-stat-count',
            'id'            => './/@data-tweet-id',
            'retweetid'     => './/@data-retweet-id',
            'text'          => './/p[contains(@class, "tweet-text")]',
            'datetime'      => './/@data-time',
        ];

        $user_stats = '//ul[contains(@class, "ProfileNav-list")]';

        $user_stats_details = [
            'followers' => './/li[contains(@class, "ProfileNav-item--followers")]/a/@title',
            'following' => './/li[contains(@class, "ProfileNav-item--following")]/a/@title',
            'likes'     => './/li[contains(@class, "ProfileNav-item--favorites")]/a/@title',
            'lists'     => './/li[contains(@class, "ProfileNav-item--lists")]/a/@title',
            'tweets'    => './/li[contains(@class, "ProfileNav-item--tweets")]/a/@title',
        ];

        libxml_use_internal_errors(true);

        $dom = new \DOMDocument;
        $dom->preserveWhiteSpace = false;
        $dom->loadHTML($page);

        $xpath = new \DomXpath($dom);

        foreach ($xpath->query($tweet) as $item) {

            foreach ($tweet_details as $key => $query) {

                $value = @$xpath->query($query, $item)->item(0)->textContent;

                if ($key == 'retweetid') {

                    $details['retweet'] = empty($value) ? 0 : 1;

                    continue;
                }

                if ($key == 'text') {

                    $value = $this->clean($value);
                    $value = htmlspecialchars($value, ENT_NOQUOTES);

                    $details['text']['raw'] = $value;
                    $details['text']['html'] = $this->html($value);

                    $details['reply'] = substr($value, 0, 1) === '@' ? 1 : 0;

                    continue;
                }

                if ($key == 'datetime') $value = date('c', $value);

                if (strpos($key, '_') !== false) {

                    $subarray = explode('_', $key);
                    $subname  = $subarray[0];
                    $subkey   = $subarray[1];

                    $details[$subname][$subkey] = $value;

                } else {

                    $details[$key] = $value;
                }
            }

            $details['user']['url'] = 'https://twitter.com/' . $details['user']['name'];
            $details['url'] = $details['user']['url'] . '/status/' . $details['id'];

            // type casting
            $details['id'] = (int) $details['id'];
            $details['stats']['fav'] = (int) $details['stats']['fav'];
            $details['stats']['rt']  = (int) $details['stats']['rt'];

            ksort($details);

            $twitter['tweets'][] = $details;
        }

        $twitter['tweets_count'] = count($twitter['tweets']);

        if ($meta['type'] == 'user') {

            $item = $xpath->query($user_stats)->item(0);

            foreach ($user_stats_details as $key => $query) {

                $value = @$xpath->query($query, $item)->item(0)->textContent;

                $twitter['user_stats'][$key] = preg_match('~([\d,]+)~', $value, $matches) ? (int) strtr($matches[1], [',' => '']) : 0;
            }
        }

        ksort($twitter);

        return $twitter;
    }
protected function parse($page, $meta)
    {
        $twitter['meta'] = $meta;

        $tweet = '//ol[@id="stream-items-id"]/li[@data-item-type="tweet"]';

        // foo_bar will be converted to foo['bar']
        $tweet_details = [
            'user_avatar'   => './/img[contains(@class, "avatar")]/@src',
            'user_fullname' => './/@data-name',
            'user_name'     => './/@data-screen-name',
            'stats_fav'     => './/span[contains(@class, "ProfileTweet-action--favorite")]//@data-tweet-stat-count',
            'stats_rt'      => './/span[contains(@class, "ProfileTweet-action--retweet")]//@data-tweet-stat-count',
            'id'            => './/@data-tweet-id',
            'retweetid'     => './/@data-retweet-id',
            'text'          => './/p[contains(@class, "tweet-text")]',
            'datetime'      => './/@data-time',
        ];

        $user_stats = '//ul[contains(@class, "ProfileNav-list")]';

        $user_stats_details = [
            'followers' => './/li[contains(@class, "ProfileNav-item--followers")]/a/@title',
            'following' => './/li[contains(@class, "ProfileNav-item--following")]/a/@title',
            'likes'     => './/li[contains(@class, "ProfileNav-item--favorites")]/a/@title',
            'lists'     => './/li[contains(@class, "ProfileNav-item--lists")]/a/@title',
            'tweets'    => './/li[contains(@class, "ProfileNav-item--tweets")]/a/@title',
        ];

        libxml_use_internal_errors(true);

        $dom = new \DOMDocument;
        $dom->preserveWhiteSpace = false;
        $dom->loadHTML($page);

        $xpath = new \DomXpath($dom);

        foreach ($xpath->query($tweet) as $item) {

            foreach ($tweet_details as $key => $query) {

                $value = @$xpath->query($query, $item)->item(0)->textContent;

                if ($key == 'retweetid') {

                    $details['retweet'] = empty($value) ? 0 : 1;

                    continue;
                }

                if ($key == 'text') {

                    $value = $this->clean($value);
                    $value = htmlspecialchars($value, ENT_NOQUOTES);

                    $details['text']['raw'] = $value;
                    $details['text']['html'] = $this->html($value);

                    $details['reply'] = substr($value, 0, 1) === '@' ? 1 : 0;

                    continue;
                }

                if ($key == 'datetime') $value = date('c', $value);

                if (strpos($key, '_') !== false) {

                    $subarray = explode('_', $key);
                    $subname  = $subarray[0];
                    $subkey   = $subarray[1];

                    $details[$subname][$subkey] = $value;

                } else {

                    $details[$key] = $value;
                }
            }

            $details['user']['url'] = 'https://twitter.com/' . $details['user']['name'];
            $details['url'] = $details['user']['url'] . '/status/' . $details['id'];

            // type casting
            $details['id'] = (int) $details['id'];
            $details['stats']['fav'] = (int) $details['stats']['fav'];
            $details['stats']['rt']  = (int) $details['stats']['rt'];

            ksort($details);

            $twitter['tweets'][] = $details;
        }

        $twitter['tweets_count'] = count($twitter['tweets']);

        if ($meta['type'] == 'user') {

            $item = $xpath->query($user_stats)->item(0);

            foreach ($user_stats_details as $key => $query) {

                $value = @$xpath->query($query, $item)->item(0)->textContent;

                $twitter['user_stats'][$key] = preg_match('~([\d,]+)~', $value, $matches) ? (int) strtr($matches[1], [',' => '']) : 0;
            }
        }

        ksort($twitter);

        return $twitter;
    }
Extract a series of information from a twitter page.

@see doc/Twitter.md for the detailed structure of the returned array
@see Twitter::queryToMeta() for details about the $meta array

@param string $page content of a twitter page
@param array  $meta

@return array|bool false on error