【问题标题】:Limiting scope of included functions in PHP限制 PHP 中包含的函数的范围
【发布时间】:2023-04-04 04:16:01
【问题描述】:

我想在一个方法中包含/要求一个包含多个函数的文件。我的目标是使包含的函数仅在相应方法的范围内可用,而不在调用范围内。这在 PHP 中可行吗?

例如在下面的最小示例中,我希望 foo() 仅在方法 buzz() 内可用,而不是在周围范围内可用(不幸的是)。

// foo.php

<?php
function foo()
{
    return "foo";
}
// bar.php

<?php
class Bar 
{
    public function buzz()
    {
         require_once("foo.php");
         return foo();
    }
}

$bar = new Bar();

// should work
echo $bar->buzz();

// should not work
echo foo();

一般任务是将遗留代码库中的函数包装在 Symfony 服务的方法中。只能通过调用相应的方法来访问旧功能。因此,旧函数不应在全局范围内。

一个主要限制是foo.php 中的函数是我无法修改的遗留代码库的一部分。

【问题讨论】:

  • 也许您最好将其声明为特征 (php.net/manual/en/language.oop5.traits.php)。目前您正在尝试使用嵌套函数,这并不是特别 OO。
  • 也许namespaces 会有所帮助?
  • @NigelRen 这些函数来自我无法修改的遗留代码库。我的目标是将它们包装在新项目中的类方法中(直到它们最终被替换),只为它们提供一个 OO 接口。

标签: php symfony scope php-7.2


【解决方案1】:

我的目标是使包含的功能仅在 相应方法的范围,而不是在调用范围内。这是 可以在 PHP 中使用吗?

不直接。不是类方法的未命名空间的函数始终是全局范围的。如果您只是不希望它们在全局范围内,那么只需在命名空间中声明它们。但是,它们仍然可以从任何范围调用。

如果这还不够,还有一个(相当丑陋的)替代方案。由于函数中定义的变量仅对该函数可用,因此您可以通过使用分配给变量的匿名函数来近似您尝试执行的操作。这些将在函数结束时有效地消失。

foo.php

$function = function()
{
    return "foo";
};

bar.php

class Bar 
{
    public function buzz()
    {
        require_once 'foo.php';
        $function();
    }
}

【讨论】:

  • 谢谢!不幸的是,要求是不要触及文件foo.php(仍在扩展的遗留代码库),这可能不允许您提到的任何一种方法。
  • @SimonFromme 这可能是你的问题,因为它是任何解决方案的主要限制。
  • 如果你不能触摸foo.php,那么我认为你很不走运,因为require_once 会重置命名空间,并且函数将始终是全局的。
【解决方案2】:

如果您想获得创意,可以在需要之前重写文件以包含命名空间声明。这可能看起来像这样:

function wrapInNamespace(string $req): string
{
    // Namespaces have to start with a letter, unique IDs might not
    $tempNamespace = 'a' . uniqid();
    $namespaceDeclaration = sprintf("namespace %s;\r\n", $tempNamespace);

    $lines = file($req);

    array_splice($lines, 1, null, $namespaceDeclaration);

    $temp = tmpfile();
    fwrite($temp, implode('', $lines));
    require_once(stream_get_meta_data($temp)['uri']);
    fclose($temp);

    return $tempNamespace;
}

array_splice 行在文件的第二行添加了一个动态生成的命名空间声明,然后我们将其返回,以便您以后可以引用这些函数。这是通过临时文件完成的。

丑陋的部分是,当实际调用它们时,您需要分配一个命名空间标识符,因为据我所知,不可能在一行中调用动态命名空间中的函数(我会很高兴被告知不同):

class Bar
    public function buzz()
    {
        $tempNamespace = wrapInNamespace('foo.php');
        $func = "\\$tempNamespace\\foo";
        $func();
    }
}

所有放在一起,这意味着你可以这样做:

echo (new Bar)->buzz();
# foo

foo();
# PHP Fatal error:  Uncaught Error: Call to undefined function foo() ...

这都要求您的文件在第一行以&lt;?php 开头,并且它们不包含任何其他命名空间声明。使用同一个文件调用 wrapInNamespace 两次会得到两个完全不同的文件版本——只要你不从 buzz 方法中泄漏命名空间,它就不能从其他任何地方调用。这是否有用将取决于您所包含的源文件;如果它们只是其他全局函数的集合,那么这将很好地完成工作。如果它们还包含过程代码,那么它的用处就会降低。

【讨论】:

    猜你喜欢
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 2016-12-08
    • 2014-01-19
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 1970-01-01
    相关资源
    最近更新 更多