Как запустить код из включенного пути к файлу объекта в область действия функции вне объекта?

В настоящее время я выполняю рефакторинг кода из функции парсера страниц в ООП.
У меня возникают трудности при включении и запуске кода из файла в область основных функций:

Объект:

class phpFragment {
private $sData;

function render() {
return include $oElement->sData;
}
}

Класс контейнера объекта:

class pageData {
protected $aPhpFragments;
protected $aCssFragments;

public function outputData($sTag) {
switch($sTag) {
case 'php':
foreach($this->aPhpFragments as $oPhpFragment) {
return $oPhpFragment->render();
}
break;
case 'css':
foreach($this->aCssFragments as $oCssFragment) {
echo $oCssFragment->render();
}
break;
}
}
}

Основная функция:

function parsePage($sLanguageCode) {
$oTranslator = new translator($sLanguageCode);
$aTranslations = $oTranslator->translations('page');
$oBuilderClass = new builder($aTranslations);

//... queries to get data and set pagedata and get the template file
$oPageData = $oPage->getData();
$aTemplateTags = $oTemplate->getTags();
foreach($aTemplateTags as $sTag) {
$oPageData->outputData($sTag);
}

//....
}

Код включения (пример):

<?php

$oBuilderClass->build_element(.... parameters here);

?>

Я хочу инициировать класс Builder только один раз, потому что он содержит довольно много данных, и я не хочу воссоздавать это при каждом включении.

Как я могу вернуть код включения в функцию parsePage, где можно использовать builderClass?

2

Решение

Вы можете создать Context класс, который будет контейнером ваших переменных области видимости и поможет вам включить (выполнить) код в контекст. Это будет одноэлементный класс (будет создан только один экземпляр).

Вот как это использовать: метод current() возвращает текущий экземпляр, затем вы можете экспортировать переменные в контекст, используя export() метод, он принимает массив ключ / значение. Метод execute() принимает имя файла в качестве параметра и включает его в список доступных экспортируемых переменных, вы можете добавить временные переменные в качестве второго параметра:

//Somewhere before execute();
oContext::current()->export([
'variable1' => 'value1',
'instance' => $instance
]);

//Then anywhere in your file:
oContext::current()->execute("toBeIncluded.php", [
'tmp_variable' => 'tmp_value'
]);

//toBeIncluded.php
echo $variable1;
echo $instance->method1();
echo $tmp_variable;

В твоем случае:

Основная функция:

function parsePage($sLanguageCode) {
$oTranslator = new translator($sLanguageCode);
$aTranslations = $oTranslator->translations('page');
$oBuilderClass = new builder($aTranslations);

//export variables to your context
//Don't be aware of memroy usage objects are passed by reference
oContext::current()->export(compact('oBuilderClass'));

//... queries to get data and set pagedata and get the template file
$oPageData = $oPage->getData();
$aTemplateTags = $oTemplate->getTags();
foreach($aTemplateTags as $sTag) {
$oPageData->outputData($sTag);
}

//....
}

Объект:

class phpFragment {
private $sData;

function render() {
oContext::current()->execute($oElement->sData);
}
}

Ниже вы найдете объявление класса:

oContext.class.php

/**
* Class oContext
*/
class oContext {

/**
* The singleton instance
* @var oContext
*/
private static $instance = null;

/**
* the exported variables
* @var array
*/
private $variables = [];

/**
* Return the singleton or create one if does not exist
*
* @return oContext
*/
public static function current() {
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}

/**
* Export an array of key/value variables
*
* @param $variables
* @return $this
*/
public function export($variables) {
foreach ($variables as $key => $value) {
$this->variables[$key] = $value;
}
return $this;
}

/**
* Include and execute a file in this context
*
* @param $file
* @param array $variables temporary exports will not be added to the context (not available in the next call)
* @return $this
*/
public function execute($file, $variables = []) {
//Populate variables
foreach (array_merge($this->variables, $variables) as $key => $value) {
${$key} = $value;
}
include $file;
return $this;
}

}

Я надеюсь, что это поможет вам достичь вашей цели.

Удачи.

3

Другие решения

Если я правильно понимаю вашу проблему, то вы хотите выполнить весь код из файла php как метод, вызываемый из объекта. Если да, то вы, вероятно, хотите использовать eval описанная функция Вот.

С eval Функция, вы можете прочитать ваш php-файл как строку и оценить его как php-код, а не включать его.

Если ваш php файл использует оператор return, то следуйте документации

eval () возвращает NULL, если return не вызывается в вычисленном коде, в
в этом случае возвращается значение, переданное для возврата.

Вы можете просто вернуть это значение из вашего метода.

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

class phpFragment {
private $sData;

function render() {
return include $oElement->sData;
}
}

с этим

class phpFragment {
private $sData;

function render() {
//read a file into variable as string
$phpCode = file_get_contents($oElement->sData);

//prepare code by adding return statement and '?>' at the begining (because you have an open tag in php files).
$phpCode = '?> ' . str_replace('$oBuilderClass->build_element', 'return $oBuilderClass->build_element', $phpCode);

//I guess that included files don't use any variables declared out of file so we need to simply escape every '$' character in file
//that they can evaluate correctly.
$phpCode = str_replace('$', '\$', $phpCode);

return eval($phpCode);
}
}
0

Похоже, проблема внедрения зависимости: вы хотите $oBuilderClass быть в поле зрения внутри include код?

Если у вас есть доступ к контейнеру зависимостей приложения, я бы зарегистрировал объект в этом контейнере. В общих чертах, что-то вроде \Application::bind('Builder', $oBuilderClass)потом сделаем Builder::build_element, Однако то, что вы пишете свой собственный рендер, предполагает, что у вас нет доступа к инфраструктуре с формальным Контейнер IoC.

Предположим, что у вас нет контейнера IoC, наиболее целесообразно сделать следующее:

$GLOBALS['oBuilderClass'] = new builder(...);

потом в вашем числе:

global $oBuilderClass;
$oBuilderClass->build_element(...);

Это не особенно элегантно, однако. Вы можете обойти строителя так, чтобы в нижней части звонка вы имели:

function render(builder $oBuilderClass) {
return include $oElement->sData;
}

который ставит $oBuilderClass по объему на момент включения. Я бы предпочел сначала формальный контейнер IoC, затем передать объект, затем, наконец, если ни один из них не работает для вас, а затем использовать глобальную переменную.

0
По вопросам рекламы [email protected]