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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 18 additions & 21 deletions agents/framework/Web/UI/IAdapterControl.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,39 @@
### Directories
[framework](../../INDEX.md) / [Web](../INDEX.md) / [UI](./INDEX.md) / **`IAdapterControl`**

## Class Info
## Interface Info
**Location:** `framework/Web/UI/IAdapterControl.php`
**Namespace:** `Prado\Web\UI`
**Since:** 4.3.3

## Overview
Interface defining the contract for objects returned by `TControl::getAdapterControl()`. That method returns either the control itself or its attached `TControlAdapter`. Both `TControl` and `TControlAdapter` implement this interface so the framework can dispatch lifecycle calls through a single, uniform pointer without knowing whether an adapter is present.

All lifecycle methods, the render entry-point, and state hooks are called through this interface during the page request cycle.
Common contract for the object returned by `TControl::getAdapterControl()`. That method returns either the control itself or its `TControlAdapter` when one is set, so both classes implement this interface. Lifecycle methods, the render entry-point, and state hooks are all called through it, letting `TControl` dispatch to either the adapter or itself with a single typed call.

## Interface Methods
## Methods

| Method | Description |
|---|---|
| `createChildControls()` | Creates child controls |
| `onInit($param)` | Called at the `OnInit` lifecycle stage |
| `onLoad($param)` | Called at the `OnLoad` lifecycle stage |
| `onPreRender($param)` | Called at the `OnPreRender` lifecycle stage |
| `onUnload($param)` | Called at the `OnUnload` lifecycle stage |
| `render(THtmlWriter $writer)` | Renders the control |
| `onInit($param)` | Invoked at the `OnInit` lifecycle stage |
| `onLoad($param)` | Invoked at the `OnLoad` lifecycle stage |
| `onPreRender($param)` | Invoked at the `OnPreRender` lifecycle stage |
| `onUnload($param)` | Invoked at the `OnUnload` lifecycle stage |
| `render($writer)` | Renders the control to `$writer` |
| `loadState()` | Loads additional persistent control state |
| `saveState()` | Saves additional persistent control state |

## Implementors

- **`TControl`** — implements it directly; `getAdapterControl()` returns `$this` when no adapter is set.
- **`TControlAdapter`** — base adapter class; each method delegates to the attached control by default. Subclasses override only what they need.
- **[`TControl`](TControl.md)** — satisfies all methods natively; `getAdapterControl()` returns `$this` when no adapter is set.
- **[`TControlAdapter`](TControlAdapter.md)** — provides pass-through implementations that delegate to the attached control; subclasses override only what they customise.

## Patterns & Gotchas
## Usage

- **Purpose of `getAdapterControl()`** — `TControl::initRecursive`, `loadRecursive`, `preRenderRecursive`, `unloadRecursive`, `renderControl`, and the state hooks all route through `getAdapterControl()` so that an adapter transparently intercepts any or all of these stages.
- **Attach via `TControl::setAdapter()`** — once an adapter is attached, `getAdapterControl()` returns it instead of the control itself.
- **Adding adapters post-init** — setting an adapter after the control has been initialized can result in lifecycle methods being called on the adapter for stages the control has already passed.
```php
// Inside TControl — getAdapterControl() is protected; returns IAdapterControl
$this->getAdapterControl()->onPreRender($param);
$this->getAdapterControl()->render($writer);
```

## See Also

- [TControlAdapter](./TControlAdapter.md)
- [TControl](./TControl.md)

**@since 4.3.3**
Application code never calls `getAdapterControl()` directly; it is used exclusively inside `TControl`'s own lifecycle and rendering methods.
47 changes: 22 additions & 25 deletions agents/framework/Web/UI/IFilterRenderable.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,46 @@
### Directories
[framework](../../INDEX.md) / [Web](../INDEX.md) / [UI](./INDEX.md) / **`IFilterRenderable`**

## Class Info
## Interface Info
**Location:** `framework/Web/UI/IFilterRenderable.php`
**Namespace:** `Prado\Web\UI`
**Extends:** `IRenderable`
**Since:** 4.3.3

## Overview
Interface that marks a control as supporting render-output filtering via the `onRenderFilter` event. Extends `IRenderable`.

`TControl::renderControl` and `TControl::renderChildren` detect this interface and automatically handle the capture-and-restore lifecycle: when at least one `onRenderFilter` handler is registered, the writer's inner `ITextWriter` is swapped for a fresh buffer, `render()` runs into that buffer, and the captured HTML is then passed through `onRenderFilter` handlers before being written to the real output.
Marks a control as supporting render-output filtering via the `onRenderFilter` event. `TControl::renderControl` and `TControl::renderChildren` detect this interface and automatically wrap the render call in a capture-and-restore filter lifecycle (`preRenderFilter` / `processRenderFilter`). Implement using [`TFilterRenderableTrait`](Traits/TFilterRenderableTrait.md).

