This page collects all BEAR.Sunday manuals in one place.
Overview
“Becoming the self you want to be” Be Framework is a framework for objects to transform into the self they want to be, by their own will.
Marcel Proust said:
The real voyage of discovery consists not in seeking new landscapes, but in having new eyes.
—Marcel Proust, ‘The Prisoner’ (In Search of Lost Time, Volume 5) 1923
From Doing to Being
First, look at this:
// Traditional way to delete a user
$user = User::find($id);
$user->delete();
// A different way to delete a user
$activeUser = User::find($id);
$deletedUser = new DeletedUser($activeUser);
“DeletedUser”? What is that? you might think.
Actually, this question is the entrance to a new world of programming. Let’s think about programming in a way you have never thought of before.
From ‘What to Do’ to ‘What to Be’
Traditional programming focuses on DOING (what to do):
$user->validate();
$user->save();
$user->notify();
Be Framework focuses on BEING (what to be):
$userInput = new UserInput($name, $email);
$validatedUser = new ValidatedUser($userInput);
$savedUser = new SavedUser($validatedUser);
The former instructs objects “what to do”. The latter expresses “what state the object will become”.
Why This Matters
When focusing on DOING:
- You need to check “Is this action possible?” every time before execution
- You have to deal with various error cases
- Processing to prevent invalid states is always necessary
When focusing on BEING:
- Invalid state objects simply do not exist from the beginning
- The existence of the object itself becomes the proof of “correct state”
- You can focus only on what you can do at that time. What you cannot do is simply not executable
The difference lies in the type itself:
// Traditional: Generic type
function processUser(User $user) { }
// Be Framework: Specific state of being
function processUser(ValidatedUser $user) { }
function saveUser(SavedUser $user) { }
function archiveUser(DeletedUser $user) { }
Each type is not just data, but expresses a specific state of the object. In other words, the temporal change of the object is represented by the type, and you can only do what is possible at that time. For example, you cannot order a non-existent object to be deleted.
Why Not a “Controller”?
The “Controller” in traditional MVC frameworks aims for “control and domination” as its name suggests. However, as systems become complex, this “approach of trying to control everything” becomes difficult.
A Controller has “omnipotent freedom” to access all models and components, but having no constraints means implicitly bearing “infinite responsibility” to control and maintain consistency for every procedure in the system by itself.
Be Framework adopts a different approach. Rather than operations creating the target data, simple input objects like seeds meet other objects, grow naturally, and “transform themselves (Metamorphosis)” into the target final objects.
Commander to Gardener:
- The Commander orders subordinates (objects) to “Move!”. But it is impossible to keep ordering all complex autonomous movements.
- The Gardener does not order plants. They only prepare the environment like water and light.
Plants care only about their own transformation. They do not try to change others, but accept the environment and transform themselves autonomously (Metamorphosis) to become what they should be. Be Framework also prepares an environment where, once given input, objects become final objects by themselves. Let go of control and entrust to autonomous transformation. This is the core concept of Be Framework.
What You Will Learn in This Manual
You can acquire the following new programming methods:
- Design “what to be” instead of “what to do”
- Make it impossible to create invalid states from the beginning instead of checking them
- Express natural transformation (self-metamorphosis) instead of forcing objects to change
- Trust the correct state instead of preventing errors
So Why “DeletedUser”?
Now back to the opening question. new DeletedUser($activeUser) is not an action—it is a transformation. The user is not “being deleted”; a new existence called DeletedUser is born from $activeUser. The type itself proves that deletion has occurred. There is no need to check a $status flag, no risk of calling methods on an already-deleted user. This is the essence of Be Framework: express state transitions as new types, not as actions on existing objects.
Let’s Get Started
Want to try it first? Jump to the Getting Started guide for a hands-on Hello World, or the Tutorial for a real-world example.
Want to understand the concepts? Continue to Input Classes to learn the foundations step by step →
Input Classes
“We begin from conditions we cannot choose, and build our existence from there.”
—From Heidegger’s concept of Thrownness (Geworfenheit) (‘Being and Time’, 1927)
The Starting Point
Input Classes are the starting point of all transformations in the Be Framework.
This contains only the elements that the object itself possesses, with no external dependencies. It is, so to speak, the identity of the object. Since it is inside the object, we call this Immanence.
Basic Structure
#[Be(ValidatedUser::class)] // Destiny of Metamorphosis
final readonly class UserInput
{
public function __construct(
public string $name, // Immanent
public string $email // Immanent
) {}
}
Key Characteristics
Pure Identity: Input Classes contain only what the object fundamentally is—no external dependencies or complex logic.
Use Case Origin: Every use case has its own Input Class.
Destination (Object’s Destiny): The #[Be()] attribute declares what this input will become.
Read-only Properties: All properties are readonly. The values of the Input Class are never modified.
Examples
Simple Data Input
#[Be(OrderCalculation::class)]
final readonly class OrderInput
{
public function __construct(
public array $items, // Immanent
public string $currency // Immanent
) {}
}
Complex Structured Input
#[Be(PaymentProcessing::class)]
final readonly class PaymentInput
{
public function __construct(
public Money $amount, // Immanent
public CreditCard $card, // Immanent
public Address $billing // Immanent
) {}
}
The Input Class meets the outside world and begins transformation. We will see that process in Being Classes ➡️
Being Classes
“The Way constantly does nothing, yet there is nothing it does not do.”
—Laozi, ‘Tao Te Ching’, Chapter 37 (6th Century BC)
Immanence and Transcendence
If Input Classes are the “Beginning”, Being Classes express the “Transformed Being”.
The public properties of the Input Class are inherited by the Being Class constructor. These inherited values are called Immanence—they preserve identity through transformation. In the constructor, external powers—Transcendence—are injected and transform the Immanence.
Basic Structure
final readonly class ValidatedUser
{
public string $displayName;
public bool $isValid;
public function __construct(
#[Input] string $name, // Immanence
#[Input] string $email, // Immanence
#[Inject] NameFormatter $formatter, // Transcendence
#[Inject] EmailValidator $validator // Transcendence
) {
$this->displayName = $formatter->format($name); // New Immanence
$this->isValid = $validator->validate($email); // New Immanence
}
}
#[Input] parameters automatically receive values from the previous class’s public properties by matching names. UserInput’s public string $name maps to ValidatedUser’s #[Input] string $name. #[Inject] parameters receive external dependencies from the DI container. The detailed rules of this automatic matching are explained in Chapter 5: Metamorphosis.
Objects as Temporal Beings
In Be Framework, objects are not treated as static data structures, but as temporal beings that exist only within a specific moment in time.
Birth (Constructor)
The constructor is where objects are born. All Being Classes follow the same flow of transformation:
Immanence (#[Input]) + Transcendence (#[Inject]) → New Immanence
Immanence meets Transcendence, the logic of transformation takes effect, and new Immanence is born through property assignment. The moment it is born, the object’s identity and state are determined and become Immutable.
Life (Being)
The object exposes its “form as it should be” to the world as public readonly properties. The framework reads these properties and passes them as #[Input] to the next class in the chain. The object then vanishes, making way for the next.
Becoming the Self You Want to Be
All transformations are journeys to become the final “self you want to be, the self you are meant to be (Final Object)”.
The Transcendence encountered influences the new Immanence and disappears. Like a childhood friend, they shape me and become part of me, but as a temporal being only at that moment, they are no longer there.
Example of Transformation
final readonly class OrderCalculation
{
public Money $subtotal;
public Money $tax;
public Money $total;
public function __construct(
#[Input] array $items, // Immanence
#[Input] string $currency, // Immanence
#[Inject] PriceCalculator $calculator, // Transcendence
#[Inject] TaxService $taxService // Transcendence
) {
$this->subtotal = $calculator->calculateSubtotal($items, $currency);
$this->tax = $taxService->calculateTax($this->subtotal);
$this->total = $this->subtotal->add($this->tax); // New Immanence
}
}
OrderCalculation wants to become a calculated order. ValidatedUser wants to become a validated user. Each class becomes the “self it wants to be” in the constructor. In Be Framework, we think centering on BEING (existence), not DOING (action).
Reflection on Transformation
Immanence alone cannot change. No matter how rich the data, it cannot become a new existence by its own power. Transformation always requires an encounter with Transcendence—a power outside itself. And the Transcendence that was encountered changes the Immanence and then disappears. This pattern of “meeting, changing, and vanishing” is not limited to code. Flour meets yeast and heat to become bread; grapes meet yeast and time to become wine; a seed meets soil, water, and light to become a flower. Transformation in every domain follows this pattern.
Natural Flow
Being classes do not “do” anything. By the meeting of what they have (Immanence) and powers given from outside (Transcendence), they naturally transform into the form they should be. Nothing orchestrates this flow. Each object is simply passed to the next.
To the destination of transformation, Final Objects ➡️
Final Objects
“You are not me. How do you know that I do not know the feelings of a fish?”
—Zhuangzi’s response when asked “You are not a fish. How do you know the feelings of a fish?” (‘Zhuangzi’, 4th Century BC)
The Destination
For users, only Input and Final Objects are visible. The journey that began with an Input Class arrives as a Final Object—this is the destination of metamorphosis.
Basic Structure
final readonly class SuccessfulOrder
{
public string $orderId;
public string $confirmationCode;
public DateTimeImmutable $timestamp;
public string $message;
public BeenConfirmed $been; // Evidence of completion
public function __construct(
#[Input] Money $total, // Immanence
#[Input] CreditCard $card, // Immanence
#[Inject] OrderIdGenerator $generator, // Transcendence
#[Inject] Receipt $receipt // Transcendence
) {
$this->orderId = $generator->generate();
$this->confirmationCode = $receipt->generate($total);
$this->timestamp = new DateTimeImmutable();
$this->message = "Order Confirmed: {$this->orderId}";
$this->been = new BeenConfirmed(
actor: $card->getHolderName(),
timestamp: $this->timestamp,
evidence: [
'total' => $total->getAmount(),
'payment_method' => $card->getType(),
'confirmation' => $this->confirmationCode
]
);
}
}
The $been property uses an application-defined, domain-specific type. SuccessfulOrder has BeenConfirmed, FailedOrder has BeenRejected, a DeletedUser might have BeenDeleted. What counts as “evidence of completion” depends on the domain—you design the class to capture whatever your domain requires as proof.
In contrast to Input Classes, Final Objects fully express the richness of the domain. Immanence has met Transcendence, undergone transformation, and reached a complete state that requires no further change.
Completeness of Temporal Being
In Be Framework, we capture the temporal existence of objects on two axes:
#[Be]: The self you want to become, the destination (Direction towards the future)$been: The self that has completed (Evidence of past perfect)
Essential values like $orderId and $confirmationCode are public properties. $been is different—it records the evidence of completion: who, when, and on what basis the process completed.
Completeness from Within
In traditional programming, external tests judge whether an object has been processed correctly. But a Final Object holds what it is as its own structure. What was input, what happened, and what it became—all contained within a single existence, serving as evidence of completion.
Comparison with Input Classes
| Input Class | Final Object |
|---|---|
| Starting Point of Metamorphosis | Destination of Metamorphosis |
| What User Provides | What User Receives |
| Simple Structure | Rich and Complete State |
Multiple Final Destinies
Objects can have multiple possible final forms determined by their nature. Here $becoming triggers the metamorphosis chain—this mechanism is explained in the next chapter:
$order = $becoming(new OrderInput($items, $card));
if ($order instanceof SuccessfulOrder) {
echo $order->confirmationCode;
} else {
echo $order->message; // Error message
}
The Designer’s Work
Designing the mechanism of transformation between Input and Final Object—that is the designer’s responsibility. Users touch only the two ends, but the Being Classes in between form the backbone of the system.
The Final Object knows that it is complete, from within.
How does declared metamorphosis come to life? On to Becoming ➡️
Becoming
“Being passes into nothing, and nothing passes into being. This movement is becoming.”
—G.W.F. Hegel, Science of Logic (1812)
Triggering Metamorphosis
In chapters 2–4, we saw the structure of input classes, being classes, and final objects. The #[Be()] attribute declares “what to become,” but declaration alone does nothing. Becoming sets that declaration in motion:
$finalObject = $becoming(new EmailInput($name, $email));
EmailInput → EmailValidation → UserCreation → WelcomeMessage. The chain executes automatically, following each class’s #[Be()] declaration. Each object is born, handed over to the next existence, and ceases to be. Being becomes nothing, and from that nothing the next being emerges—this movement continues until the chain reaches its end.
Obtaining Becoming
Becoming is injected from the DI container:
final readonly class UserRegistrationPage
{
public function __construct(
private BecomingInterface $becoming
) {}
public function __invoke(string $name, string $email): WelcomeMessage
{
return ($this->becoming)(new EmailInput($name, $email));
}
}
The caller only knows what goes in and what comes out. The metamorphosis in between is determined by #[Be()] declarations.
Nested Becoming
By using Becoming within a being class, one becoming can contain another:
final readonly class OrderProcessing
{
public PaymentResult $payment;
public ShippingResult $shipping;
public function __construct(
#[Input] Order $order, // Immanence
#[Inject] Becoming $becoming // Transcendence
) {
$this->payment = $becoming(new PaymentInput($order->getPayment()));
$this->shipping = $becoming(new ShippingInput($order->getAddress()));
}
}
Since Becoming is injected as transcendence, metamorphosis chains can be triggered from within being classes as well. A diamond pattern that converges branched results into a single object is also possible.
There is not just one way. Learn various paths in Metamorphosis ➡️
Metamorphosis
“Space and time cannot be defined independently of each other.”
—Albert Einstein, The Foundation of the General Theory of Relativity (1916)
Time and Domain Are Inseparable
Just as Einstein discovered the inseparability of time and space, the Be Framework considers time and domain as a single entity that cannot be divided. Approval processes have their approval time, payments have their payment time, and transformation naturally emerges along the unique temporal axis that each domain logic possesses.
Irreversible Flow of Time
Object metamorphosis follows the arrow of time in a unidirectional flow. There is no returning to the past, no remaining in the same moment:
// Time T0: Birth of input
#[Be(EmailValidation::class)]
final readonly class EmailInput { /* ... */ }
// Time T1: First metamorphosis (T0 is already past)
#[Be(UserCreation::class)]
final readonly class EmailValidation { /* ... */ }
// Time T2: Second metamorphosis (T1 becomes memory)
#[Be(WelcomeMessage::class)]
final readonly class UserCreation { /* ... */ }
// Time T3: Final existence (encompassing all past)
final readonly class WelcomeMessage { /* ... */ }
Each moment never returns, and new existence preserves previous forms as memory within itself. Like a river flowing, time moves only in one direction.
Self-Determination of Destiny
Like living beings in reality, objects determine their own destiny through the interaction between immanence and transcendence. This is not following a predetermined route, but natural metamorphosis responding to the circumstances of that moment:
#[Be([ApprovalNotification::class, RejectionNotification::class])]
final readonly class ApplicationReview
{
public Approved|Rejected $being;
public function __construct(
#[Input] string $email, // Immanence
#[Input] array $documents, // Immanence
#[Inject] ReviewService $reviewer // Transcendence
) {
$result = $reviewer->evaluate($documents);
// Destiny is decided at this very moment
$this->being = $result->isApproved()
? new Approved($email, $result->getScore())
: new Rejected($email, $result->getReasons());
}
}
Approved and Rejected are Reason objects—like Emergency and Observation in the triage tutorial. The Reason determines the destiny: it carries the decision and its basis, and when assigned to $being, its type decides which class comes next. Decision logic that completes within the constructor belongs in the Reason layer. When deferred behavior is needed—methods to be called after construction, like assignER() in the triage example—those capabilities belong on the Final class.
Type-Based Continuation
Among the candidate classes specified in #[Be()], the framework automatically selects the one whose constructor #[Input] parameter can be satisfied by the current object’s public properties:
// ApplicationReview's $being is of type Approved,
// so this class is selected because #[Input] Approved matches
final readonly class ApprovalNotification
{
public function __construct(
#[Input] Approved $approval,
#[Inject] Mailer $mailer
) {
$mailer->send($approval->email, 'Approved! Score: ' . $approval->score);
}
}
$being is a conventional property name often used for this self-determination pattern, but it is not a name required by the framework. Any public property participates in matching.
Self-Organizing Pipelines
Like UNIX pipes that combine simple commands to create powerful systems, Be Framework combines typed objects to create natural transformation flows.
# UNIX: Text flows through externally controlled pipelines
cat access.log | grep "404" | awk '{print $7}' | sort | uniq -c
In UNIX, the shell controls the pipeline. In Be Framework, objects declare their own destiny with #[Be()]—no controller or orchestrator controls the flow.
Heraclitus said “the flowing is the river.” Just as it is not that a river flows, but that the flowing itself is the river, domains in the Be Framework are temporal existence that never rest until they reach their end.
Variable names that carry constraints and contracts, Semantic Variables ➡️
Semantic Variables
“What exists necessarily exists, and what does not exist necessarily does not exist”
—Spinoza, Ethics, Part I, Proposition 29 (1677)
Meaning and Constraints
$email is not just a string. A semantic variable is an identifier of information—it expresses meaning and carries constraints as a complete information model:
final class Email
{
#[Validate]
public function validate(string $email): void
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidEmailException();
}
}
}
// Automatically applied to any constructor argument named $email
public function __construct(string $email) {}
Define it once, and it automatically applies to every constructor parameter named $email. The value in $email is not correct by accident—it is correct by necessity. What cannot be correct simply cannot exist.
Decorating Names
Adding attributes to the same name refines the conditions for existence. When a #[Validate] method has an attribute on its parameter, it only executes when the constructor argument has a matching attribute:
// Basic $age constraint (0–150 years)
final readonly class Age
{
#[Validate]
public function validate(int $age): void
{
if ($age < 0 || $age > 150) {
throw new InvalidAgeException();
}
}
// Only executed when #[Teen] attribute is present
#[Validate]
public function validateTeen(#[Teen] int $age): void
{
if ($age < 13 || $age > 19) {
throw new InvalidTeenAgeException();
}
}
}
// Only basic Age validation applied
public function __construct(int $age) {}
// Both Age + Teen validation applied
public function __construct(#[Teen] int $age) {}
The same $age gets different existential conditions depending on its attributes.
Names as Relations
Semantic variables hold not only individual constraints but also relationships between variables. When a #[Validate] method’s parameter names partially match a constructor’s parameter names, the corresponding values are automatically passed:
// Format validation — email address confirmation match
final readonly class EmailConfirmation
{
#[Validate]
public function validate(string $email, string $confirmEmail): void
{
if ($email !== $confirmEmail) {
throw new EmailMismatchException();
}
}
}
// Ordering — start date must be before end date
final readonly class DateRange
{
#[Validate]
public function validate(string $startDate, string $endDate): void
{
if ($startDate > $endDate) {
throw new InvalidDateRangeException();
}
}
}
// External service — zip code and prefecture consistency
final readonly class ZipPrefecture
{
public function __construct(
private ZipResolver $resolver // Injected via DI
) {}
#[Validate]
public function validate(string $zipCode, string $prefecture): void
{
if (!$this->resolver->matches($zipCode, $prefecture)) {
throw new ZipPrefectureMismatchException();
}
}
}
Define once, and they are automatically applied to any constructor whose argument names match:
// EmailConfirmation + DateRange auto-applied
public function __construct(
string $email,
string $confirmEmail,
string $startDate,
string $endDate,
) {}
// ZipPrefecture auto-applied
public function __construct(
string $zipCode,
string $prefecture,
) {}
Constraints Dwell in Names
The power of names extends beyond format validation:
public function __construct(
public string $email, // Format constraint
public float $bodyTemperature, // Value range constraint
public string $inStockItemId, // Business rule constraint
) {}
From formats to value ranges to business rules—names define the conditions for existence.
When constraints dwelling in names are violated, existence fails. Learn how to handle this in Semantic Exceptions.
No existence exists without reason. Reason Layer ➡️
Reason Layer
“Everything that exists has a reason for its existence”
—Leibniz, Principle of Sufficient Reason (1714)
Reason for Existence
ExpressDelivery can exist as such because it has express shipping capabilities. StandardDelivery can exist as such because it has standard shipping capabilities. This foundation for “why it can be in that existence” is the raison d’être.
The Reason Layer is a design pattern that expresses this raison d’être as a single object.
final readonly class ExpressDelivery
{
public Fee $fee;
public function __construct(
#[Input] OrderData $order, // Immanence
#[Inject] ExpressShipping $reason // Reason for existence
) {
$this->fee = $reason->calculateFee($order->weight);
}
}
ExpressShipping is the raison d’être of ExpressDelivery. It provides the complete tool set needed for express delivery.
Reason as $being
When a reason object is passed as $being, it takes on an additional role. Its type serves as the basis for determining the transformation destination, while simultaneously providing the methods specific to that mode of existence.
final readonly class ExpressDelivery
{
public Fee $fee;
public function __construct(
#[Input] OrderData $order,
#[Input] ExpressShipping $being // Type determines transformation and provides express-specific methods
) {
$this->fee = $being->calculateFee($order->weight);
}
}
final readonly class StandardDelivery
{
public Fee $fee;
public function __construct(
#[Input] OrderData $order,
#[Input] StandardShipping $being // Type determines transformation and provides standard-specific methods
) {
$this->fee = $being->calculateFee($order->weight);
}
}
The type ExpressShipping $being itself is the reason why it becomes ExpressDelivery. The framework reads this type and automatically selects the corresponding transformation destination.
Any Reason object can serve as either #[Inject] (providing transcendent capabilities) or $being (determining destiny). The difference is not in the object itself, but in how it is used. A JTASProtocol that evaluates patients as #[Inject] in one context could determine destiny as $being in another.
Defining Reason Classes
Reason classes bundle the services necessary to realize a specific mode of existence:
namespace App\Reason;
final readonly class ExpressShipping
{
public function __construct(
private PriorityCarrier $carrier,
private RealTimeTracker $tracker,
) {}
public function calculateFee(Weight $weight): Fee // Express rate
{
return $this->carrier->expressFee($weight);
}
public function guaranteeDeliveryBy(Address $address): \DateTimeImmutable // Guaranteed delivery date
{
return $this->carrier->guaranteedDate($address);
}
public function realTimeTrack(TrackingId $id): TrackingStatus // Real-time tracking
{
return $this->tracker->realTimeStatus($id);
}
}
final readonly class StandardShipping
{
public function __construct(
private RegularCarrier $carrier,
private BatchTracker $tracker,
) {}
public function calculateFee(Weight $weight): Fee // Standard rate
{
return $this->carrier->standardFee($weight);
}
public function estimateDeliveryWindow(Address $address): DateRange // Estimated delivery window
{
return $this->carrier->estimateWindow($address);
}
}
Difference from Individual Injection
The Reason Layer uses #[Inject]. So how does it differ from using multiple #[Inject] attributes separately?
Individual injection:
public function __construct(
#[Input] OrderData $order,
#[Inject] PriorityCarrier $carrier,
#[Inject] RealTimeTracker $tracker,
#[Inject] InsuranceService $insurance,
#[Inject] DeliveryScheduler $scheduler
) {
// Using scattered tools individually
}
Reason Layer:
public function __construct(
#[Input] OrderData $order,
#[Inject] ExpressShipping $reason // Related tools bundled as reason for existence
) {
$this->fee = $reason->calculateFee($order->weight);
}
“What is needed to become ExpressDelivery?” — a single reason object answers that question. Objects themselves declare “what to become”, while reason objects realize “how to achieve that state”.
Reason Returning Potential
So far, Reason has returned immediate values like Fee. But consider order processing: inventory must be reserved, payment captured, and shipping arranged—and all three must succeed before any of them commit. If payment fails after inventory is reserved, that reservation must not persist.
For this kind of all-or-nothing coordination, Reason returns a Potential—an object that holds a deferred operation, realized later via be(). This pattern is only needed when multiple external operations must all commit atomically.
Potential: Prepared but Uncommitted
A Reason method prepares the external operation and returns a Potential:
final class PaymentGateway
{
public function authorize(string $cardNumber, int $amount): PaymentCapture
{
$authCode = $this->api->authorize($cardNumber, $amount);
return new PaymentCapture(
$authCode,
$amount,
fn () => $this->api->capture($authCode, $amount),
);
}
}
PaymentCapture is a Potential—it holds the authorization code and a deferred capture operation. The payment is authorized but not yet captured. Calling be() commits it:
$capture = $gateway->authorize($cardNumber, $amount);
$capture->authorizationCode; // Available immediately
$capture->be(); // Commits the capture
Moment: Holding Potential
A class that holds a Potential from Reason is called a Moment (Hegel’s 契機—an essential aspect that only makes sense as part of a whole). A Moment implements MomentInterface, provided by the framework:
interface MomentInterface
{
public function be(): void;
}
final readonly class PaymentCompleted implements MomentInterface
{
public PaymentCapture $capture;
public function __construct(
#[Input] public string $cardNumber,
#[Input] public int $amount,
#[Inject] PaymentGateway $gateway,
) {
$this->capture = $gateway->authorize($cardNumber, $amount);
}
public function be(): void
{
$this->capture->be();
}
}
Convergence: Final Realizes Moments
When multiple Moments must all succeed together, a Final Object receives them and calls be() on each. This is not an external command—it is self-completion:
final readonly class OrderConfirmed
{
public string $orderId;
public string $status;
public function __construct(
public InventoryReserved $inventory,
public PaymentCompleted $payment,
public ShippingArranged $shipping,
) {
$this->inventory->be();
$this->payment->be();
$this->shipping->be();
$this->orderId = 'ORD-' . date('Ymd') . '-' . bin2hex(random_bytes(4));
$this->status = 'confirmed';
}
}
If any Moment cannot be created (because its Reason failed), the Final Object is never constructed. If all Moments exist, be() commits every deferred operation. No manual rollback flags, no nested try-catch.
When to Use This Pattern
Use Potential-returning Reason when multiple external operations must succeed atomically—all commit together, or none at all. For simple cases where Reason returns an immediate value, Potential is unnecessary.
The inability to exist is itself an existence. Learn how to handle this in Validation and Error Handling ➡️
Semantic Exceptions
“I have not failed. I’ve just found 10,000 ways that won’t work”
—Thomas Edison (1847-1931)
Meaningful Failures
Generic exceptions tell what happened:
catch (Exception $e) {
echo $e->getMessage(); // "Validation failed"
}
In contrast, semantic exceptions tell why existence is impossible:
catch (SemanticVariableException $e) {
foreach ($e->getErrors()->exceptions as $exception) {
echo get_class($exception) . ": " . $exception->getMessage();
// EmptyNameException: Name cannot be empty
// InvalidEmailException: Invalid email format
}
}
Domain Exception Classes
All exceptions extend PHP’s \DomainException. Technical exceptions (RuntimeException, InvalidArgumentException, etc.) are not used. Failures are always expressed as failures with domain meaning:
final readonly class EmptyNameException extends \DomainException {}
final readonly class InvalidEmailException extends \DomainException
{
public function __construct(public string $invalidEmail)
{
parent::__construct("Invalid email format: {$invalidEmail}");
}
}
// Age-related existence failures
abstract class AgeException extends \DomainException {}
final readonly class NegativeAgeException extends AgeException {}
final readonly class AgeTooHighException extends AgeException {}
Domain exceptions hold not just messages but structured data. From the $invalidEmail property, programs can access the invalid email address value—for display, API responses, logging, and more:
catch (InvalidEmailException $e) {
$logData = [
'invalid_email' => $e->invalidEmail, // Programmatically accessible
'user_ip' => $request->getClientIp(),
'timestamp' => now()
];
Logger::warning('Invalid email attempt', $logData);
}
Multilingual Messages
The #[Message] attribute lets exceptions speak in the user’s language:
#[Message([
'en' => 'Name cannot be empty.',
'ja' => '名前は空にできません。',
'es' => 'El nombre no puede estar vacío.'
])]
final readonly class EmptyNameException extends \DomainException {}
#[Message([
'en' => 'Age must be at least {min} years.',
'ja' => '年齢は最低{min}歳でなければなりません。'
])]
final readonly class AgeTooYoungException extends \DomainException
{
public function __construct(public int $min = 13) {}
}
Error Collection
The framework does not stop at the first error—it collects all validation errors:
try {
$user = $becoming(new UserInput('', 'invalid-email', 10));
} catch (SemanticVariableException $e) {
// Three errors collected simultaneously:
// - EmptyNameException
// - InvalidEmailException
// - AgeTooYoungException
$messages = $e->getErrors()->getMessages('en');
// ["Name cannot be empty", "Invalid email format", "Age must be at least 13"]
}
Rather than “fail fast”—understand all problems at once.
Errors as Existence
Error states can be treated as valid metamorphosis results:
#[Be([ValidUser::class, InvalidUser::class])]
final readonly class UserValidation
{
public ValidUser|InvalidUser $being;
public function __construct(#[Input] string $data)
{
try {
$this->being = new ValidUser($data);
} catch (ValidationException $e) {
$this->being = new InvalidUser($e->getErrors());
}
}
}
Rather than halting execution with exceptions, errors are expressed as types. Failure, like success, is a legitimate result of transformation.
For the full picture, see Reference ➡️
Semantic Logging
“What we record becomes memory; what we remember becomes truth”
—Adapted from Orwell’s concept in 1984 (1949)
Overview
Traditional logs:
[INFO] User registered: alice@example.com
[INFO] Verification passed
[INFO] Insert into users table
Semantic logs:
{
"open": { "from": "UnverifiedEmail", "to": "RegisteredUser" },
"events": [
{ "type": "email_format_asserted", "context": { "email": "alice@example.com" } },
{ "type": "user_inserted", "context": { "userId": 42, "email": "alice@example.com" } }
],
"close": { "properties": { "userId": 42, "value": "alice@example.com" } }
}
Traditional logs are just lines of text arranged chronologically — the reader is left to guess which lines belong to the same operation.
With semantic logging, one metamorphosis fits in one JSON. Source and destination, intermediate events, final properties — “what became what, and why” is recorded as typed, structured data and can be validated with JSON Schema.
Be Framework provides two semantic recording mechanisms:
$been— Proof that the Final object carries its own history (proof)SemanticLoggerInterface— A log that records hierarchical operations (log)
| | log | $been |
|–||
Technical foundation: Koriym.SemanticLogger
For the full framework overview, see Reference ➡️
Reference
“Knowledge is the beginning of action, and action is the completion of knowledge”
—Wang Yangming, Chuánxí Lù (1518)
Official Repositories
- Be Framework Core — Framework core and libraries
- Application Skeleton — Project starter skeleton
- Concept Stage Documentation — Early documentation exploring design philosophy evolution
Development Reference
- Naming Standards — Being-oriented naming principles
- Directory Layout — Canonical
src/<dir>/slots and what each is for - Philosophy Behind — Philosophical roots of ontological programming
Philosophy Behind
“Everything flows” (Panta Rhei) —Heraclitus (535-475 BC)
Everything is Existence
Be—Being is Everything. This framework is built on the premise that everything is existence.
As we deepened our questions about domains, we arrived at questions of existence. What makes existence possible? How does existence transform? What does it mean to not exist?
For 2,500 years, Eastern thinkers and Western philosophers have deepened their questions about existence. Yet this framework did not adopt philosophy as design principles. Rather, by deepening questions about domains, their teachings came to feel like testimony.
2. The Question of “WHETHER?”
Procedural programming asks HOW?—how to do it. OOP asks WHAT?—what is it. Ontological programming asks first, WHETHER?—can it even exist.
#[Be(ValidatedUser::class)]
final readonly class UserInput
{
public function __construct(
public string $email,
public int $age
) {}
}
If conditions are met, ValidatedUser exists. If not, it simply doesn’t.
4. Heraclitus: Everything Flows
Objects in Time
Heraclitus observed that you cannot step into the same river twice.
Traditional objects often exist outside of time:
$user->age = 5;
$user->age = 50; // Same object, different age
$user->delete();
$user->getName(); // After deletion?
Temporal Sequence
One observation: domain concepts often have natural temporal order.
// Types can express this order:
UserInput → RegisteredUser → ActiveUser → DeletedUser
Each stage is distinct. A DeletedUser type cannot become ActiveUser—the type system reflects this constraint.
Time T0: EmailInput — initial state
↓
Time T1: ValidatedEmail — after validation
↓
Time T2: RegisteredUser — after registration
Each stage represents a complete state, not a partial one.
6. Wu Wei: Non-Forcing
Laozi wrote:
“The Tao does nothing, yet nothing is left undone.”
This suggests acting in accordance with natural flow rather than forcing outcomes.
// Forcing
$controller->forceUserToValidate();
$controller->forceUserToSave();
// Enabling
#[Be(ValidatedUser::class)]
#[Be(SavedUser::class)]
$user = $becoming(new UserInput($data));
The second approach doesn’t force—it declares what the object can become and lets the transformation happen.
8. Two Kinds of Transparency
Structural
UserInput → ValidatedUser → SavedUser → ActiveUser
The transformation path is visible in the types.
Semantic
string $email // Name suggests Email validation
string $password // Name suggests Password validation
Names carry meaning.
When both structure and semantics are transparent, code serves as its own documentation.
| Heraclitus | Everything flows | Input → Being → Final |
| Aristotle | Potentiality | Success\|Failure $being |
| Laozi | Non-forcing | #[Be] declaration |
| Spinoza | Necessary existence | Semantic variables |
| Husserl | Transcendence in immanence | #[Input] + #[Inject] |
| Zhuangzi | Self-testimony | $been |
| Heidegger | Language is the house of Being | Class names construct the world |
| Buddhism | Momentariness | Cessation and arising |
Log-Driven Development (LDD)
“Once Zhuang Zhou dreamt he was a butterfly… Suddenly he awoke and there he was, solid and unmistakable Zhuang Zhou. But he didn’t know if he was Zhuang Zhou who had dreamt he was a butterfly, or a butterfly dreaming he was Zhuang Zhou.” —Zhuangzi, ‘The Adjustment of Controversies’ (Qi Wu Lun) (Circa 4th Century BC)
Note: This chapter describes the “Future Vision” that Be Framework aims for. Not everything is implemented in the current version.
Ultimate Transparency
Be Framework aims for a world where there is ultimate transparency in all layers—code, execution, log, and specification—and also where the boundaries between them become ambiguous.
Reversibility Brought by Three Transparencies
The one-way flow of writing code from specifications, executing it, and outputting logs is the natural order of things (standard). However, Be Framework seeks to explore the possibility of complete “Reversibility” through the following three transparencies:
- Structural Transparency:
- The
#[Be]attribute explicitly manifests the flow of metamorphosis as the structure of the code itself. This allows drawing the entire specific Decision Graph of the application just by static analysis.
- The
- Semantic Transparency:
- Variable names are not just labels, but contracts. A name like
$emailencompasses validation by theEmailclass and philosophical definitions (like ALPS). This lets you understand the “meaning” and “guarantee” of the data just by looking at the variable name.
- Variable names are not just labels, but contracts. A name like
- Execution Transparency:
- Semantic logs are not merely “transit records” but record the entire basis of judgment “why that decision was reached”.
Infinite Loop of Value
This transparency enables the “Elimination of Guesswork” by AI.
- Log: Humans (or AI) write the “story that should be” as a log.
- AI Construction: AI assembles code not by guesswork but as “work” from that log, relying on structural and semantic transparency.
- Code: The generated code becomes the definition of Temporal Being.
- Execution: The code is executed, and a log according to the contract is output.
- Verification: Confirm that the output log matches the original story (Log) through execution transparency.
Through this cycle, a world where “Dream (Log)” and “Reality (Code)” are mutually and continuously converted is completed.
Log-Driven Development (LDD)
The new development method brought about by this reversibility is Log-Driven Development (LDD).
“Code ⇔ Log ⇔ Specification”
Just as TDD (Test-Driven Development) defines “Behavior (Doing)” and then implements it, LDD defines “Story (Log)” and then generates Existence (Code/Being).
- Narrative First: Developers first write the “Semantic Log that should be”. This is the “story” the system should trace, and is itself the DSL (Domain Specific Language) defining the application.
```yaml
UserRegistration:
- Input: {email: “New User”, …} Becomes: ValidatedUser
- ValidatedUser: Becomes: RegisteredUser ```
- Specification Generation: “Specifications (what state transitions are required)” are derived from the log.
- Code Generation: From the specifications,
#[Be]chains and class definitions to realize them are automatically generated.
Future Debugging
In a world where ultimate transparency is realized, debugging becomes synonymous with “reading logs”. Non-reproducible bugs do not exist. This is because the log is a complete “executable specification”, and just by pouring that log in, the system can Reproduce (Replay) exactly the same metamorphosis process.
Conclusion: Code as Philosophy
The “Butterfly Dream” at the beginning is an anecdote of the ancient Chinese thinker Zhuangzi.
He dreamt he was a butterfly, fluttering about… suddenly he awoke… but he didn’t know if he was Zhuang Zhou who had dreamt he was a butterfly, or a butterfly dreaming he was Zhuang Zhou.
Digital reality where logs can become code in LDD is exactly this Butterfly Dream, and Be Framework’s complete transparency makes the boundary of meaning between log and code ambiguous. Thought becomes word, word becomes code, and code becomes story.
Programming in Be Framework is not merely ordering a computer. It is an act of defining “Temporal Being” in digital space acting like life, and letting its metamorphosis spin out a meaningful story (Log).
Be Framework FAQ
0) TL;DR
Q. Is this a new “programming paradigm”?
A. A pattern is a better answer to an existing question. A paradigm changes the question itself.
$email is not validated externally — its very existence is proof of correctness. Semantic variables, self-determination, self-proof, choreography — these are not individual features but all derived from a single viewpoint: “seeing the world from the domain’s own self.”
Procedural programming asked HOW (how to do it). Object-oriented programming asked WHAT (what is it). Being-oriented programming asks WHETHER (can it even exist).
2) Core Features
Q5. What does the #[Be] attribute do?
A. It declares an object’s destiny (what it will become next).
You can specify single or multiple transformation candidates, and the framework automatically selects the continuation at runtime based on the type of the $being property.
For details, see Metamorphosis.
Q6. What’s the role of the $being property?
A. It holds the next existence that the current existence leads to.
Union types explicitly show all possible outcomes.
Q6.5. Are $being and $been special properties?
A. No, they are not special properties.
becoming() doesn’t read property names, but looks at the types declared on properties to select the next class.
Therefore, the names $being / $been are not required. public Success|Failure $result; works the same way with different names.
However, using these names according to documentation, samples, and design philosophy provides the benefit of immediately recognizing “transformation destination (being)” and “completion evidence (been)”.
Q7. How is the metamorphosis chain controlled?
A. Through the chain of #[Be] attributes, the next transformation is automatically determined.
When multiple transformation destinations are possible, they are expressed as union types in the $being property, and the next transformation destination is determined by the type actually assigned. This mechanism allows complex business logic to be expressed declaratively.
Q8. What are “Semantic Variables”?
A. Variable name = meaning + constraints. $email must be a “valid Email” to exist.
It integrates scattered validations (controller/validator/docs) into types.
See Semantic Variables for details.
Q8.5. How do you handle variables with different meanings but same constraints ($userId, $authorId, etc.)?
A. Share constraints through common base classes or traits while separating meaning through inheritance.
// src/Semantic/Abstract/Id.php - Common constraint base class
namespace App\Semantic\Abstract;
abstract readonly class Id {
public function __construct(public string $value) {
if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}/', $value)) {
throw new InvalidIdException();
}
}
}
// src/Semantic/UserId.php - Semantic variables through inheritance
readonly class UserId extends \App\Semantic\Abstract\Id {}
readonly class AuthorId extends \App\Semantic\Abstract\Id {}
// Usage: variable names carry semantic meaning
function updateArticle(UserId $userId, AuthorId $authorId) {
// $userId and $authorId cannot be confused
}
Q9. What is the “Reason Layer”?
A. It gathers the foundation — the complete tool set — for an existence to come into being, into a single object.
In traditional DI, dependencies are injected individually and scattered, making it hard to see “what are the preconditions for this existence?” Reason bundles the grounds for existence by meaning, so in tests you can swap out the entire set of preconditions at once.
See Reason Layer for details.
Q10. What is $been (self-proof) for?
A. It internally contains the trail of that existence’s completion (who, when, what).
It aligns well with external tests and audit logs.
See Final Objects for details.
4) Integration with Existing Assets
Q15. Can this be introduced to existing MVC apps?
A. Yes. Replace the Use Case layer with Be and call becoming(new …Input) from Controllers. Gradual migration is possible.
Q16. Where do you use DB or external APIs?
A. Confined to Reason. Existence types are freed from database schemas and API constraints — the definition of existence is separated from technical means.
Q17. What are the framework dependencies?
A. Core uses PHP standard + DI (e.g., Ray.Di). Integration with Laravel/Symfony etc. is possible via adapters.
Q18. Do static analysis and IDE completion work?
A. Very well since types are “states.” Branching is made explicit with union types, and completion is safe too.
6) Migration
Q21. What are the migration steps for existing code? (Minimal steps)
A.
- Choose one representative use case
- Extract input as
…Input(immanent only) - Design transformation target as
…(existence), add#[Be] - Consolidate external dependencies into Reason
- Call
becoming(new …Input)from Controller - Migrate validation to semantic variables / replace exceptions semantically
8) Examples & Snippets
Q23. What’s a minimal example of typical flow?
A.
// 1) Input (immanent only)
#[Be(ValidatedUser::class)]
final readonly class UserInput {
public function __construct(public string $name, public string $email) {}
}
// 2) Being (moment of transformation)
final readonly class ValidatedUser {
public string $display;
public bool $isValid;
public function __construct(
#[Input] string $name,
#[Input] string $email,
#[Inject] NameFormatter $fmt,
#[Inject] EmailValidator $v
) {
$this->display = $fmt->format($name);
$this->isValid = $v->validate($email);
}
}
// 3) Execution (self-organization)
$user = $becoming(new UserInput($name, $email));
// Result: ValidatedUser object
// $user->display: "Formatted name"
// $user->isValid: true (for valid email)
10) Going Deeper
Q. Where can I learn more?
A. A trilogy of neutral review articles by the high-performance LLM Fable 5 offers a multifaceted analysis. Start with the first: Three Lines of While-Loop and Fifteen Thinkers — A Be Framework Review.
This page collects all BEAR.Sunday manuals in one place.
Overview
“Becoming the self you want to be” Be Framework is a framework for objects to transform into the self they want to be, by their own will.
Marcel Proust said:
The real voyage of discovery consists not in seeking new landscapes, but in having new eyes.
—Marcel Proust, ‘The Prisoner’ (In Search of Lost Time, Volume 5) 1923
From Doing to Being
First, look at this:
// Traditional way to delete a user
$user = User::find($id);
$user->delete();
// A different way to delete a user
$activeUser = User::find($id);
$deletedUser = new DeletedUser($activeUser);
“DeletedUser”? What is that? you might think.
Actually, this question is the entrance to a new world of programming. Let’s think about programming in a way you have never thought of before.
From ‘What to Do’ to ‘What to Be’
Traditional programming focuses on DOING (what to do):
$user->validate();
$user->save();
$user->notify();
Be Framework focuses on BEING (what to be):
$userInput = new UserInput($name, $email);
$validatedUser = new ValidatedUser($userInput);
$savedUser = new SavedUser($validatedUser);
The former instructs objects “what to do”. The latter expresses “what state the object will become”.
Why This Matters
When focusing on DOING:
- You need to check “Is this action possible?” every time before execution
- You have to deal with various error cases
- Processing to prevent invalid states is always necessary
When focusing on BEING:
- Invalid state objects simply do not exist from the beginning
- The existence of the object itself becomes the proof of “correct state”
- You can focus only on what you can do at that time. What you cannot do is simply not executable
The difference lies in the type itself:
// Traditional: Generic type
function processUser(User $user) { }
// Be Framework: Specific state of being
function processUser(ValidatedUser $user) { }
function saveUser(SavedUser $user) { }
function archiveUser(DeletedUser $user) { }
Each type is not just data, but expresses a specific state of the object. In other words, the temporal change of the object is represented by the type, and you can only do what is possible at that time. For example, you cannot order a non-existent object to be deleted.
Why Not a “Controller”?
The “Controller” in traditional MVC frameworks aims for “control and domination” as its name suggests. However, as systems become complex, this “approach of trying to control everything” becomes difficult.
A Controller has “omnipotent freedom” to access all models and components, but having no constraints means implicitly bearing “infinite responsibility” to control and maintain consistency for every procedure in the system by itself.
Be Framework adopts a different approach. Rather than operations creating the target data, simple input objects like seeds meet other objects, grow naturally, and “transform themselves (Metamorphosis)” into the target final objects.
Commander to Gardener:
- The Commander orders subordinates (objects) to “Move!”. But it is impossible to keep ordering all complex autonomous movements.
- The Gardener does not order plants. They only prepare the environment like water and light.
Plants care only about their own transformation. They do not try to change others, but accept the environment and transform themselves autonomously (Metamorphosis) to become what they should be. Be Framework also prepares an environment where, once given input, objects become final objects by themselves. Let go of control and entrust to autonomous transformation. This is the core concept of Be Framework.
What You Will Learn in This Manual
You can acquire the following new programming methods:
- Design “what to be” instead of “what to do”
- Make it impossible to create invalid states from the beginning instead of checking them
- Express natural transformation (self-metamorphosis) instead of forcing objects to change
- Trust the correct state instead of preventing errors
So Why “DeletedUser”?
Now back to the opening question. new DeletedUser($activeUser) is not an action—it is a transformation. The user is not “being deleted”; a new existence called DeletedUser is born from $activeUser. The type itself proves that deletion has occurred. There is no need to check a $status flag, no risk of calling methods on an already-deleted user. This is the essence of Be Framework: express state transitions as new types, not as actions on existing objects.
Let’s Get Started
Want to try it first? Jump to the Getting Started guide for a hands-on Hello World, or the Tutorial for a real-world example.
Want to understand the concepts? Continue to Input Classes to learn the foundations step by step →
Input Classes
“We begin from conditions we cannot choose, and build our existence from there.”
—From Heidegger’s concept of Thrownness (Geworfenheit) (‘Being and Time’, 1927)
The Starting Point
Input Classes are the starting point of all transformations in the Be Framework.
This contains only the elements that the object itself possesses, with no external dependencies. It is, so to speak, the identity of the object. Since it is inside the object, we call this Immanence.
Basic Structure
#[Be(ValidatedUser::class)] // Destiny of Metamorphosis
final readonly class UserInput
{
public function __construct(
public string $name, // Immanent
public string $email // Immanent
) {}
}
Key Characteristics
Pure Identity: Input Classes contain only what the object fundamentally is—no external dependencies or complex logic.
Use Case Origin: Every use case has its own Input Class.
Destination (Object’s Destiny): The #[Be()] attribute declares what this input will become.
Read-only Properties: All properties are readonly. The values of the Input Class are never modified.
Examples
Simple Data Input
#[Be(OrderCalculation::class)]
final readonly class OrderInput
{
public function __construct(
public array $items, // Immanent
public string $currency // Immanent
) {}
}
Complex Structured Input
#[Be(PaymentProcessing::class)]
final readonly class PaymentInput
{
public function __construct(
public Money $amount, // Immanent
public CreditCard $card, // Immanent
public Address $billing // Immanent
) {}
}
To the destination of transformation, Final Objects ➡️
Final Objects
“You are not me. How do you know that I do not know the feelings of a fish?”
—Zhuangzi’s response when asked “You are not a fish. How do you know the feelings of a fish?” (‘Zhuangzi’, 4th Century BC)
The Destination
For users, only Input and Final Objects are visible. The journey that began with an Input Class arrives as a Final Object—this is the destination of metamorphosis.
Basic Structure
final readonly class SuccessfulOrder
{
public string $orderId;
public string $confirmationCode;
public DateTimeImmutable $timestamp;
public string $message;
public BeenConfirmed $been; // Evidence of completion
public function __construct(
#[Input] Money $total, // Immanence
#[Input] CreditCard $card, // Immanence
#[Inject] OrderIdGenerator $generator, // Transcendence
#[Inject] Receipt $receipt // Transcendence
) {
$this->orderId = $generator->generate();
$this->confirmationCode = $receipt->generate($total);
$this->timestamp = new DateTimeImmutable();
$this->message = "Order Confirmed: {$this->orderId}";
$this->been = new BeenConfirmed(
actor: $card->getHolderName(),
timestamp: $this->timestamp,
evidence: [
'total' => $total->getAmount(),
'payment_method' => $card->getType(),
'confirmation' => $this->confirmationCode
]
);
}
}
The $been property uses an application-defined, domain-specific type. SuccessfulOrder has BeenConfirmed, FailedOrder has BeenRejected, a DeletedUser might have BeenDeleted. What counts as “evidence of completion” depends on the domain—you design the class to capture whatever your domain requires as proof.
In contrast to Input Classes, Final Objects fully express the richness of the domain. Immanence has met Transcendence, undergone transformation, and reached a complete state that requires no further change.
Completeness of Temporal Being
In Be Framework, we capture the temporal existence of objects on two axes:
#[Be]: The self you want to become, the destination (Direction towards the future)$been: The self that has completed (Evidence of past perfect)
Essential values like $orderId and $confirmationCode are public properties. $been is different—it records the evidence of completion: who, when, and on what basis the process completed.
Completeness from Within
In traditional programming, external tests judge whether an object has been processed correctly. But a Final Object holds what it is as its own structure. What was input, what happened, and what it became—all contained within a single existence, serving as evidence of completion.
Comparison with Input Classes
| Input Class | Final Object |
|---|---|
| Starting Point of Metamorphosis | Destination of Metamorphosis |
| What User Provides | What User Receives |
| Simple Structure | Rich and Complete State |
Multiple Final Destinies
Objects can have multiple possible final forms determined by their nature. Here $becoming triggers the metamorphosis chain—this mechanism is explained in the next chapter:
$order = $becoming(new OrderInput($items, $card));
if ($order instanceof SuccessfulOrder) {
echo $order->confirmationCode;
} else {
echo $order->message; // Error message
}
The Designer’s Work
Designing the mechanism of transformation between Input and Final Object—that is the designer’s responsibility. Users touch only the two ends, but the Being Classes in between form the backbone of the system.
The Final Object knows that it is complete, from within.
There is not just one way. Learn various paths in Metamorphosis ➡️
Metamorphosis
“Space and time cannot be defined independently of each other.”
—Albert Einstein, The Foundation of the General Theory of Relativity (1916)
Time and Domain Are Inseparable
Just as Einstein discovered the inseparability of time and space, the Be Framework considers time and domain as a single entity that cannot be divided. Approval processes have their approval time, payments have their payment time, and transformation naturally emerges along the unique temporal axis that each domain logic possesses.
Irreversible Flow of Time
Object metamorphosis follows the arrow of time in a unidirectional flow. There is no returning to the past, no remaining in the same moment:
// Time T0: Birth of input
#[Be(EmailValidation::class)]
final readonly class EmailInput { /* ... */ }
// Time T1: First metamorphosis (T0 is already past)
#[Be(UserCreation::class)]
final readonly class EmailValidation { /* ... */ }
// Time T2: Second metamorphosis (T1 becomes memory)
#[Be(WelcomeMessage::class)]
final readonly class UserCreation { /* ... */ }
// Time T3: Final existence (encompassing all past)
final readonly class WelcomeMessage { /* ... */ }
Each moment never returns, and new existence preserves previous forms as memory within itself. Like a river flowing, time moves only in one direction.
Self-Determination of Destiny
Like living beings in reality, objects determine their own destiny through the interaction between immanence and transcendence. This is not following a predetermined route, but natural metamorphosis responding to the circumstances of that moment:
#[Be([ApprovalNotification::class, RejectionNotification::class])]
final readonly class ApplicationReview
{
public Approved|Rejected $being;
public function __construct(
#[Input] string $email, // Immanence
#[Input] array $documents, // Immanence
#[Inject] ReviewService $reviewer // Transcendence
) {
$result = $reviewer->evaluate($documents);
// Destiny is decided at this very moment
$this->being = $result->isApproved()
? new Approved($email, $result->getScore())
: new Rejected($email, $result->getReasons());
}
}
Approved and Rejected are Reason objects—like Emergency and Observation in the triage tutorial. The Reason determines the destiny: it carries the decision and its basis, and when assigned to $being, its type decides which class comes next. Decision logic that completes within the constructor belongs in the Reason layer. When deferred behavior is needed—methods to be called after construction, like assignER() in the triage example—those capabilities belong on the Final class.
Type-Based Continuation
Among the candidate classes specified in #[Be()], the framework automatically selects the one whose constructor #[Input] parameter can be satisfied by the current object’s public properties:
// ApplicationReview's $being is of type Approved,
// so this class is selected because #[Input] Approved matches
final readonly class ApprovalNotification
{
public function __construct(
#[Input] Approved $approval,
#[Inject] Mailer $mailer
) {
$mailer->send($approval->email, 'Approved! Score: ' . $approval->score);
}
}
$being is a conventional property name often used for this self-determination pattern, but it is not a name required by the framework. Any public property participates in matching.
Self-Organizing Pipelines
Like UNIX pipes that combine simple commands to create powerful systems, Be Framework combines typed objects to create natural transformation flows.
# UNIX: Text flows through externally controlled pipelines
cat access.log | grep "404" | awk '{print $7}' | sort | uniq -c
In UNIX, the shell controls the pipeline. In Be Framework, objects declare their own destiny with #[Be()]—no controller or orchestrator controls the flow.
Heraclitus said “the flowing is the river.” Just as it is not that a river flows, but that the flowing itself is the river, domains in the Be Framework are temporal existence that never rest until they reach their end.
No existence exists without reason. Reason Layer ➡️
Reason Layer
“Everything that exists has a reason for its existence”
—Leibniz, Principle of Sufficient Reason (1714)
Reason for Existence
ExpressDelivery can exist as such because it has express shipping capabilities. StandardDelivery can exist as such because it has standard shipping capabilities. This foundation for “why it can be in that existence” is the raison d’être.
The Reason Layer is a design pattern that expresses this raison d’être as a single object.
final readonly class ExpressDelivery
{
public Fee $fee;
public function __construct(
#[Input] OrderData $order, // Immanence
#[Inject] ExpressShipping $reason // Reason for existence
) {
$this->fee = $reason->calculateFee($order->weight);
}
}
ExpressShipping is the raison d’être of ExpressDelivery. It provides the complete tool set needed for express delivery.
Reason as $being
When a reason object is passed as $being, it takes on an additional role. Its type serves as the basis for determining the transformation destination, while simultaneously providing the methods specific to that mode of existence.
final readonly class ExpressDelivery
{
public Fee $fee;
public function __construct(
#[Input] OrderData $order,
#[Input] ExpressShipping $being // Type determines transformation and provides express-specific methods
) {
$this->fee = $being->calculateFee($order->weight);
}
}
final readonly class StandardDelivery
{
public Fee $fee;
public function __construct(
#[Input] OrderData $order,
#[Input] StandardShipping $being // Type determines transformation and provides standard-specific methods
) {
$this->fee = $being->calculateFee($order->weight);
}
}
The type ExpressShipping $being itself is the reason why it becomes ExpressDelivery. The framework reads this type and automatically selects the corresponding transformation destination.
Any Reason object can serve as either #[Inject] (providing transcendent capabilities) or $being (determining destiny). The difference is not in the object itself, but in how it is used. A JTASProtocol that evaluates patients as #[Inject] in one context could determine destiny as $being in another.
Defining Reason Classes
Reason classes bundle the services necessary to realize a specific mode of existence:
namespace App\Reason;
final readonly class ExpressShipping
{
public function __construct(
private PriorityCarrier $carrier,
private RealTimeTracker $tracker,
) {}
public function calculateFee(Weight $weight): Fee // Express rate
{
return $this->carrier->expressFee($weight);
}
public function guaranteeDeliveryBy(Address $address): \DateTimeImmutable // Guaranteed delivery date
{
return $this->carrier->guaranteedDate($address);
}
public function realTimeTrack(TrackingId $id): TrackingStatus // Real-time tracking
{
return $this->tracker->realTimeStatus($id);
}
}
final readonly class StandardShipping
{
public function __construct(
private RegularCarrier $carrier,
private BatchTracker $tracker,
) {}
public function calculateFee(Weight $weight): Fee // Standard rate
{
return $this->carrier->standardFee($weight);
}
public function estimateDeliveryWindow(Address $address): DateRange // Estimated delivery window
{
return $this->carrier->estimateWindow($address);
}
}
Difference from Individual Injection
The Reason Layer uses #[Inject]. So how does it differ from using multiple #[Inject] attributes separately?
Individual injection:
public function __construct(
#[Input] OrderData $order,
#[Inject] PriorityCarrier $carrier,
#[Inject] RealTimeTracker $tracker,
#[Inject] InsuranceService $insurance,
#[Inject] DeliveryScheduler $scheduler
) {
// Using scattered tools individually
}
Reason Layer:
public function __construct(
#[Input] OrderData $order,
#[Inject] ExpressShipping $reason // Related tools bundled as reason for existence
) {
$this->fee = $reason->calculateFee($order->weight);
}
“What is needed to become ExpressDelivery?” — a single reason object answers that question. Objects themselves declare “what to become”, while reason objects realize “how to achieve that state”.
Reason Returning Potential
So far, Reason has returned immediate values like Fee. But consider order processing: inventory must be reserved, payment captured, and shipping arranged—and all three must succeed before any of them commit. If payment fails after inventory is reserved, that reservation must not persist.
For this kind of all-or-nothing coordination, Reason returns a Potential—an object that holds a deferred operation, realized later via be(). This pattern is only needed when multiple external operations must all commit atomically.
Potential: Prepared but Uncommitted
A Reason method prepares the external operation and returns a Potential:
final class PaymentGateway
{
public function authorize(string $cardNumber, int $amount): PaymentCapture
{
$authCode = $this->api->authorize($cardNumber, $amount);
return new PaymentCapture(
$authCode,
$amount,
fn () => $this->api->capture($authCode, $amount),
);
}
}
PaymentCapture is a Potential—it holds the authorization code and a deferred capture operation. The payment is authorized but not yet captured. Calling be() commits it:
$capture = $gateway->authorize($cardNumber, $amount);
$capture->authorizationCode; // Available immediately
$capture->be(); // Commits the capture
Moment: Holding Potential
A class that holds a Potential from Reason is called a Moment (Hegel’s 契機—an essential aspect that only makes sense as part of a whole). A Moment implements MomentInterface, provided by the framework:
interface MomentInterface
{
public function be(): void;
}
final readonly class PaymentCompleted implements MomentInterface
{
public PaymentCapture $capture;
public function __construct(
#[Input] public string $cardNumber,
#[Input] public int $amount,
#[Inject] PaymentGateway $gateway,
) {
$this->capture = $gateway->authorize($cardNumber, $amount);
}
public function be(): void
{
$this->capture->be();
}
}
Convergence: Final Realizes Moments
When multiple Moments must all succeed together, a Final Object receives them and calls be() on each. This is not an external command—it is self-completion:
final readonly class OrderConfirmed
{
public string $orderId;
public string $status;
public function __construct(
public InventoryReserved $inventory,
public PaymentCompleted $payment,
public ShippingArranged $shipping,
) {
$this->inventory->be();
$this->payment->be();
$this->shipping->be();
$this->orderId = 'ORD-' . date('Ymd') . '-' . bin2hex(random_bytes(4));
$this->status = 'confirmed';
}
}
If any Moment cannot be created (because its Reason failed), the Final Object is never constructed. If all Moments exist, be() commits every deferred operation. No manual rollback flags, no nested try-catch.
When to Use This Pattern
Use Potential-returning Reason when multiple external operations must succeed atomically—all commit together, or none at all. For simple cases where Reason returns an immediate value, Potential is unnecessary.
For the full picture, see Reference ➡️
Semantic Logging
“What we record becomes memory; what we remember becomes truth”
—Adapted from Orwell’s concept in 1984 (1949)
Overview
Traditional logs:
[INFO] User registered: alice@example.com
[INFO] Verification passed
[INFO] Insert into users table
Semantic logs:
{
"open": { "from": "UnverifiedEmail", "to": "RegisteredUser" },
"events": [
{ "type": "email_format_asserted", "context": { "email": "alice@example.com" } },
{ "type": "user_inserted", "context": { "userId": 42, "email": "alice@example.com" } }
],
"close": { "properties": { "userId": 42, "value": "alice@example.com" } }
}
Traditional logs are just lines of text arranged chronologically — the reader is left to guess which lines belong to the same operation.
With semantic logging, one metamorphosis fits in one JSON. Source and destination, intermediate events, final properties — “what became what, and why” is recorded as typed, structured data and can be validated with JSON Schema.
Be Framework provides two semantic recording mechanisms:
$been— Proof that the Final object carries its own history (proof)SemanticLoggerInterface— A log that records hierarchical operations (log)
| | log | $been |
|–||
Technical foundation: Koriym.SemanticLogger
For the full framework overview, see Reference ➡️
Reference
“Knowledge is the beginning of action, and action is the completion of knowledge”
—Wang Yangming, Chuánxí Lù (1518)
Official Repositories
- Be Framework Core — Framework core and libraries
- Application Skeleton — Project starter skeleton
- Concept Stage Documentation — Early documentation exploring design philosophy evolution
Development Reference
- Naming Standards — Being-oriented naming principles
- Directory Layout — Canonical
src/<dir>/slots and what each is for - Philosophy Behind — Philosophical roots of ontological programming
Philosophy Behind
“Everything flows” (Panta Rhei) —Heraclitus (535-475 BC)
Everything is Existence
Be—Being is Everything. This framework is built on the premise that everything is existence.
As we deepened our questions about domains, we arrived at questions of existence. What makes existence possible? How does existence transform? What does it mean to not exist?
For 2,500 years, Eastern thinkers and Western philosophers have deepened their questions about existence. Yet this framework did not adopt philosophy as design principles. Rather, by deepening questions about domains, their teachings came to feel like testimony.
2. The Question of “WHETHER?”
Procedural programming asks HOW?—how to do it. OOP asks WHAT?—what is it. Ontological programming asks first, WHETHER?—can it even exist.
#[Be(ValidatedUser::class)]
final readonly class UserInput
{
public function __construct(
public string $email,
public int $age
) {}
}
If conditions are met, ValidatedUser exists. If not, it simply doesn’t.
4. Heraclitus: Everything Flows
Objects in Time
Heraclitus observed that you cannot step into the same river twice.
Traditional objects often exist outside of time:
$user->age = 5;
$user->age = 50; // Same object, different age
$user->delete();
$user->getName(); // After deletion?
Temporal Sequence
One observation: domain concepts often have natural temporal order.
// Types can express this order:
UserInput → RegisteredUser → ActiveUser → DeletedUser
Each stage is distinct. A DeletedUser type cannot become ActiveUser—the type system reflects this constraint.
Time T0: EmailInput — initial state
↓
Time T1: ValidatedEmail — after validation
↓
Time T2: RegisteredUser — after registration
Each stage represents a complete state, not a partial one.
6. Wu Wei: Non-Forcing
Laozi wrote:
“The Tao does nothing, yet nothing is left undone.”
This suggests acting in accordance with natural flow rather than forcing outcomes.
// Forcing
$controller->forceUserToValidate();
$controller->forceUserToSave();
// Enabling
#[Be(ValidatedUser::class)]
#[Be(SavedUser::class)]
$user = $becoming(new UserInput($data));
The second approach doesn’t force—it declares what the object can become and lets the transformation happen.
8. Two Kinds of Transparency
Structural
UserInput → ValidatedUser → SavedUser → ActiveUser
The transformation path is visible in the types.
Semantic
string $email // Name suggests Email validation
string $password // Name suggests Password validation
Names carry meaning.
When both structure and semantics are transparent, code serves as its own documentation.
| Heraclitus | Everything flows | Input → Being → Final |
| Aristotle | Potentiality | Success\|Failure $being |
| Laozi | Non-forcing | #[Be] declaration |
| Spinoza | Necessary existence | Semantic variables |
| Husserl | Transcendence in immanence | #[Input] + #[Inject] |
| Zhuangzi | Self-testimony | $been |
| Heidegger | Language is the house of Being | Class names construct the world |
| Buddhism | Momentariness | Cessation and arising |
Log-Driven Development (LDD)
“Once Zhuang Zhou dreamt he was a butterfly… Suddenly he awoke and there he was, solid and unmistakable Zhuang Zhou. But he didn’t know if he was Zhuang Zhou who had dreamt he was a butterfly, or a butterfly dreaming he was Zhuang Zhou.” —Zhuangzi, ‘The Adjustment of Controversies’ (Qi Wu Lun) (Circa 4th Century BC)
Note: This chapter describes the “Future Vision” that Be Framework aims for. Not everything is implemented in the current version.
Ultimate Transparency
Be Framework aims for a world where there is ultimate transparency in all layers—code, execution, log, and specification—and also where the boundaries between them become ambiguous.
Reversibility Brought by Three Transparencies
The one-way flow of writing code from specifications, executing it, and outputting logs is the natural order of things (standard). However, Be Framework seeks to explore the possibility of complete “Reversibility” through the following three transparencies:
- Structural Transparency:
- The
#[Be]attribute explicitly manifests the flow of metamorphosis as the structure of the code itself. This allows drawing the entire specific Decision Graph of the application just by static analysis.
- The
- Semantic Transparency:
- Variable names are not just labels, but contracts. A name like
$emailencompasses validation by theEmailclass and philosophical definitions (like ALPS). This lets you understand the “meaning” and “guarantee” of the data just by looking at the variable name.
- Variable names are not just labels, but contracts. A name like
- Execution Transparency:
- Semantic logs are not merely “transit records” but record the entire basis of judgment “why that decision was reached”.
Infinite Loop of Value
This transparency enables the “Elimination of Guesswork” by AI.
- Log: Humans (or AI) write the “story that should be” as a log.
- AI Construction: AI assembles code not by guesswork but as “work” from that log, relying on structural and semantic transparency.
- Code: The generated code becomes the definition of Temporal Being.
- Execution: The code is executed, and a log according to the contract is output.
- Verification: Confirm that the output log matches the original story (Log) through execution transparency.
Through this cycle, a world where “Dream (Log)” and “Reality (Code)” are mutually and continuously converted is completed.
Log-Driven Development (LDD)
The new development method brought about by this reversibility is Log-Driven Development (LDD).
“Code ⇔ Log ⇔ Specification”
Just as TDD (Test-Driven Development) defines “Behavior (Doing)” and then implements it, LDD defines “Story (Log)” and then generates Existence (Code/Being).
- Narrative First: Developers first write the “Semantic Log that should be”. This is the “story” the system should trace, and is itself the DSL (Domain Specific Language) defining the application.
```yaml
UserRegistration:
- Input: {email: “New User”, …} Becomes: ValidatedUser
- ValidatedUser: Becomes: RegisteredUser ```
- Specification Generation: “Specifications (what state transitions are required)” are derived from the log.
- Code Generation: From the specifications,
#[Be]chains and class definitions to realize them are automatically generated.
Future Debugging
In a world where ultimate transparency is realized, debugging becomes synonymous with “reading logs”. Non-reproducible bugs do not exist. This is because the log is a complete “executable specification”, and just by pouring that log in, the system can Reproduce (Replay) exactly the same metamorphosis process.
Conclusion: Code as Philosophy
The “Butterfly Dream” at the beginning is an anecdote of the ancient Chinese thinker Zhuangzi.
He dreamt he was a butterfly, fluttering about… suddenly he awoke… but he didn’t know if he was Zhuang Zhou who had dreamt he was a butterfly, or a butterfly dreaming he was Zhuang Zhou.
Digital reality where logs can become code in LDD is exactly this Butterfly Dream, and Be Framework’s complete transparency makes the boundary of meaning between log and code ambiguous. Thought becomes word, word becomes code, and code becomes story.
Programming in Be Framework is not merely ordering a computer. It is an act of defining “Temporal Being” in digital space acting like life, and letting its metamorphosis spin out a meaningful story (Log).
Be Framework FAQ
0) TL;DR
Q. Is this a new “programming paradigm”?
A. A pattern is a better answer to an existing question. A paradigm changes the question itself.
$email is not validated externally — its very existence is proof of correctness. Semantic variables, self-determination, self-proof, choreography — these are not individual features but all derived from a single viewpoint: “seeing the world from the domain’s own self.”
Procedural programming asked HOW (how to do it). Object-oriented programming asked WHAT (what is it). Being-oriented programming asks WHETHER (can it even exist).
2) Core Features
Q5. What does the #[Be] attribute do?
A. It declares an object’s destiny (what it will become next).
You can specify single or multiple transformation candidates, and the framework automatically selects the continuation at runtime based on the type of the $being property.
For details, see Metamorphosis.
Q6. What’s the role of the $being property?
A. It holds the next existence that the current existence leads to.
Union types explicitly show all possible outcomes.
Q6.5. Are $being and $been special properties?
A. No, they are not special properties.
becoming() doesn’t read property names, but looks at the types declared on properties to select the next class.
Therefore, the names $being / $been are not required. public Success|Failure $result; works the same way with different names.
However, using these names according to documentation, samples, and design philosophy provides the benefit of immediately recognizing “transformation destination (being)” and “completion evidence (been)”.
Q7. How is the metamorphosis chain controlled?
A. Through the chain of #[Be] attributes, the next transformation is automatically determined.
When multiple transformation destinations are possible, they are expressed as union types in the $being property, and the next transformation destination is determined by the type actually assigned. This mechanism allows complex business logic to be expressed declaratively.
Q8. What are “Semantic Variables”?
A. Variable name = meaning + constraints. $email must be a “valid Email” to exist.
It integrates scattered validations (controller/validator/docs) into types.
See Semantic Variables for details.
Q8.5. How do you handle variables with different meanings but same constraints ($userId, $authorId, etc.)?
A. Share constraints through common base classes or traits while separating meaning through inheritance.
// src/Semantic/Abstract/Id.php - Common constraint base class
namespace App\Semantic\Abstract;
abstract readonly class Id {
public function __construct(public string $value) {
if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}/', $value)) {
throw new InvalidIdException();
}
}
}
// src/Semantic/UserId.php - Semantic variables through inheritance
readonly class UserId extends \App\Semantic\Abstract\Id {}
readonly class AuthorId extends \App\Semantic\Abstract\Id {}
// Usage: variable names carry semantic meaning
function updateArticle(UserId $userId, AuthorId $authorId) {
// $userId and $authorId cannot be confused
}
Q9. What is the “Reason Layer”?
A. It gathers the foundation — the complete tool set — for an existence to come into being, into a single object.
In traditional DI, dependencies are injected individually and scattered, making it hard to see “what are the preconditions for this existence?” Reason bundles the grounds for existence by meaning, so in tests you can swap out the entire set of preconditions at once.
See Reason Layer for details.
Q10. What is $been (self-proof) for?
A. It internally contains the trail of that existence’s completion (who, when, what).
It aligns well with external tests and audit logs.
See Final Objects for details.
4) Integration with Existing Assets
Q15. Can this be introduced to existing MVC apps?
A. Yes. Replace the Use Case layer with Be and call becoming(new …Input) from Controllers. Gradual migration is possible.
Q16. Where do you use DB or external APIs?
A. Confined to Reason. Existence types are freed from database schemas and API constraints — the definition of existence is separated from technical means.
Q17. What are the framework dependencies?
A. Core uses PHP standard + DI (e.g., Ray.Di). Integration with Laravel/Symfony etc. is possible via adapters.
Q18. Do static analysis and IDE completion work?
A. Very well since types are “states.” Branching is made explicit with union types, and completion is safe too.
6) Migration
Q21. What are the migration steps for existing code? (Minimal steps)
A.
- Choose one representative use case
- Extract input as
…Input(immanent only) - Design transformation target as
…(existence), add#[Be] - Consolidate external dependencies into Reason
- Call
becoming(new …Input)from Controller - Migrate validation to semantic variables / replace exceptions semantically
8) Examples & Snippets
Q23. What’s a minimal example of typical flow?
A.
// 1) Input (immanent only)
#[Be(ValidatedUser::class)]
final readonly class UserInput {
public function __construct(public string $name, public string $email) {}
}
// 2) Being (moment of transformation)
final readonly class ValidatedUser {
public string $display;
public bool $isValid;
public function __construct(
#[Input] string $name,
#[Input] string $email,
#[Inject] NameFormatter $fmt,
#[Inject] EmailValidator $v
) {
$this->display = $fmt->format($name);
$this->isValid = $v->validate($email);
}
}
// 3) Execution (self-organization)
$user = $becoming(new UserInput($name, $email));
// Result: ValidatedUser object
// $user->display: "Formatted name"
// $user->isValid: true (for valid email)
10) Going Deeper
Q. Where can I learn more?
A. A trilogy of neutral review articles by the high-performance LLM Fable 5 offers a multifaceted analysis. Start with the first: Three Lines of While-Loop and Fifteen Thinkers — A Be Framework Review.
Demos
Experience Be Framework concepts through working code.
Hello World Demo
The simplest metamorphosis. An Input with a name becomes a Final with a greeting.
HelloInput → Hello
(name) (greeting)
Input
#[Be([Hello::class])]
final readonly class HelloInput
{
public function __construct(
public string $name,
) {}
}
The #[Be] attribute declares what this Input can become.
Final
final readonly class Hello
{
public string $greeting;
public function __construct(
#[Input] string $name, // From HelloInput
#[Inject] Greeting $greeting, // From DI container
) {
$this->greeting = "{$greeting->greeting} {$name}";
}
}
Reason (Raison d’être)
final class Greeting
{
public string $greeting = 'Hello';
}
The name becomes a greeting by borrowing the external force of Reason. Here, Greeting provides ‘Hello’.
Usage
$input = new HelloInput(name: 'World');
$final = ($becoming)($input);
echo $final->greeting; // "Hello World"
Links
–|-|
“Be, Don’t Do”
Getting Started
Now that you’ve learned the concepts, let’s put them into practice.
Requirements
- PHP 8.4+
- Composer
Installation
composer create-project be-framework/skeleton my-project --stability dev
cd my-project
Run the Example
composer dev
Output:
Hello World
That’s it! Let’s look at the code.
Project Structure
bin/
└── be.php # CLI entrypoint
src/
├── Input/
│ └── HelloInput.php # Starting point
├── Final/
│ └── Hello.php # Destination
├── Reason/
│ └── Greeting.php # Transcendent capability
├── Semantic/
│ └── Name.php # Semantic variables
├── Exception/
│ └── EmptyNameException.php
└── Module/
└── AppModule.php # DI configuration
The Code
Input Class
#[Be([Hello::class])]
final readonly class HelloInput
{
public function __construct(
public string $name
) {}
}
The #[Be] attribute declares the destiny—what this input will become.
Final Class
final readonly class Hello
{
public string $greeting;
public function __construct(
#[Input] string $name,
#[Inject] Greeting $greeting,
) {
$this->greeting = "{$greeting->greeting} {$name}";
}
}
#[Input]receives data from the previous stage (HelloInput)#[Inject]receives Transcendence (capability from outside)
Reason Class
final class Greeting
{
public string $greeting = 'Hello';
}
The Greeting provides the transcendent capability—the power to greet.
Executing Metamorphosis
$injector = new Injector(new AppModule());
$becoming = new Becoming($injector, 'Be\\App\\Semantic');
$input = new HelloInput('World');
$hello = $becoming($input);
echo $hello->greeting; // "Hello World"
What Just Happened?
HelloInput('World')
↓ Becoming executes
Hello (with Greeting injected)
→ "Hello World"
The input didn’t “do” anything—it became Hello through metamorphosis.
Try Semantic Validation
Edit bin/be.php to pass an empty name:
$input = new HelloInput('');
Run again:
composer dev
You’ll see an error message because Semantic/Name.php validates that name cannot be empty.
Key Concepts Demonstrated
| Concept | In This Example |
|---|---|
| Immanence | $name in HelloInput |
| Transcendence | Greeting injected via #[Inject] |
| Metamorphosis | HelloInput → Hello transformation |
| Semantic Validation | Name.php validates input |
Next Steps
Ready for a more complete example with Being classes and branching? Continue to Tutorial →
Or revisit the concepts:
- Input Classes - Starting points
- Final Objects - Destinations
- Semantic Variables - Domain ontology
Be Framework Manual
Understanding existence through transformation
Overview
New Paradigm: First encounter with being-oriented programming
Input Classes
Starting point of transformation - pure Immanent nature
Being Classes
Intermediate transformations through Immanent + Transcendent interactions
Final Objects
The destination of metamorphosis - complete transformed beings
Metamorphosis
Inseparability of time and domain, self-determination of destiny
Semantic Variables
Domain-specific validation and ontological type safety
Reason Layer
Raison d’être and the reasons that enable object existence
Semantic Exceptions
Semantic exceptions and multilingual error messages
Reference
Essential resources and links for framework development
FAQ
Frequently asked questions and answers
“Be, Don’t Do”
Tutorial: Emergency Triage
Build a triage system where vital signs determine a patient’s existence as “emergency” or “observation.”
Prerequisites
- Complete Getting Started
- PHP 8.4+
- Basic understanding of Be Framework overview
Introduction
In this tutorial, we’ll build an emergency triage system that demonstrates the core philosophy of Be Framework: objects don’t DO things—they BECOME things.
A patient doesn’t “get triaged.” They become an emergency case or an observation case, based on the transcendent wisdom of medical protocol.
The Metamorphosis
PatientArrival (raw vital signs)
↓ JTAS Protocol assesses
TriageAssessment (the chrysalis stage)
↓ Destiny is determined
EmergencyCase or ObservationCase (final existence)
Step 1: Define the Ontology
Before writing logic, we define our domain ontology—the vocabulary of what can exist in this domain.
BodyTemperature
// src/Semantic/BodyTemperature.php
/**
* Below 30°C or above 45°C, a human cannot survive.
* Such values are rejected at the semantic level.
*/
final class BodyTemperature
{
#[Validate]
public function validate(float $bodyTemperature): void
{
if ($bodyTemperature < 30.0 || $bodyTemperature > 45.0) {
throw new LethalVitalException();
}
}
}
HeartRate
// src/Semantic/HeartRate.php
/**
* Below 20 or above 250 bpm indicates cardiac arrest or lethal arrhythmia.
*/
final class HeartRate
{
#[Validate]
public function validate(int $heartRate): void
{
if ($heartRate < 20 || $heartRate > 250) {
throw new LethalVitalException();
}
}
}
These aren’t just validation rules. They define your domain ontology—the vocabulary of what can exist. This declarative foundation serves as documentation that both humans and AI can read to understand your domain. (See Semantic Variables for details.)
Step 2: Define Exceptions
When vital signs indicate non-survivable conditions, the patient’s existence is rejected:
// src/Exception/LethalVitalException.php
#[Message([
'en' => 'Vital signs indicate non-survivable conditions.',
'ja' => 'バイタルサインが生存不可能な状態を示しています。'
])]
final class LethalVitalException extends \DomainException
{
}
Step 3: Define the Reason (Transcendence)
The JTASProtocol (Japan Triage and Acuity Scale) is not a programmer’s arbitrary rule. It represents transcendent medical wisdom—objective knowledge that exists independently in the world. In Be Framework, such domain logic becomes a first-class citizen: injectable, testable, and explicitly visible.
// src/Reason/JTASProtocol.php
/**
* JTAS (Japan Triage and Acuity Scale) Protocol
*
* A transcendent medical wisdom that exists independently
* of any individual patient or programmer.
*
* Note: Simplified. Real JTAS has 5 levels.
*/
final readonly class JTASProtocol
{
/** @return 'emergency'|'observation' */
public function assess(float $bodyTemperature, int $heartRate): string
{
if ($bodyTemperature >= 39.0 || $heartRate >= 120) {
return 'emergency';
}
return 'observation';
}
}
This is the Reason—the external force that enables metamorphosis. Just as a caterpillar needs environmental conditions to become a butterfly, our data needs the JTASProtocol to become a triaged patient.
Step 4: Create Input Class
The starting point—raw vital signs at the moment of arrival:
// src/Input/PatientArrival.php
#[Be([TriageAssessment::class])]
final readonly class PatientArrival
{
public function __construct(
public float $bodyTemperature,
public int $heartRate
) {}
}
The #[Be] attribute declares destiny: this arrival WILL BECOME a TriageAssessment.
Step 5: Create Reason Objects
These Reason objects represent the two possible destinies. The Reason determines the destiny—”this is it”:
// src/Reason/Emergency.php
final readonly class Emergency {}
// src/Reason/Observation.php
final readonly class Observation {}
These aren’t empty classes—they ARE the distinction. An Emergency is fundamentally different from an Observation. The type itself carries meaning. When assigned to $being, the Reason’s type determines which Final class comes next.
Step 6: Create Being Class
This is where metamorphosis happens. The patient is in a liminal state—their final form not yet determined:
// src/Being/TriageAssessment.php
/**
* Raw vital signs meet the JTAS Protocol (transcendent wisdom)
* and the patient's destiny is determined.
*/
#[Be([EmergencyCase::class, ObservationCase::class])]
final readonly class TriageAssessment
{
public Emergency|Observation $being;
public function __construct(
#[Input] public float $bodyTemperature,
#[Input] public int $heartRate,
#[Inject] JTASProtocol $protocol
) {
$urgency = $protocol->assess($bodyTemperature, $heartRate);
$this->being = ($urgency === 'emergency')
? new Emergency()
: new Observation();
}
}
#[Inject] brings in the JTASProtocol—transcendent wisdom from outside. The $being property (Union type) determines which Final class receives the transformation. We don’t “set status”—the patient BECOMES their destiny.
Step 7: Create Final Classes
The final forms—each with unique capabilities:
EmergencyCase
// src/Final/EmergencyCase.php
/**
* A patient who EXISTS as highest priority.
* This is not just a status flag. This patient IS an emergency.
* Their type grants them capabilities that others don't have.
*/
final readonly class EmergencyCase
{
public string $priority;
public string $color;
public function __construct(
#[Input] public float $bodyTemperature,
#[Input] public int $heartRate,
#[Input] public Emergency $being
) {
$this->priority = 'IMMEDIATE';
$this->color = 'RED';
}
/**
* Only an EmergencyCase can demand ER assignment
*/
public function assignER(): string
{
return "Secure ER Room 1 immediately. Summon emergency physician.";
}
}
ObservationCase
// src/Final/ObservationCase.php
/**
* A patient who EXISTS as stable.
* They can safely wait while emergency cases are handled.
*/
final readonly class ObservationCase
{
public string $priority;
public string $color;
public function __construct(
#[Input] public float $bodyTemperature,
#[Input] public int $heartRate,
#[Input] public Observation $being
) {
$this->priority = 'DELAYED';
$this->color = 'GREEN';
}
/**
* Observation cases are assigned to waiting area
*/
public function assignWaitingArea(): string
{
return "Move to waiting area. Monitor vitals every 30 minutes.";
}
}
Each type has different methods. EmergencyCase can assignER(), while ObservationCase can assignWaitingArea(). Type determines capability—you cannot assign an ER room to an observation patient.
Step 8: Execute the Metamorphosis
// bin/be.php
use Be\App\Input\PatientArrival;
use Be\App\Module\AppModule;
use Be\Framework\Becoming;
use Ray\Di\Injector;
$injector = new Injector(new AppModule());
$becoming = new Becoming($injector, 'Be\\App\\Semantic');
// High fever patient
$patient = new PatientArrival(bodyTemperature: 39.5, heartRate: 90);
$final = $becoming($patient);
echo $final->priority; // "IMMEDIATE"
echo $final->color; // "RED"
echo $final->assignER(); // "Secure ER Room 1 immediately..."
Temporal Existence
All existence flows through time—from Input through Being to Final.
PatientArrival(39.5°C, 90 bpm)
↓ #[Be([TriageAssessment::class])]
TriageAssessment
├─ JTASProtocol->assess() returns 'emergency'
└─ $being = Emergency
↓ #[Be([EmergencyCase::class, ObservationCase::class])]
EmergencyCase (because $being is Emergency)
→ priority: IMMEDIATE
→ color: RED
→ assignER(): "Secure ER Room 1..."
Handling Non-Survivable Existence
// Temperature outside survivable range
$invalid = new PatientArrival(bodyTemperature: 50.0, heartRate: 80);
try {
$becoming($invalid);
} catch (SemanticVariableException $e) {
echo $e->getErrors()->getMessages('en')[0];
// "Vital signs indicate non-survivable conditions."
}
The metamorphosis is rejected. A patient with lethal vital signs cannot exist in our system.
Why This Matters
Traditional Approach (Doing)
$patient = new Patient($temp, $hr);
if ($triageService->isEmergency($patient)) {
$patient->setStatus('emergency');
$this->erService->assign($patient);
}
Problems:
- Patient can exist in invalid state
- Status can be changed at any time
erService->assign()can be called on any patient
Be Framework Approach (Being)
$patient = new PatientArrival($temp, $hr);
$final = $becoming($patient);
// $final IS an EmergencyCase or ObservationCase
// Only EmergencyCase has assignER() method
$final->assignER(); // Type-safe: only possible for EmergencyCase
Benefits:
- Non-survivable states cannot exist
- Type IS the status (immutable)
- Capabilities belong to existence
Type determines capability. Existence precedes action.
Project Structure
src/
├── Being/
│ └── TriageAssessment.php # Intermediate stage
├── Exception/
│ └── LethalVitalException.php
├── Input/
│ └── PatientArrival.php # Entry point
├── Module/
│ └── AppModule.php # DI configuration
├── Final/
│ ├── EmergencyCase.php # Final form: emergency
│ └── ObservationCase.php # Final form: observation
├── Reason/
│ ├── Emergency.php # Reason: determines destiny
│ ├── JTASProtocol.php # Reason: transcendent wisdom
│ └── Observation.php # Reason: determines destiny
└── Semantic/
├── BodyTemperature.php # What CAN exist
└── HeartRate.php
Key Insight
The patient doesn’t “get triaged”—they become a triaged state. EmergencyCase and ObservationCase are different types with different capabilities. Once transformed, status cannot change without new metamorphosis.
All existence is relational, temporal, ever-becoming. Immanence meets transcendence. New being emerges. “To be is to become.”
Metamorphosis in Other Domains
The same pattern applies everywhere:
| Domain | Input | Being | Final | Reason |
|---|---|---|---|---|
| Triage | PatientArrival | TriageAssessment | Emergency/Observation | JTASProtocol |
| Brewing | RawMaterials | Fermentation | PremiumSake/Vinegar | YeastCulture |
| Immigration | VisaApplication | ConsularReview | Resident/Visitor | ImmigrationLaw |
| Justice | Evidence | Trial | Guilty/Acquitted | PenalCode |
| Stellar | GasCloud | Protostar | Star/BlackHole | PhysicsLaws |
Every domain has its metamorphosis. Every existence has its reason.
Next Steps
- Semantic Variables - Deep dive into semantic validation
- Metamorphosis - Metamorphosis and branching patterns
- Reason Layer - Understanding transcendence