PHP code example of sugarcraft / candy-core

1. Go to this page and download the library: Download sugarcraft/candy-core library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

sugarcraft / candy-core example snippets


use SugarCraft\Core\{Cmd, KeyType, Model, Msg, Program};
use SugarCraft\Core\Msg\{KeyMsg, WindowSizeMsg};

final class Counter implements Model
{
    public function __construct(public readonly int $count = 0) {}
    public function init(): ?\Closure { return null; }

    public function update(Msg $msg): array
    {
        if ($msg instanceof KeyMsg) {
            return match (true) {
                $msg->type === KeyType::Char && $msg->rune === 'q' => [$this, Cmd::quit()],
                $msg->type === KeyType::Up    => [new self($this->count + 1), null],
                $msg->type === KeyType::Down  => [new self($this->count - 1), null],
                default => [$this, null],
            };
        }
        return [$this, null];
    }

    public function view(): string { return "count: $this->count\n(↑/↓ to change, q to quit)"; }
}

(new Program(new Counter()))->run();

use SugarCraft\Core\{Kind, Model, Msg, Program, Subscriptions};
use SugarCraft\Core\Cmd\SubscribeCmd;

final class Clock implements Model
{
    public function __construct(public readonly int $ticks = 0) {}

    public function init(): ?\Closure { return null; }

    public function update(Msg $msg): array
    {
        // tick handling...
        return [$this, null];
    }

    public function view(): string { return "ticks: $this->ticks\n"; }

    public function subscriptions(): ?Subscriptions
    {
        return (new Subscriptions())->withTick('clock-tick', 1.0, fn () => new TickMsg());
    }
}

use SugarCraft\Core\{Model, SubscriptionCapable};

final class StaticModel implements Model
{
    use SubscriptionCapable;
    // ... no subscriptions() needed
}

use SugarCraft\Core\{Cmd, KeyType, Model, Msg, Program, RootModelWithScreenStack, Screen, ScreenStack};
use SugarCraft\Core\Cmd\{PushScreenCmd, PopScreenCmd};
use SugarCraft\Core\Msg\{KeyMsg, ScreenStackPushedMsg, ScreenStackPoppedMsg};

final class DetailScreen implements Model
{
    public function __construct(public readonly string $id) {}
    public function init(): ?\Closure { return null; }
    public function update(Msg $msg): array
    {
        if ($msg instanceof KeyMsg && $msg->rune === 'b') {
            return [$this, Cmd::pop()];
        }
        return [$this, null];
    }
    public function view(): string { return "Detail: {$this->id}\n(press b to go back)\n"; }
}

// Root model owns the stack and handles infrastructure messages.
final class App implements Model, \SugarCraft\Core\ScreenStackCapable
{
    use \SugarCraft\Core\SubscriptionCapable;
    public function __construct(public ScreenStack $screens = new ScreenStack()) {}
    public function screens(): ScreenStack { return $this->screens; }
    public function init(): ?\Closure { return null; }

    public function update(Msg $msg): array
    {
        if ($msg instanceof ScreenStackPushedMsg) {
            return [new self($this->screens->push($msg->screen)), null];
        }
        if ($msg instanceof ScreenStackPoppedMsg) {
            if ($this->screens->isEmpty()) return [$this, null];
            $popped = $this->screens->current();
            return [new self($this->screens->pop()), null];
        }
        if ($msg instanceof KeyMsg && $msg->rune === 'n') {
            return [$this, new PushScreenCmd(new Screen(new DetailScreen('item-1'), title: 'Item 1'))];
        }
        return [$this, null];
    }

    public function view(): string
    {
        $active = $this->screens->isEmpty()
            ? new \SugarCraft\Core\Model\Anonymous(fn() => "Push a screen with n\n")
            : $this->screens->current()->model;
        return $active->view();
    }
}

(new Program(new App()))->run();

use SugarCraft\Core\I18n\T;

T::setLocale(T::detect());                 // 'en' / 'fr' / 'de' from $LANG
echo T::t('core.color.invalid_hex', ['hex' => '#zz']);
// => "invalid hex color: #zz"

use SugarCraft\Core\Lang;

throw new \InvalidArgumentException(
    Lang::t('color.invalid_hex', ['hex' => $hex])
);

T::overrideNamespace('charts', '/etc/myapp/lang/charts');

use SugarCraft\Core\{Cmd, KeyType, Model, Msg, Program};
use SugarCraft\Core\Msg\KeyMsg;

final class ShoppingList implements Model
{
    /** @param list<string> $items @param array<int,bool> $bought */
    public function __construct(
        public readonly array $items,
        public readonly array $bought = [],
        public readonly int $cursor = 0,
    ) {}

    // 1. init() runs once at startup. Return a Cmd or null.
    public function init(): ?\Closure { return null; }

    // 2. update() takes a Msg and returns [newModel, ?Cmd].
    public function update(Msg $msg): array
    {
        if (!$msg instanceof KeyMsg) {
            return [$this, null];
        }
        return match (true) {
            $msg->type === KeyType::Char && $msg->rune === 'q'
                => [$this, Cmd::quit()],
            $msg->type === KeyType::Up
                => [new self($this->items, $this->bought, max(0, $this->cursor - 1)), null],
            $msg->type === KeyType::Down
                => [new self($this->items, $this->bought, min(count($this->items) - 1, $this->cursor + 1)), null],
            $msg->type === KeyType::Space => [
                new self(
                    $this->items,
                    [...$this->bought, $this->cursor => !($this->bought[$this->cursor] ?? false)],
                    $this->cursor,
                ),
                null,
            ],
            default => [$this, null],
        };
    }

    // 3. view() renders the current state. Pure function — no side effects.
    public function view(): string
    {
        $lines = ["Shopping list:\n"];
        foreach ($this->items as $i => $name) {
            $cursor = $i === $this->cursor ? '>' : ' ';
            $check  = ($this->bought[$i] ?? false) ? '[x]' : '[ ]';
            $lines[] = "  $cursor $check $name";
        }
        $lines[] = "\n(↑/↓ to move, space to toggle, q to quit)";
        return implode("\n", $lines);
    }
}

(new Program(new ShoppingList(['eggs', 'milk', 'bread', 'candy'])))->run();

   error_log('counter is now ' . $count . "\n", 3, '/tmp/candy.log');