Implement using [TFilterRenderableTrait](./Traits/TFilterRenderableTrait.md).

## Interface Methods
## Methods

| Method | Description |
|---|---|
| `hasEventHandler(string $name): bool` | Returns whether at least one handler is registered for the named event. Required so `TControl::preRenderFilter` can test `onRenderFilter` without assuming the object is a `TComponent`. |
| `onRenderFilter(string $renderedText): string` | Raises the `onRenderFilter` event via a [TRenderFilterParameter](./TRenderFilterParameter.md), passing captured HTML through all registered handlers, and returns the (possibly modified) string. |
| `hasEventHandler($name)` | Returns whether at least one handler is registered for the named event. Required so `TControl::preRenderFilter` can test the event without assuming a `TComponent` base. |
| `onRenderFilter($output)` | Raises the `onRenderFilter` event, passes HTML through handlers via a [`TRenderFilterParameter`](TRenderFilterParameter.md), returns the (possibly modified) HTML string. |

Inherits `render(THtmlWriter $writer)` from `IRenderable`.
## Implementation

## How to Implement
Implement the interface by using [`TFilterRenderableTrait`](Traits/TFilterRenderableTrait.md), which provides `onRenderFilter`. `hasEventHandler` is satisfied by `TComponent`, which all practical implementors extend:

```php
use Prado\Web\UI\IFilterRenderable;
use Prado\Web\UI\Traits\TFilterRenderableTrait;

class MyControl extends TCompositeControl implements IFilterRenderable
class MyControl extends TComponent implements IFilterRenderable
{
use TFilterRenderableTrait;
// onRenderFilter() and hasEventHandler() are provided by the trait + TComponent

public function render($writer): void
{
$writer->write('<p>content</p>');
}
}
```

## Patterns & Gotchas

- **No-op when no handlers** — `preRenderFilter` calls `hasEventHandler('onRenderFilter')` and returns `null` (no buffer swap) when the result is false. Zero overhead for controls without handlers.
- **`TControl` itself implements this** — `TControl` implements `IFilterRenderable` via `TFilterRenderableTrait`, so all controls can receive `onRenderFilter` handlers out of the box.
- **Non-`TControl` implementors** — `renderChildren` also checks `IFilterRenderable` on non-`TControl` children (plain `IRenderable` objects), applying the same lifecycle when they implement this interface.
`TControl` already implements `IFilterRenderable` — no extra work is needed for controls that extend it.

## See Also
## Filter lifecycle

- [TFilterRenderableTrait](./Traits/TFilterRenderableTrait.md)
- [TRenderFilterParameter](./TRenderFilterParameter.md)
- [TControl](./TControl.md)
When `TControl::renderChildren` encounters a non-`TControl` child that implements `IFilterRenderable`:
1. `preRenderFilter($writer, $child)` — checks `$child->hasEventHandler('onRenderFilter')`. If true, swaps the writer's inner buffer and saves the original.
2. `$child->render($writer)` — renders into the capture buffer.
3. `processRenderFilter($writer, $oldWriter, $child)` — flushes the buffer, calls `$child->onRenderFilter($output)`, writes the result to the original writer, and restores it.

