ArgumentsTest.php 4.07 KB
Newer Older
Ketan's avatar
Ketan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Magento\Test\Integrity\Phrase;

use Magento\Framework\Component\ComponentRegistrar;

/**
 * Scan source code for detects invocations of __() function or Phrase object, analyzes placeholders with arguments
 * and see if they not equal
 */
class ArgumentsTest extends \Magento\Test\Integrity\Phrase\AbstractTestCase
{
    /**
     * @var \Magento\Setup\Module\I18n\Parser\Adapter\Php\Tokenizer\PhraseCollector
     */
    protected $_phraseCollector;

    /**
     * List of files that must be omitted
     *
     * @todo remove blacklist related logic when all files correspond to the standard
     * @var array
     */
    protected $blackList;

    protected function setUp()
    {
        $this->_phraseCollector = new \Magento\Setup\Module\I18n\Parser\Adapter\Php\Tokenizer\PhraseCollector(
            new \Magento\Setup\Module\I18n\Parser\Adapter\Php\Tokenizer(),
            true,
            \Magento\Framework\Phrase::class
        );

        $componentRegistrar = new ComponentRegistrar();
        $this->blackList = [
            // the file below is the only file where strings are translated without corresponding arguments
            $componentRegistrar->getPath(ComponentRegistrar::MODULE, 'Magento_Translation')
                . '/Model/Js/DataProvider.php',
        ];
    }

    public function testArguments()
    {
        $incorrectNumberOfArgumentsErrors = [];
        $missedPhraseErrors = [];
        foreach ($this->_getFiles() as $file) {
            if (in_array($file, $this->blackList)) {
                continue;
            }
            $this->_phraseCollector->parse($file);

            foreach ($this->_phraseCollector->getPhrases() as $phrase) {
                $this->checkEmptyPhrases($phrase, $missedPhraseErrors);
                $this->checkArgumentMismatch($phrase, $incorrectNumberOfArgumentsErrors);
            }
        }
        $this->assertEmpty(
            $missedPhraseErrors,
            sprintf(
                "\n%d missed phrases were discovered: \n%s",
                count($missedPhraseErrors),
                implode("\n\n", $missedPhraseErrors)
            )
        );
        $this->assertEmpty(
            $incorrectNumberOfArgumentsErrors,
            sprintf(
                "\n%d usages of inconsistency the number of arguments and placeholders were discovered: \n%s",
                count($incorrectNumberOfArgumentsErrors),
                implode("\n\n", $incorrectNumberOfArgumentsErrors)
            )
        );
    }

    /**
     * Will check if phrase is empty
     *
     * @param $phrase
     * @param $missedPhraseErrors
     */
    private function checkEmptyPhrases($phrase, &$missedPhraseErrors)
    {
        if (empty(trim($phrase['phrase'], "'\"\t\n\r\0\x0B"))) {
            $missedPhraseErrors[] = $this->_createMissedPhraseError($phrase);
        }
    }

    /**
     * Will check if the number of arguments does not match the number of placeholders
     *
     * @param $phrase
     * @param $incorrectNumberOfArgumentsErrors
     */
    private function checkArgumentMismatch($phrase, &$incorrectNumberOfArgumentsErrors)
    {
        if (preg_match_all('/%(\w+)/', $phrase['phrase'], $matches) || $phrase['arguments']) {
            $placeholderCount = count(array_unique($matches[1]));

            // Check for zend placeholders %placeholder% and sprintf placeholder %s
            if (preg_match_all('/%((s)|([A-Za-z]+)%)/', $phrase['phrase'], $placeHolders, PREG_OFFSET_CAPTURE)) {
                foreach ($placeHolders[0] as $ph) {
                    // Check if char after placeholder is not a digit or letter
                    $charAfterPh = $phrase['phrase'][$ph[1] + strlen($ph[0])];
                    if (!preg_match('/[A-Za-z0-9]/', $charAfterPh)) {
                        $placeholderCount--;
                    }
                }
            }

            if ($placeholderCount != $phrase['arguments']) {
                $incorrectNumberOfArgumentsErrors[] = $this->_createPhraseError($phrase);
            }
        }
    }
}