mozart icon indicating copy to clipboard operation
mozart copied to clipboard

classmap_prefix duplication and other issues with classmaps

Open olegabr opened this issue 3 years ago • 2 comments

{
    "name": "ethereumico/ether-and-erc20-tokens-woocommerce-payment-gateway",
    "description": "Ether and ERC20 tokens WooCommerce Payment Gateway enables customers to pay with Ether or any ERC20 or ERC223 token on your WooCommerce store.",
    "type": "project",
    "repositories": [
       {
          "type": "vcs",
          "url": "https://github.com/olegabr/web3.php"
       },
       {
          "type": "vcs",
          "url": "https://github.com/olegabr/php-keccak"
       }
    ],
    "require": {
        "olegabr/web3.php": "~0.1",
        "freemius/wordpress-sdk": "^2.4",
        "woocommerce/action-scheduler": "dev-master"
    },
    "license": "GPLv3",
    "authors": [
        {
            "name": "EthereumICO",
            "email": "[email protected]"
        }
    ],
    "minimum-stability": "dev",
    "require-dev": {
        "coenjacobs/mozart": "dev-master"
    },
    "extra": {
        "mozart": {
            "dep_namespace": "\\Ethereumico\\Epg\\Dependencies\\",
            "dep_directory": "/vendor-epg/",
            "classmap_directory": "/vendor-epg/classes/",
            "classmap_prefix": "ether_and_erc20_tokens_woocommerce_payment_gateway_",
            "packages": [
                "freemius/wordpress-sdk",
                "guzzlehttp/guzzle",
                "guzzlehttp/promises",
                "guzzlehttp/psr7",
                "olegabr/keccak",
                "olegabr/web3.php",
                "paragonie/constant_time_encoding",
                "paragonie/random_compat",
                "phpseclib/phpseclib",
                "psr/http-message",
                "ralouphie/getallheaders",
                "symfony/polyfill-intl-idn",
                "symfony/polyfill-intl-normalizer",
                "symfony/polyfill-mbstring",
                "symfony/polyfill-php72",
                "woocommerce/action-scheduler"
            ],
            "delete_vendor_directories": true
        }
    },
    "scripts": {
        "post-install-cmd": [
            "\"vendor/bin/mozart\" compose",
            "composer dump-autoload"
        ],
        "post-update-cmd": [
            "\"vendor/bin/mozart\" compose",
            "composer dump-autoload"
        ]
    }
}

It created file /home/USER/PROJECT/vendor-epg/classes/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php with contents:

<?php

class ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_Normalizer extends Symfony\Polyfill\Intl\ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_Normalizer\Normalizer
{

Three issues here:

  1. class prefix added 4 times
  2. Symfony\Polyfill\Intl\ namespace is not prefixed with Ethereumico\Epg\Dependencies\
  3. The last namespace before class name is prefixed with class name prefix (4 times of course): ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_ether_and_erc20_tokens_woocommerce_payment_gateway_Normalizer

The /home/USER/PROJECT/vendor-epg/Symfony/Polyfill/Intl/Normalizer/Normalizer.php file is defined correctly as:

<?php

namespace \Ethereumico\Epg\Dependencies\Symfony\Polyfill\Intl\Normalizer;

class Normalizer

olegabr avatar Dec 21 '20 14:12 olegabr

The following ClassmapReplacer.php will skip classnames that already begin with the prefix.

<?php
/**
 * The purpose of this file is to find and update classnames (and interfaces...) in their declarations.
 * Those replaced are recorded and their uses elsewhere are updated in a later step.
 */

namespace CoenJacobs\Mozart\Replace;

class ClassmapReplacer extends BaseReplacer
{

    /** @var string[] */
    public $replacedClasses = [];

    /** @var string */
    public $classmap_prefix;

    public function replace($contents)
    {

        return preg_replace_callback(
            "
	/						# Start the pattern
		namespace\s+[a-zA-Z0-9_\x7f-\xff\\\\]+[;{\s\n]{1}.*?(?=namespace|$) 
							# Look for a preceeding namespace declaration, up until 
							# a potential second namespace declaration
		|					# if found, match that much before repeating the search 
							# on the remainder of the string
		[^a-zA-Z0-9_\x7f-\xff]+			# After a non-class character,
		{$this->classmap_prefix}  		# match an already prefixed classname
		[a-zA-Z0-9_\x7f-\xff]+			# before continuing on the remainder of the string
		|
		(?:abstract\sclass|class|interface)\s+	# Look behind for class, abstract class, interface
		([a-zA-Z0-9_\x7f-\xff]+)		# Match the word until the first 
							# non-classname-valid character
		\s?					# Allow a space after
		(?:{|extends|implements|\n)		# Class declaration can be followed by {, extends, 
							# implements, or a new line
	/sx", // 					# dot matches newline, ignore whitespace in regex.
            function ($matches) use ($contents) {

                // If we're inside a namespace other than the global namesspace, just return.
                if (preg_match('/^namespace\s+[a-zA-Z0-9_\x7f-\xff\\\\]+[;{\s\n]{1}.*/', $matches[0])) {
                    return $matches[0];
                }

	            if (preg_match("/[^a-zA-Z0-9_\x7f-\xff]+{$this->classmap_prefix}[a-zA-Z0-9_\x7f-\xff]+/", $matches[0])) {
		            return $matches[0];
	            }

                // The prepended class name.
                $replace = $this->classmap_prefix . $matches[1];
                $this->saveReplacedClass($matches[1], $replace);
                return str_replace($matches[1], $replace, $matches[0]);
            },
            $contents
        );
    }

    public function saveReplacedClass($classname, $replacedName)
    {
        $this->replacedClasses[ $classname ] = $replacedName;
    }
}

It is PR #101 with this added:

[^a-zA-Z0-9_\x7f-\xff]+			# After a non-class character,
{$this->classmap_prefix}  		# match an already prefixed classname
[a-zA-Z0-9_\x7f-\xff]+			# before continuing on the remainder of the string
|

Updating Replacer.php:125 to :

'/(.*)([^a-zA-Z0-9_\x7f-\xff\\\\])'. $original . '([^a-zA-Z0-9_\x7f-\xff])/U',

fixes the extends incorrect replacement but needs a bit more thought and testing before making a PR.

PhpStorm was highlighting the namespace in Symphony/Polyfill/Intl/Normalizer/Normalizer.php with "undefined namespace Intl" and "undefined constant Normalizer". I changed composer.json's "dep_namespace": "\\Ethereumico\\Epg\\Dependencies\\", to "dep_namespace": "Ethereumico\\Epg\\Dependencies\\", and the message went away.

BrianHenryIE avatar Dec 24 '20 20:12 BrianHenryIE

That PR that was referred above is now released as part of Mozart 0.6.0. @olegabr can you confirm this is now resolved?

coenjacobs avatar Jan 17 '21 18:01 coenjacobs