Я работаю над личным проектом, создавая некоторые базовые функциональные возможности для работы с PHP-компонентами, чтобы их можно было создавать аналогично компонентам в React. Я столкнулся с каким-то странным поведением, когда если я рендерил компонент внутри строки, используя строковую интерполяцию, он появляется перед тем местом, в котором он вызывается в коде. Я могу заставить разметку отображать правильно, выполняя каждый шаг в виде отдельной строки кода, но я хотел бы иметь возможность включать эти компоненты в строки. Почему этот рендеринг может происходить не по порядку? Любая идея будет принята с благодарностью.
Я пытался вернуть включенные компоненты вместо их эхо, но (возможно, из-за использовавшихся двойных кавычек?) Они вообще не рендерится при этом. Я также объединил компоненты вместе в виде строк. Это не работает. Если я пишу отдельные строки кода и заканчиваю их точкой с запятой после каждого, я получаю ожидаемый результат.
Вот что я хотел бы иметь:
echo "
<section class='videoInfo'>
<h1>{$props[$video]->getProperty(VIDEO_PROP::TITLE)}</h1>
<div class='secondary'>
<span class='viewCount'>{$props[$video]->getProperty(VIDEO_PROP::VIEWS)} views</span>
{$loader::render(COMPONENT::OPINION_CONTROLS)}
{$loader::render(COMPONENT::VIDEO_DETAILS)}
</div>
</section>
";
Я также попробовал это, что приводит к тому же искаженному выводу, что и выше:
echo "
<section class='videoInfo'>
<h1>{$props[$video]->getProperty(VIDEO_PROP::TITLE)}</h1>
<div class='secondary'>
<span class='viewCount'>{$props[$video]->getProperty(VIDEO_PROP::VIEWS)} views</span>" .
$loader::render(COMPONENT::OPINION_CONTROLS) .
$loader::render(COMPONENT::VIDEO_DETAILS) .
"</div>
</section>
";
Это работает правильно, но более громоздко написать:
echo "
<section class='videoInfo'>
<h1>{$props[$video]->getProperty(VIDEO_PROP::TITLE)}</h1>
<div class='secondary'>
<span class='viewCount'>{$props[$video]->getProperty(VIDEO_PROP::VIEWS)} views</span>";
$loader::render(COMPONENT::OPINION_CONTROLS);
$loader::render(COMPONENT::VIDEO_DETAILS);
echo "</div>
</section>
";
Я ожидаю, что выходной HTML будет отражать отраженную строку. Таким образом, окончательный результат должен выглядеть следующим образом:
<section class="videoInfo">
<h1>Second Test</h1>
<div class="secondary">
<span class="viewCount">86 views</span>
<!-- includes javascript for handling like/dislike buttons -->
<script src="assets/js/opinions.js"></script>
<div class="controls">
<button class="opinion far fa-thumbs-up" onclick="like(this, 60)">
<span class="text">0</span>
</button>
<button class="opinion far fa-thumbs-down" onclick="dislike(this, 60)">
<span class="text">0</span>
</button>
</div>
<!-- includes javascript for handling edit/subscribe buttons -->
<script src="assets/js/videoInteractions.js"></script>
<section class="details">
<nav>
<a href="profile.php?username=testman">
<img src="assets/images/generic_profile.png" class="profilePic">
</a>
</nav>
<div class="uploadInfo">
<span class="uploader">
<a href="profile.php?username=testman">
testman
</a>
</span>
<span class="uploadDate">
Published on Jun 4, 2019
</span>
</div>
<button class="subscribe " onclick="subscribe("chan123", "testman", this)">
<span class="text">Subscribe 0</span>
</button>
<section class="description">
Testing view incrementing
</section>
</section>
</div>
</section>
Это действительно вывод, который я получаю, если выполняю каждый шаг в виде отдельной строки кода. Однако, если я использую интерполяцию или конкатенацию строк, компоненты загружаются не по порядку. Фактически загруженные компоненты появляются перед разделом videoInfo!
<button class="opinion far fa-thumbs-up" onclick="like(this, 60)">
<span class="text">0</span>
</button>
<button class="opinion far fa-thumbs-down" onclick="dislike(this, 60)">
<span class="text">0</span>
</button>
<nav>
<a href="profile.php?username=testman">
<img src="assets/images/generic_profile.png" class="profilePic">
</a>
</nav>
<div class="uploadInfo">
<span class="uploader">
<a href="profile.php?username=testman">
testman
</a>
</span>
<span class="uploadDate">
Published on Jun 4, 2019
</span>
</div>
<button class="subscribe " onclick="subscribe("chan123", "testman", this)">
<span class="text">Subscribe 0</span>
</button>
<section class="description">
Testing view incrementing
</section>
<h1>Second Test</h1>
<div class="secondary">
<span class="viewCount">87 views</span>
</div>
Компоненты вывода сами по себе отражают строки. Они выглядят так:
(opinionControls)
echo "
<!-- includes javascript for handling like/dislike buttons -->
<script src='assets/js/opinions.js'></script>
<div class='controls'>
$likeButton
$dislikeButton
</div>
";
(videoDetails)
echo "
<!-- includes javascript for handling edit/subscribe buttons -->
<script src='assets/js/videoInteractions.js'></script>
<section class='details'>
<nav>
$profileButton
</nav>
<div class='uploadInfo'>
<span class='uploader'>
<a href='profile.php?username=$uploader'>
$uploader
</a>
</span>
<span class='uploadDate'>
Published on $formattedDate
</span>
</div>
$interactButton
<section class='description'>
{$props[$video]->getProperty(VIDEO_PROP::DESCRIPTION)}
</section>
</section>
";
Вложенные эхо как-то всплывают из эха верхнего уровня? Я пытаюсь обернуть голову вокруг этого поведения.
Я пытался отрегулировать вложенные компоненты, заставляя их возвращать строки вместо эха, но мне не повезло заставить их визуализироваться таким образом. Я пробовал одинарные кавычки и двойные кавычки вокруг возвращаемых строк. Вот так:
return '
<!-- includes javascript for handling like/dislike buttons -->
<script src=\'assets/js/opinions.js\'></script>
<div class=\'controls\'>
$likeButton
$dislikeButton
</div>
';
и
return "
<section class='videoInfo'>
<h1>{$props[$video]->getProperty(VIDEO_PROP::TITLE)}</h1>
<div class='secondary'>
<span class='viewCount'>{$props[$video]->getProperty(VIDEO_PROP::VIEWS)} views</span>
{$loader::render(COMPONENT::OPINION_CONTROLS)}
{$loader::render(COMPONENT::VIDEO_DETAILS)}
</div>
</section>
";
Функция $ loader :: render () довольно проста. Он вызывает функцию, которая вызывает include () для компонента, который был загружен немного раньше в приложении. Соответствующие функции ComponentLoader следующие:
// Load component into static primedComponents array as a key, with its value its props
public static function primeComponentWithProps(string $includesPath, array $props)
{
self::$primedComponents[$includesPath] = $props;
}
// Extract component and props from static array, then include it on the page
public static function render(string $includesPath)
{
$props = self::$primedComponents[$includesPath];
$args = [$includesPath, $props];
self::loadComponentWithProps(...$args);
// remove the component from the array of primed components once it has been rendered
unset(self::$primedComponents[$includesPath]);
}
/** used to import components that require props */
public static function loadComponentWithProps(string $includesPath, array $props)
{
// extract($props); // using extract() would allow props to be accessed directly by key names--but currently we are using a props array and accessing values by key to make it clearer what is going on in components which consume props
include($includesPath);
}