**@since 4.3.3**
If no handler is registered, all three steps are no-ops and output goes directly to the writer.
157 changes: 70 additions & 87 deletions agents/framework/Web/UI/TPage.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,86 +24,72 @@ TPage is the base class for all web pages in PRADO framework. It extends [TTempl
- `Head` ([THead](./WebControls/THead.md)): Page header element
- `Validators` ([TList](../../Collections/TList.md)): Collection of registered validators
- `Theme` ([TTheme](./TTheme.md)): Page theme for styling
- `StyleSheet` ([TTheme](./TTheme.md)): Page stylesheet theme
- `StyleSheetTheme` ([TTheme](./TTheme.md)): Page stylesheet theme
- `ClientScript` ([TClientScriptManager](./TClientScriptManager.md)): Manages client-side scripts
- `Title` (string): Page title, held until a THead is set on the page
- `PagePath` (string): Path to the current page
- `IsPostBack` (bool): Whether the request is a postback (read-only)
- `IsCallback` (bool): Whether the request is a callback (read-only)
- `StatePersisterClass` / `StatePersister` ([IPageStatePersister](./IPageStatePersister.md)): Where page state is stored
- `EnableStateValidation` (bool): Whether page state should be HMAC validated
- `EnableStateEncryption` (bool): Whether page state should be encrypted
- `EnableStateEncryption` (bool): Whether page state should be encrypted
- `EnableStateCompression` (bool): Whether page state should be compressed
- `EnableJavaScript` (bool): Whether client supports JavaScript
- `Focus` (string|[TControl](./TControl.md)): Control or element to be focused on page load
- `EnableStateIGBinary` (bool): Whether page state uses the igbinary serializer when available
- `ClientSupportsJavaScript` (bool): Whether client supports JavaScript
- `Focus` (string|[TControl](./TControl.md)): Control or element to be focused on page load (write-only)
- `CallbackClient` ([TCallbackClientScript](./ActiveControls/TCallbackClientScript.md)): Client-side commands for a callback response

## Core Methods

### Page Lifecycle
- `initRecursive()`: Initializes page and child controls
- `loadRecursive()`: Loads page and child controls
- `preRenderRecursive()`: Pre-renders page and child controls
- `unloadRecursive()`: Unloads page and child controls
- `saveState()`: Saves page state to persister
- `run($writer)`: Entry point called by [TPageService](../Services/TPageService.md)
- `processNormalRequest($writer)`, `processPostBackRequest($writer)`, `processCallbackRequest($writer)`: The three life cycles
- `onPreInit()`, `onInitComplete()`, `onPreLoad()`, `onLoadComplete()`, `onPreRenderComplete()`, `onSaveStateComplete()`: Page-only life cycle events
- `flushWriter()`: Flushes the content rendered so far to the response

### Form Management
- `getForm()`: Gets form instance
- `setForm()`: Sets form instance
- `setFocus($control)`: Sets focus to a control
- `getFocus()`: Gets focus control or element ID
- `renderForm()`: Renders HTML form
- `getForm()` / `setForm($form)`: The single [TForm](./TForm.md) of the page
- `getHead()` / `setHead($head)`: The single [THead](./WebControls/THead.md) of the page
- `setFocus($control)`: Sets focus to a control or element ID
- `ensureRenderInForm($control)`: Throws when a control renders outside the form
- `getInFormRender()`, `beginFormRender($writer)`, `endFormRender($writer)`: Form render state, invoked by TForm

### Validation
- `getValidators()`: Gets list of registered validators
- `registerValidator($validator)`: Registers a validator
- `unregisterValidator($validator)`: Unregisters a validator
- `validate()`: Performs page validation
- `getIsValid()`: Checks if page is valid
- `getValidators($validationGroup = null)`: Gets the [TList](../../Collections/TList.md) of registered validators; validators add and remove themselves
- `validate($validationGroup = null)`: Performs page validation
- `getIsValid()`: Whether the input is valid; throws when `validate()` has not run

### State Management
- `getPageStatePersister()`: Gets page state persister instance
- `loadPageState()`: Loads page state from request
- `savePageState()`: Saves page state to response
- `getPageState()`: Gets page state data
- `getControlState()`: Gets control state data for page
- `setStateValidation()`: Sets page state validation
- `getStateValidation()`: Gets page state validation
- `setViewState()`: Sets viewstate data for page
- `getViewState()`: Gets viewstate data for page

### Event Handling
- `raisePostBackEvent($sender, $param)`: Raises postback event
- `onLoadPostData()`: Handles loading postback data
- `onLoad()`: Raises OnLoad event
- `onPreRender()`: Raises OnPreRender event
- `onUnload()`: Raises OnUnload event
- `getStatePersister()`: Gets page state persister instance
- `loadPageState()` / `savePageState()`: Reads and writes the page state through the persister
- `saveState()` / `loadState()`: Adds the controls requiring post data to the page's own state
- `getClientState()` / `setClientState($state)`: State to be written to the client
- `getRequestClientState()`: State posted back from the client

### Postback & Data Handling
- `getIsPostBack()`, `getIsCallback()`: Request type
- `processPostData($postData, $beforeLoad)`: Dispatches post data to the controls
- `registerRequiresPostData($control)`: Registers a control to load post data on the next postback
- `getIsLoadingPostData()`: Whether post data is being loaded
- `getPostBackEventTarget()` / `setPostBackEventTarget($control)`: Control raising the postback event
- `getPostBackEventParameter()` / `setPostBackEventParameter($value)`: Postback event parameter
- `raiseChangedEvents()`: Raises OnPostDataChanged for the controls whose data changed
- `isSystemPostField($field)`: Whether a post field is one of the framework `FIELD_*` fields

### Callbacks
- `getCallbackClient()`: Client-side script handler for the callback response
- `getCallbackEventTarget()` / `setCallbackEventTarget($control)`: Control raising the callback event
- `getCallbackEventParameter()` / `setCallbackEventParameter($value)`: JSON decoded callback parameter

### Theme Management
- `getTheme()`: Gets page theme
- `setTheme($value)`: Sets page theme
- `getStyleSheet()`: Gets page stylesheet
- `setStyleSheet($value)`: Sets page stylesheet
- `applyControlSkin()`: Applies skin to controls

### Client Script Management
- `getClientScript()`: Gets client script manager
- `registerClientScript()`: Registers client script
- `registerStyleSheet()`: Registers CSS stylesheet
- `addStyleSheet()`: Adds CSS stylesheet
- `getJavaScript()`: Gets JavaScript object
- `getTheme()` / `setTheme($value)`: Gets and sets the page theme
- `getStyleSheetTheme()` / `setStyleSheetTheme($value)`: Gets and sets the page stylesheet theme
- `applyControlSkin($control)` / `applyControlStyleSheet($control)`: Applies a skin to a control

### Postback & Data Handling
- `getPostBackEventTarget()`: Gets control that raised postback event
- `setPostBackEventTarget()`: Sets control that raised postback event
- `getPostBackEventParameter()`: Gets postback event parameter
- `setPostBackEventParameter()`: Sets postback event parameter
- `getPostBackData()`: Gets postback data
- `setPostBackData()`: Sets postback data
- `getHasChanged()`: Checks if page data has changed

### Rendering
- `render()`: Renders page to HTML output
- `renderChildren()`: Renders page child controls
- `renderFormContent()`: Renders form content
- `renderHead()`: Renders page head element
- `renderClientScript()`: Renders client-side scripts
- `renderPostData()`: Renders postback data fields
### Client Script and Caching
- `getClientScript()`: Gets the client script manager, which registers scripts, stylesheets and hidden fields
- `getCachingStack()`: Stack of the active [TOutputCache](./WebControls/TOutputCache.md) controls
- `registerCachingAction($context, $funcName, $funcParams)`: Records an action to replay when cached content is served

## Constants
- `FIELD_POSTBACK_TARGET`: System postback target field name
Expand All @@ -114,35 +100,32 @@ TPage is the base class for all web pages in PRADO framework. It extends [TTempl
- `FIELD_CALLBACK_PARAMETER`: System callback parameter field name

## Page Lifecycle Stages
1. **Init**: Page and control initialization
2. **Load**: Loading of page state and postback data
3. **PreRender**: Page preparation for rendering
4. **Render**: Actual HTML output generation
5. **Unload**: Page cleanup
`onPreInit` -> `initRecursive` -> `onInitComplete` -> `loadPageState` *(POST/Callback)* -> `processPostData` *(POST/Callback)* -> `onPreLoad` -> `loadRecursive` -> `processPostData` *(POST/Callback)* -> `raiseChangedEvents` *(POST/Callback)* -> `raisePostBackEvent` *(POST-only)* -> `processCallbackEvent` *(Callback-only)* -> `onLoadComplete` -> `preRenderRecursive` -> `onPreRenderComplete` -> `savePageState` -> `onSaveStateComplete` -> `renderControl` *(GET/POST)* / `renderCallbackResponse` *(Callback-only)* -> `unloadRecursive`

## Validation Flow
1. Validators are registered during Init lifecycle
2. Validation is triggered in PreRender lifecycle
3. Page is validated in Validate method
4. Validation results are stored in IsValid property
1. A validator adds itself to `Validators` when it is initialized, and removes itself when it is unloaded
2. A button with `CausesValidation` calls `validate($validationGroup)` from its `raisePostBackEvent()`
3. `raisePostBackEvent()` on the page validates when no control is registered as the event target
4. `getIsValid()` reports the outcome and throws when `validate()` has not run

## Usage Example
```php
// Create page with form
$page = new TPage();
$form = new TForm();
$page->setForm($form);

// Register validator
$validator = new TRequiredFieldValidator();
$page->registerValidator($validator);

// Process page
$page->initRecursive();
$page->loadRecursive();
if ($page->validate()) {
$page->preRenderRecursive();
$page->render($writer);
class HomePage extends TPage
{
public function onLoad($param)
{
parent::onLoad($param);
if (!$this->getIsPostBack()) {
$this->setTitle('Home');
}
}

public function buttonClicked($sender, $param)
{
if ($this->getIsValid()) {
// handle the click
}
}
}
```

Expand Down
Loading
Loading