- ✅ .NET 9 — минимальная версия SDK.
- ✅ События: основной способ подписки —
.NET-подобный
через->add(callable)
/Hook->close()
. Генератор заглушек добавляет понятные подсказки с сигнатурой обработчика. - ✅ UX-обёртки над контролами удалены (
UxButton
,UxWindow
, …) — используйте оригинальныеAvalonia\Controls\*
. - 🧪 Дополнительный (экспериментальный) способ подписки через
Peachpie\Avalonia\Ux\Ux
сохранён как удобный синтаксический сахар. - 🧩 Генератор заглушек: копирует stubs из NuGet-пакетов и генерирует PHP-заглушки по .NET-типам для автодополнения IDE.
Peachpie.Avalonia — библиотека, позволяющая создавать кроссплатформенные приложения (Windows, macOS, Linux и др.) на PHP в среде .NET с использованием Avalonia UI.
Особенности:
- Полная совместимость с .NET — PHP код компилируется в IL через PeachPie.
- Двустороннее взаимодействие — свободно комбинируйте PHP и C#.
- Кроссплатформенность — всё, где работает .NET 9 и Avalonia.
- Установите .NET 9 SDK
- Поставьте шаблоны:
dotnet new uninstall Peachpie.Avalonia.Templates # удалите принудительно устаревшую версию шаблонов.
dotnet new install Peachpie.Avalonia.Templates
- Создайте приложение:
dotnet new php.avalonia.app -o MyApp
cd MyApp
dotnet restore
dotnet msbuild -t:PeachpieStubs # восстановит vendor-stubs и сгенерирует IDE-заглушки в vendor/Stubs
dotnet run
При успешном запуске откроется окно шаблонного приложения.
Используйте .NET
-подобную модель через \Pchp\Core\ClrEvent
:
use Avalonia\Controls\Button;
$button = new Button();
$hook = $button->Click->add(function (object $sender, \Avalonia\Interactivity\RoutedEventArgs $e): void {
// обработчик
});
// Отписаться:
$hook->close(); // или ->dispose()
Генератор заглушек прописывает в PHPDoc точную сигнатуру коллбэка (тип
EventArgs
, имена параметров), что даёт корректные подсказки IDE.
Синтаксический сахар через Peachpie\Avalonia\Ux\Ux
:
use Avalonia\Controls\Button;
use Peachpie\Avalonia\Ux\Ux;
use Php\Output\Logger;
$button = new Button();
Ux::of($button)->onClick(fn($s, $e) => Logger::Info("Клик!"));
Ux::of($button)->onceClick(fn() => Logger::Info("Только один раз"));
Ux::of($button)->offClick(); // снять все обработчики Click
Ux::on($button, ['Click', 'PointerPressed'], fn() => Logger::Info("Множественная подписка"));
Этот способ удобен, но носит статус дополнительного/тестируемого. Базовым остаётся
->add(callable)
.
IDE-заглушки (stubs) не участвуют в компиляции/рантайме, а служат для автодополнения, подсветки типов и навигации.
Механизм делает два шага:
- Копирует готовые PHP-stubs из подключённых NuGet-пакетов (их папка
vendor
). - Генерирует stubs по публичным .NET типам (без generic-конструкций), включая:
- классы/интерфейсы/свойства/методы;
- события как
@var \Pchp\Core\ClrEvent $Name
с документированной сигнатуройcallback
; - перегрузки методов в PHPDoc (
.NET overloads
).
Все файлы складываются в <проект>/vendor/Stubs
и используются IDE для подсказок.
Запуск вручную:
dotnet msbuild -t:PeachpieStubs
- ❌ Обёртки
Ux*
над контролами удалены. - ✅ Используйте оригинальные
Avalonia\Controls\*
и подписку на события через->add(callable)
. - 🧪 Хелпер
Peachpie\Avalonia\Ux\Ux
можно применять как дополнительный синтаксический сахар.
Было (устаревшее) | Стало (актуально) |
---|---|
use Peachpie\Avalonia\Controls\UxButton; |
use Avalonia\Controls\Button; |
$btn = new UxButton(); |
$btn = new Button(); |
$btn->on('Click', fn()=>...); |
$hook = $btn->Click->add(fn()=>...); |
use Avalonia\Controls\Button;
$button = new Button();
$button->Content = "Нажми меня";
$count = 0;
$hook = $button->Click->add(function(object $s, \Avalonia\Interactivity\RoutedEventArgs $e) use (&$count, $button) {
$count++;
$button->Content = "Нажато: $count";
});
use Avalonia\Controls\{Window, StackPanel, Button, TextBlock};
$window = new Window();
$panel = new StackPanel();
$txt = new TextBlock();
$txt->Text = "Hello PeachPie Avalonia!";
$btn = new Button();
$btn->Content = "Click me";
$btn->Click->add(function() use ($txt) {
$txt->Text = "Clicked!";
});
$panel->Children->Add($txt);
$panel->Children->Add($btn);
$window->Content = $panel;
$window->Show();
XAML (пример):
<!-- MyView.axaml -->
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.Views.MyView"
Name="PageView">
<StackPanel>
<TextBlock Name="text1" Text="Hello!" />
<Button Name="btnOk" Content="OK" />
</StackPanel>
</UserControl>
PHP:
<?php
declare(strict_types=1);
namespace MyApp\Views;
use Avalonia\Markup\Xaml\AvaloniaXamlLoader;
use Avalonia\Controls\UserControl;
use Avalonia\Controls\TextBlock;
use Avalonia\Controls\Button;
use Peachpie\Avalonia\Ux\Ux;
class MyView extends UserControl
{
/** @var TextBlock */
public $text1;
/** @var Button */
public $btnOk;
/** @var UserControl */
public $PageView;
public function __construct()
{
// Важно для PeachPie: именованный параметр, чтобы вызвать одноаргументную перегрузку
AvaloniaXamlLoader::Load(obj: $this);
// Поиск по имени из .axaml — через обёртку Ux::find():
$this->PageView = Ux::find($this, "PageView");
$this->text1 = Ux::find($this, "text1");
$this->btnOk = Ux::find($this, "btnOk");
$this->btnOk->Click->add(fn() => $this->text1->Text = "OK clicked");
}
}
use Peachpie\Avalonia\Ux\Ux;
use Avalonia\Controls\Button;
$btn = new Button();
Ux::on($btn, ['Click', 'PointerPressed'], fn()=> /* … */ );