Ограничение объема включенных функций в PHP

Я хочу включить/требовать файл, содержащий несколько функций из метода. Моя цель - сделать так включенные функции доступными только в области действия соответствующего метода, а не в области вызова. Возможно ли это в 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 являются частью устаревшей кодовой базы, которую я не могу изменить.


person Simon Fromme    schedule 25.11.2019    source источник
comment
Возможно, вам было бы лучше объявить это чертой (php.net/ manual/en/language.oop5.traits.php). На данный момент вы пытаетесь использовать вложенные функции, что не совсем ООП.   -  person Nigel Ren    schedule 25.11.2019
comment
Может быть, вам помогут пространства имен?   -  person A1rPun    schedule 25.11.2019
comment
@NigelRen Функции взяты из устаревшей кодовой базы, которую я не могу изменить. Моя цель состоит в том, чтобы обернуть их в методы класса в новом проекте (до тех пор, пока они в конечном итоге не будут заменены), чтобы предоставить им только объектно-ориентированный интерфейс.   -  person Simon Fromme    schedule 25.11.2019


Ответы (2)


Моя цель - сделать так включенные функции доступными только в области действия соответствующего метода, а не в области вызова. Возможно ли это в PHP?

Не напрямую. Функции без пространства имен, которые не являются методами класса, всегда имеют глобальную область видимости. Если вы просто не хотите, чтобы они были в глобальной области видимости, просто объявите их в пространстве имен. Однако они по-прежнему будут вызываться из любой области.

Если этого недостаточно, есть (довольно уродливая) альтернатива. Поскольку переменные, определенные в функции, доступны только для этой функции, вы можете аппроксимировать то, что пытаетесь сделать, используя анонимные функции, назначенные переменной. Они эффективно исчезнут в конце функции.

foo.php

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

bar.php

class Bar 
{
    public function buzz()
    {
        require_once 'foo.php';
        $function();
    }
}
person Alex Howansky    schedule 25.11.2019
comment
Спасибо! К сожалению, требование состоит в том, чтобы не трогать файл foo.php (устаревшая кодовая база, которая все еще расширяется), что, вероятно, не позволит использовать ни один из упомянутых вами методов. - person Simon Fromme; 25.11.2019
comment
@SimonFromme Вероятно, это должно быть в вашем вопросе, поскольку это серьезное ограничение для любого решения. - person Nigel Ren; 25.11.2019
comment
Если вы не можете коснуться foo.php, то я думаю, вам не повезло, так как require_once сбрасывает пространство имен, и функция всегда будет глобальной. - person Alex Howansky; 25.11.2019

Если вы хотите проявить творческий подход, вы можете переписать файл, включив в него объявление пространства имен до того, как оно вам потребуется. Это может выглядеть примерно так:

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() ...

Все это требует, чтобы ваши файлы начинались с <?php в первой строке и не содержали никаких других объявлений пространств имен. Вызов wrapInNamespace дважды с одним и тем же файлом даст вам две совершенно разные версии файла - пока вы не утечете пространство имен из своего метода buzz, его нельзя будет вызвать из другого места. Полезно это или нет, будет зависеть от того, какие именно исходные файлы вы включаете; если они представляют собой просто набор глобальных функций, это прекрасно справится с этой задачей. Если они также содержат процедурный код, это будет менее полезно.

person iainn    schedule 26.11.2019