New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid usage of global functions #3645
Comments
All functions are prefixed with |
@fabpot I'm aware of the bc break it's more what could be rethinked when creating a new major version. The usecase as said is make it possible to scope twig away. So when using twig inside a phar application it does not conflict with the twig version used in the project. I'm currently using the box project and its php scoper but twig can currently not scoped away because of this global function requirements. |
@alexander-schranz there is an amazing PHP tool you can use to namespace prefix all packages of any individual project that may conflict with others (like when building PHARs). PHP Scoper does an awesome job and you can also automate the whole process using composer scripts and hooks. I have used it to build Wordpress plugins and even Google uses it to prefix namespaces for their plugins as you can see here. |
@clytras I'm coming here from using PHP Scoper 😄, linked the php scoper issue in the description. And as twig does not use namespaces for its function and declare function in the global namespace, it is not possible that php scoper can scope twig as twig requires that its function are in the global namespace. So it currently would not possible to scope twig away and application use another version of twig, then its global function conflicts. |
@alexander-schranz oh I misread your posts here. That's interesting. I'm using Twig with PHP Scoper currently with no issues, but the truth is that I haven't encoutered an other plugin or code that also uses Twig yet to tell if it conflicts or not. The only caviat I've noticed, is inside Lines 141 to 155 in 393ea10
where there are Twig namespace inside some strings for the generated cahced files and I'm currently having a composer
|
@clytras are you compiling the templates into cache files? What scoper config do you use? |
@alexander-schranz yes I'm using the cache for views even with <?php
namespace MyNS\Templates;
use MyNS\Vendor\Twig\{
Environment,
Loader\FilesystemLoader,
};
...
public static function __construct()
$this->loader = new FilesystemLoader();
$this->loader->addPath(TEMPLATES_DIR, 'default');
$this->loader->addPath(TEMPLATES_USER_DIR, 'user');
$this->twig = new Environment($this->loader, [
'cache' => TEMPLATES_CACHE_DIR,
'auto_reload' => true,
]);
} All the PHP Scoper auto-runs using composer scripts inside "scripts": {
"post-install-cmd": [
"@prefix-dependencies"
],
"post-update-cmd": [
"@prefix-dependencies"
],
"prefix-dependencies": [
"php-scoper add-prefix --output-dir ./vendor-patched --force --quiet",
"@composer dump-autoload --working-dir ./vendor-patched",
"sed -i -e 's/use Twig\\\\/use MyNS\\\\\\\\Vendor\\\\\\\\Twig\\\\/g' vendor-patched/vendor/twig/twig/src/Node/ModuleNode.php"
]
} and in return [
'prefix' => 'MyNS\\Vendor',
...
'exclude-namespaces' => [
'MyNS',
// 'Acme\Foo' // The Acme\Foo namespace (and sub-namespaces)
// '~^PHPUnit\\\\Framework$~', // The whole namespace PHPUnit\Framework (but not sub-namespaces)
// '~^$~', // The root namespace only
// '', // Any namespace
],
...
]; |
I'm additional loading allfiles to create the cache files in an init of my application like its done in symfony cache warmup: $templateIterator = new \DirectoryIterator(dirname(__DIR__) . '/templates');
foreach ($templateIterator as $file) {
if (str_ends_with($file->getFilename(), '.html.twig')) {
$twig->load($file->getFilename());
}
} But run into problems with the built in filters when used then in the run from the cached files. |
@clytras Do you have: |
@alexander-schranz no I don't have |
@clytras interesting how does the scoped version of this lines look for you: Twig/src/Extension/CoreExtension.php Lines 305 to 314 in 237f178
You should configure a |
@alexander-schranz Everything gets the namespace prefix inside namespace MyNS\Vendor\Twig\Extension;
use MyNS\Vendor\Twig\ExpressionParser;
use MyNS\Vendor\Twig\Node\Expression\Binary\AddBinary;
use MyNS\Vendor\Twig\Node\Expression\Binary\AndBinary;
use MyNS\Vendor\Twig\Node\Expression\Binary\BitwiseAndBinary;
use MyNS\Vendor\Twig\Node\Expression\Binary\BitwiseOrBinary;
use MyNS\Vendor\Twig\Node\Expression\Binary\BitwiseXorBinary;
...
namespace MyNS\Vendor;
use MyNS\Vendor\Twig\Environment;
use MyNS\Vendor\Twig\Error\LoaderError;
use MyNS\Vendor\Twig\Error\RuntimeError;
use MyNS\Vendor\Twig\Extension\CoreExtension;
use MyNS\Vendor\Twig\Extension\SandboxExtension;
use MyNS\Vendor\Twig\Markup;
use MyNS\Vendor\Twig\Source;
use MyNS\Vendor\Twig\Template;
use MyNS\Vendor\Twig\TemplateWrapper; and inside if (!function_exists('twig_template_from_string')) {
function twig_template_from_string() {
return \MyNS\Vendor\twig_template_from_string(...func_get_args());
}
}
if (!function_exists('twig_var_dump')) {
function twig_var_dump() {
return \MyNS\Vendor\twig_var_dump(...func_get_args());
}
}
if (!function_exists('twig_cycle')) {
function twig_cycle() {
return \MyNS\Vendor\twig_cycle(...func_get_args());
}
} I know about the patcher. I just got the sed approach from another repo and I was too lazy to replace that with |
That looks unexpected for me, this normally should only be done by the scoper if a configured to |
That's a very clean environment. A stock Wordpress Docker container having just the plugin I'm developing and stock plugins, but again, this file is inside the Here is my entire <?php
declare(strict_types=1);
use Isolated\Symfony\Component\Finder\Finder;
// You can do your own things here, e.g. collecting symbols to expose dynamically
// or files to exclude.
// However beware that this file is executed by PHP-Scoper, hence if you are using
// the PHAR it will be loaded by the PHAR. So it is highly recommended to avoid
// to auto-load any code here: it can result in a conflict or even corrupt
// the PHP-Scoper analysis.
return [
// The prefix configuration. If a non null value is be used, a random prefix
// will be generated instead.
//
// For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#prefix
'prefix' => 'MyNS\\Vendor',
// By default when running php-scoper add-prefix, it will prefix all relevant code found in the current working
// directory. You can however define which files should be scoped by defining a collection of Finders in the
// following configuration key.
//
// This configuration entry is completely ignored when using Box.
//
// For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#finders-and-paths
'finders' => [
// Finder::create()->files()->in('lib'),
Finder::create()
->files()
->ignoreVCS(true)
->notName('/LICENSE|.*\\.md|.*\\.dist|Makefile|composer\\.json|composer\\.lock/')
->exclude([
'doc',
'test',
'test_old',
'tests',
'Tests',
'vendor-bin',
])
->in('vendor'),
Finder::create()->append([
'composer.json',
]),
],
// List of excluded files, i.e. files for which the content will be left untouched.
// Paths are relative to the configuration file unless if they are already absolute
//
// For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#patchers
'exclude-files' => [
'src/a-whitelisted-file.php',
],
// When scoping PHP files, there will be scenarios where some of the code being scoped indirectly references the
// original namespace. These will include, for example, strings or string manipulations. PHP-Scoper has limited
// support for prefixing such strings. To circumvent that, you can define patchers to manipulate the file to your
// heart contents.
//
// For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#patchers
'patchers' => [
static function (string $filePath, string $prefix, string $contents): string {
// Change the contents here.
if (preg_match('/scssphp\/src\/.*?\\.php/', $filePath)) {
// For comment definitions
return preg_replace(
'/( \\\\?)(ScssPhp\\\\)/',
"$1{$prefix}\\\\$2",
$contents
);
}
return $contents;
},
],
// List of symbols to consider internal i.e. to leave untouched.
//
// For more information see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#excluded-symbols
'exclude-namespaces' => [
'MyNS',
// 'Acme\Foo' // The Acme\Foo namespace (and sub-namespaces)
// '~^PHPUnit\\\\Framework$~', // The whole namespace PHPUnit\Framework (but not sub-namespaces)
// '~^$~', // The root namespace only
// '', // Any namespace
],
'exclude-classes' => [
// 'ReflectionClassConstant',
],
'exclude-functions' => [
// 'mb_str_split',
],
'exclude-constants' => [
// 'STDIN',
],
// List of symbols to expose.
//
// For more information see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#exposed-symbols
'expose-global-constants' => true,
'expose-global-classes' => true,
'expose-global-functions' => true,
'expose-namespaces' => [
// 'Acme\Foo' // The Acme\Foo namespace (and sub-namespaces)
// '~^PHPUnit\\\\Framework$~', // The whole namespace PHPUnit\Framework (but not sub-namespaces)
// '~^$~', // The root namespace only
// '', // Any namespace
],
'expose-classes' => [],
'expose-functions' => [],
'expose-constants' => [],
]; and And of course there are some sed commads runnning on composer "prefix-dependencies": [
"php-scoper add-prefix --output-dir ./vendor-patched --force --quiet",
"@composer dump-autoload --working-dir ./vendor-patched",
"sed -i -e 's/use Twig\\\\/use MyNS\\\\\\\\Vendor\\\\\\\\Twig\\\\/g' vendor-patched/vendor/twig/twig/src/Node/ModuleNode.php"
] |
Thx for sharing. I see, you are exposing global things via In the generated templates the Really thank you for your insights, atleast we know at the end we need to expose the global function to get twig work, aslong as twig using global functions. |
@alexander-schranz you're welcome. I can now see your point and I agree with you, indeed there might be issues with these Twig global function when something else has included an other version of Twig. Though with some deeper thinking, I see that will be the case with all packages that expose global helper functions, not only with Twig, it's just that Twig is very popular and it's most likely for a conflict to occur. I believe a solution from the PHP Scoper side dealing with all these kind of global functions would be more suitable to handle everything, because today is Twig but tomorrow there will be something else having the same issue. |
@clytras definitely. But think it is today as I know very uncommon that a library is defining global function (Update: seems to have ignore Laravel completely here 🙈 ). I think php scoper is doing a good work currently as it scope global function into an own namespace, so it will not effect the application. This normally works great. But twig is here special case as it generates php files which the scoper can not take into its account. So it is a rare case only for libraries which generates php files which the php scoper can not take into account to scope global functions. |
Because those global functions are not namespaced, I'd be happy with an However, because it's only for the benefit of static analysis, I can work around this by adding the file explicitly to my own |
Hi, I have the same problem with our WordPress plug-in. We use Strauss instead of PHP Scoper so that the Twig classes are packed into their own namespaces. I could provide a simple patch for namespace {
use <prefixed or vanilla namespaces...>;
// ...
if (!function_exists(__NAMESPACE__ . '\twig_cycle')) {
function twig_cycle($values, $position)
{
// ...
}
}
if (!function_exists(__NAMESPACE__ . '\twig_random')) {
function twig_random(Environment $env, $values = null, $max = null)
{
// ...
}
}
// ...
} // namespace What do you guys think? |
OK, that proposal does not work. I missed that the function arguments are type-hinted. In my case, I've written a simple patching utility which prepends each of |
Here is a workaround for Scoper for anyone who might be interested. @fabpot Thank you for the fantastic Twig! It would be great if Twig v.4 wouldn't use global things, so it doesn't cause any issues while scoping. |
Closing as functions are gone in Twig 4. |
While I tried to use Twig inside scoped
.phar
I found out that twig registers function in the global namespace like here in the core extension:Twig/src/Extension/CoreExtension.php
Line 305 in 237f178
I think it would be great when also the twig functions live in a namespace this would make it possible to scope twig and avoid conflicts in global namespace and errors when scoping twig.
The text was updated successfully, but these errors were encountered: