В настоящее время я выполняю рефакторинг кода из функции парсера страниц в ООП.
У меня возникают трудности при включении и запуске кода из файла в область основных функций:
Объект:
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?
Вы можете создать 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;
}
}
Я надеюсь, что это поможет вам достичь вашей цели.
Удачи.
Если я правильно понимаю вашу проблему, то вы хотите выполнить весь код из файла 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);
}
}
Похоже, проблема внедрения зависимости: вы хотите $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, затем передать объект, затем, наконец, если ни один из них не работает для вас, а затем использовать глобальную переменную.