- ✅ .NET 10 — минимальная версия SDK.
- ✅ Avalonia 12.0.0 — обновлена базовая версия Avalonia UI.
- ✅ PeachPie 1.1.13 — обновлены PeachPie SDK/runtime зависимости.
- ✅ Шаблоны
dotnet newтеперь создают проекты подnet10.0и ссылаются на пакетыPeachpie.Avaloniaверсии1.0.4. - ✅ XAML обновлён под Avalonia 12: используется
PlaceholderText/UseFloatingPlaceholderвместо устаревшихWatermark/UseFloatingWatermark. - ✅ События: основной способ подписки —
.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 10 и Avalonia 12.
Текущий стек:
| Компонент | Версия |
|---|---|
| .NET SDK / Target Framework | 10 / net10.0 |
| Avalonia | 12.0.0 |
| PeachPie | 1.1.13 |
| Peachpie.Avalonia | 1.0.4 |
- Установите .NET 10 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При успешном запуске откроется окно шаблонного приложения.
PeachPie SDK 1.1.13 поставляет compiler tool под .NET 6. Проект автоматически включает roll-forward для этого tool во время сборки, поэтому отдельная установка .NET 6 runtime не требуется.
Используйте .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- ✅ Начиная с
1.0.4, проекты ориентированы на.NET 10,Avalonia 12.0.0иPeachPie 1.1.13. - ✅ В XAML для Avalonia 12 используйте
PlaceholderTextиUseFloatingPlaceholder. - ❌ Обёртки
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()=> /* … */ );