grgk/seo-analyzer

View on GitHub
src/Analyzer.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace SeoAnalyzer;

use InvalidArgumentException;
use ReflectionException;
use SeoAnalyzer\HttpClient\Client;
use SeoAnalyzer\HttpClient\ClientInterface;
use SeoAnalyzer\HttpClient\Exception\HttpException;
use SeoAnalyzer\Metric\MetricFactory;
use SeoAnalyzer\Metric\MetricInterface;
use Symfony\Component\Translation\Loader\YamlFileLoader;
use Symfony\Component\Translation\Translator;

class Analyzer
{
    /**
     * @var Page Web page to analyze
     */
    public $page;

    /**
     * @var string Default locale to use for translations
     */
    public $locale = 'en_GB';

    /**
     * @var array Metrics array
     */
    public $metrics = [];

    /**
     * @var ClientInterface
     */
    public $client;

    /**
     * @var Translator
     */
    public $translator;

    /**
     * @param Page|null $page Page to analyze
     * @param ClientInterface|null $client
     */
    public function __construct(Page $page = null, ClientInterface $client = null)
    {
        $this->client = $client;
        if (empty($client)) {
            $this->client = new Client();
        }

        if (!empty($page)) {
            $this->page = $page;
        }
    }

    /**
     * Analyzes page at specified url.
     *
     * @param string $url Url to analyze
     * @param string|null $keyword
     * @param string|null $locale
     * @return array
     * @throws ReflectionException
     */
    public function analyzeUrl(string $url, string $keyword = null, string $locale = null): array
    {
        if (!empty($locale)) {
            $this->locale = $locale;
        }
        $this->page = new Page($url, $locale, $this->client);
        if (!empty($keyword)) {
            $this->page->keyword = $keyword;
        }
        return $this->analyze();
    }

    /**
     * Analyzes html document from file.
     *
     * @param string $filename
     * @param string|null $locale
     * @return array
     * @throws ReflectionException
     */
    public function analyzeFile(string $filename, string $locale = null): array
    {
        $this->page = new Page(null, $locale, $this->client);
        $this->page->content = file_get_contents($filename);
        return $this->analyze();
    }

    /**
     * Analyzes html document from string.
     *
     * @param string $htmlString
     * @param string|null $locale
     * @return array
     * @throws ReflectionException
     */
    public function analyzeHtml(string $htmlString, string $locale = null): array
    {
        $this->page = new Page(null, $locale, $this->client);
        $this->page->content = $htmlString;
        return $this->analyze();
    }

    /**
     * Starts analysis of a Page.
     *
     * @return array
     * @throws ReflectionException
     */
    public function analyze()
    {
        if (empty($this->page)) {
            throw new InvalidArgumentException('No Page to analyze');
        }
        if (empty($this->metrics)) {
            $this->metrics = $this->getMetrics();
        }
        $results = [];
        foreach ($this->metrics as $metric) {
            if ($analysisResult = $metric->analyze()) {
                $results[$metric->name] = $this->formatResults($metric, $analysisResult);
            }
        }
        return $results;
    }

    /**
     * Returns available metrics list for a Page
     *
     * @return array
     * @throws ReflectionException
     */
    public function getMetrics(): array
    {
        return array_merge($this->page->getMetrics(), $this->getFilesMetrics());
    }

    /**
     * Returns file related metrics.
     *
     * @return array
     * @throws ReflectionException
     */
    public function getFilesMetrics(): array
    {
        return [
            'robots' => MetricFactory::get('file.robots', $this->getFileContent(
                $this->page->getFactor('url.parsed.scheme') . '://' . $this->page->getFactor('url.parsed.host'),
                'robots.txt'
            )),
            'sitemap' => MetricFactory::get('file.sitemap', $this->getFileContent(
                $this->page->getFactor('url.parsed.scheme') . '://' . $this->page->getFactor('url.parsed.host'),
                'sitemap.xml'
            ))
        ];
    }

    /**
     * Downloads file from Page's host.
     *
     * @param $url
     * @param $filename
     * @return bool|string
     */
    protected function getFileContent($url, $filename)
    {
        $cache = new Cache();
        $cacheKey = 'file_content_' . base64_encode($url . '/' . $filename);
        if ($value = $cache->get($cacheKey)) {
            return $value;
        }
        try {
            $response = $this->client->get($url . '/' . $filename);
            $content = $response->getBody()->getContents();
        } catch (HttpException $e) {
            return false;
        }
        $cache->set($cacheKey, $content, 300);
        return $content;
    }

    /**
     * Sets up the translator for current locale.
     *
     * @param string $locale
     */
    public function setUpTranslator(string $locale)
    {
        $this->translator = new Translator($locale);
        $this->translator->setFallbackLocales(['en_GB']);
        $yamlLoader = new YamlFileLoader();
        $this->translator->addLoader('yaml', $yamlLoader);
        $localeFilename = dirname(__DIR__) . '/locale/' . $locale . '.yml';
        if (is_file($localeFilename)) {
            $this->translator->addResource('yaml', $localeFilename, $locale);
        }
    }

    /**
     * Formats metric analysis results.
     *
     * @param MetricInterface $metric
     * @param $results
     * @return array
     */
    protected function formatResults(MetricInterface $metric, string $results): array
    {
        if (empty($this->translator)) {
            $this->setUpTranslator($this->locale);
        }
        return [
            'analysis' => $this->translator->trans($results),
            'name' => $metric->name,
            'description' => $this->translator->trans($metric->description),
            'value' => $metric->value,
            'negative_impact' => $metric->impact,
        ];
    }
}