Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions src/Phaseolies/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,38 +108,34 @@ class Controller extends View
*/
protected const MAX_COMPILE_RETRIES = 3;

/**
* Counter for tracking solo block uniqueness
*
* @var int
*/
protected int $soloCounter = 0;

/**
* Tracks which solo blocks have already rendered
*
* @var array
*/
protected array $soloStack = [];

/**
* Constructor to initialize the template engine with default settings
*/
public function __construct()
{
parent::__construct();

// Set the file extension for template files (changed to .odo.php)
$this->setFileExtension('.odo.php');

// Set the directory where view files are stored
$this->setViewFolder('resources/views' . DIRECTORY_SEPARATOR);

// Set the directory where cached files are stored
$this->setCacheFolder('storage/framework/views' . DIRECTORY_SEPARATOR);

// Create the cache folder if it doesn't exist
$this->createCacheFolder();

// Set the directory where cached files are stored
$this->setSymlinkPathFolder('storage/app/public' . DIRECTORY_SEPARATOR);

// Create the cache folder if it doesn't exist
$this->createPublicSymlinkFolder();

// Set the format for echoing variables in templates
$this->setEchoFormat('$this->e(%s)');

// Initialize arrays for blocks, block stacks, and loop stacks
$this->loopStacks = [];

// Load custom syntax from config if available
$this->loadCustomSyntax();
}

Expand Down
107 changes: 107 additions & 0 deletions src/Phaseolies/Support/Odo/OdoCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -541,4 +541,111 @@ protected function compileEndscopenot(): string
{
return "<?php endif; ?>";
}

/**
* Usage: #blank($variable)
* Renders block when variable is empty, null, or an empty array/string
*
* @param mixed $variable
* @return string
*/
protected function compileBlank($variable): string
{
return "<?php if(empty{$variable}): ?>";
}

/**
* Usage: #endblank
*
* @return string
*/
protected function compileEndblank(): string
{
return "<?php endif; ?>";
}

/**
* Usage: #notblank($variable)
* Renders block when variable is NOT empty
*
* @param mixed $variable
* @return string
*/
protected function compileNotblank($variable): string
{
return "<?php if(!empty{$variable}): ?>";
}

/**
* Usage: #endnotblank
*
* @return string
*/
protected function compileEndnotblank(): string
{
return "<?php endif; ?>";
}

/**
* Usage: #solo
* Renders its content only once, even if inside a loop
*
* @return string
*/
protected function compileSolo(): string
{
return "<?php if(!isset(\$__solo['" . ($this->soloCounter ?? $this->soloCounter = 0) . "'])): \$__solo['" . $this->soloCounter++ . "'] = true; ?>";
}

/**
* Usage: #endsolo
*
* @return string
*/
protected function compileEndsolo(): string
{
return "<?php endif; ?>";
}

/**
* Usage: #inject('stack-name')
* Pushes content into a named stack
*
* @param string $expression
* @return string
*/
protected function compileInject($expression): string
{
if (isset($expression[0]) && '(' === $expression[0]) {
$expression = substr($expression, 1, -1);
}

return "<?php \$this->startInject({$expression}); ?>";
}

/**
* Usage: #endinject
*
* @return string
*/
protected function compileEndinject(): string
{
return "<?php \$this->endInject(); ?>";
}

/**
* Usage: #slot('stack-name')
* Outputs all content pushed into a named stack
*
* @param string $expression
* @return string
*/
protected function compileSlot($expression): string
{
if (isset($expression[0]) && '(' === $expression[0]) {
$expression = substr($expression, 1, -1);
}

return "<?php echo \$this->renderSlot({$expression}); ?>";
}
}
85 changes: 84 additions & 1 deletion src/Phaseolies/Support/View/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,25 @@ class View extends Factory

/**
* Stack of currently rendering view names.
* Useful for debugging and internal state tracking.
*
* @var array<int, string>
*/
protected $renderStack = [];

/**
* Named stacks for inject/slot system
*
* @var array<string, array>
*/
protected array $stacks = [];

/**
* Currently open inject stack name
*
* @var string|null
*/
protected ?string $currentInject = null;

public function __construct()
{
$this->factory = app('view');
Expand Down Expand Up @@ -262,6 +275,74 @@ private function isAssoc(array $array): bool
return array_keys($array) !== range(0, count($array) - 1);
}

/**
* Start capturing content for a named inject stack
*
* @param string $name
* @return void
*/
public function startInject(string $name): void
{
if (empty($name)) {
throw new \InvalidArgumentException('Inject stack name must not be empty');
}

$this->currentInject = trim($name, "'\"");
ob_start();
}

/**
* End capturing and push content into the named stack
*
* @return void
*/
public function endInject(): void
{
if ($this->currentInject === null) {
throw new \RuntimeException('Cannot end inject — no inject block is open');
}

$content = ob_get_clean();
$name = $this->currentInject;

if (!isset($this->stacks[$name])) {
$this->stacks[$name] = [];
}

$this->stacks[$name][] = $content;
$this->currentInject = null;
}

/**
* Render all content pushed into a named stack
*
* @param string $name
* @return string
*/
public function renderSlot(string $name): string
{
$name = trim($name, "'\"");

if (!isset($this->stacks[$name])) {
return '';
}

return implode('', $this->stacks[$name]);
}

/**
* Check if a stack has any content
*
* @param string $name
* @return bool
*/
public function hasSlot(string $name): bool
{
$name = trim($name, "'\"");

return !empty($this->stacks[$name]);
}

/**
* Clean the block state
*
Expand All @@ -272,6 +353,8 @@ public function flush()
$this->blocks = [];
$this->blockStacks = [];
$this->parents = [];
$this->stacks = [];
$this->currentInject = null;
}

public function __destruct()
Expand Down
Loading