1:1 PHP bindings of GTK 4 and the GLib main loop it runs on, as functions and classes named after their C counterparts
Package info
github.com/php-io-extensions/gtk
Language:C
Type:php-ext
Ext name:ext-gtk
pkg:composer/php-io-extensions/gtk
Requires
- php: >=8.4
Requires (Dev)
- pestphp/pest: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-23 05:50:42 UTC
README
1:1 PHP bindings of GTK 4 and the GLib main loop GTK runs on, written directly
in C against the Zend API. C functions keep their names (g_timeout_add,
g_signal_connect); functions on a type are methods of the class named after it
(g_application_register(app, cancellable, &error) is $app->register($cancellable)). No defaults, no
composites: behaviour is composed by the caller.
Linux first, macOS too. GTK 4.12+, GLib 2.74+, PHP 8.4+, NTS and ZTS.
What is bound
The calls that initialise GTK, register the application with the desktop, hold and release it, pump the main context, sleep with a budget, wake a sleep, open and close windows, and build menu bars from menu models and actions:
| Native | |
|---|---|
| functions | gtk_init, gtk_init_check, gtk_is_initialized, gtk_get_{major,minor,micro}_version, g_set_prgname, g_get_prgname, g_timeout_add, g_idle_add, g_unix_fd_add, g_source_remove, g_signal_connect, g_signal_emit_by_name, g_signal_handler_disconnect, g_signal_handler_is_connected |
GObject |
G_OBJECT_TYPE_NAME |
GApplication |
g_application_id_is_valid, get_application_id, get_flags, get_is_registered, get_is_remote, register, activate, hold, release, quit, run, g_action_map_add/remove/lookup_action, g_action_group_has/list/activate_action |
GtkApplication |
gtk_application_new, add_window, remove_window, get_active_window, get_windows, get_menubar, set_menubar, set_accels_for_action, get_accels_for_action |
GtkWidget |
show, hide, get/set_visible, get_realized, get_mapped, get/set_hexpand, get/set_vexpand, get_parent, insert_action_group, activate_action_variant, activate, get/set_halign, get/set_valign, get/set_size_request, get_width, get_height, get/set_sensitive, is_sensitive, get_color, set_margin_start/end/top/bottom, add/remove/has_css_class, measure, compute_bounds, get_first_child, get_next_sibling, get_native, get_root, grab_focus, child_focus (GtkDirectionType), has_focus |
GtkNative, GtkRoot (windows, popovers) |
get_surface, get_surface_transform, get_for_surface, realize, unrealize; get_display, get/set_focus |
GtkWindow |
new, get/set_title, get/set_default_size, get/set_child, present, close, destroy, is_active, get/set_transient_for, get/set_application, get/set_modal, get/set_hide_on_close, get_focus, get/set_default_widget, get_group |
GtkWindowGroup |
new, add_window, remove_window |
GtkApplicationWindow |
new, get/set_show_menubar, get_id |
GtkAboutDialog |
new, get/set_program_name, get/set_version, get/set_copyright, get/set_comments, get/set_website |
GtkBox |
new, append, prepend, remove, insert_child_after, reorder_child_after, get/set_spacing, get/set_homogeneous |
GtkGrid, GtkFixed |
new, attach, remove, get_child_at, get/set_row_spacing, get/set_column_spacing; new, put, move, remove, get_child_position |
GdkGLContext, GdkGLAPI, GtkGLArea |
make_current, clear_current, get_current, realize, get_use_es, get_version, get_api; new, get_context, make_current, attach_buffers, queue_render, get/set_auto_render, get/set_has_depth_buffer, get/set_has_stencil_buffer, get/set_allowed_apis, set_required_version, get_error (signal render) |
GtkCssProvider, GdkDisplay, GdkSurface |
new, load_from_string; get_default, sync, get_clipboard; get_width, get_height (signal layout); functions gtk_style_context_add/remove_provider_for_display |
GtkLabel |
new, get/set_text, get/set_wrap, set_ellipsize, get/set_xalign, set_justify, set_markup, get/set_use_markup, get_current_uri |
GtkButton, GtkToggleButton, GtkCheckButton, GtkSwitch |
new_with_label, get/set_label; new_with_label, get/set_active; new_with_label, get/set_active, get/set_label; new, get/set_active |
GtkLinkButton |
new, new_with_label, get/set_uri, get/set_visited |
GCancellable, GAsyncResult, GtkUriLauncher |
new, cancel, is_cancelled; —; new, get/set_uri, launch, launch_finish |
GtkAlertDialog, GtkFileDialog, GtkFileLauncher (GTK 4.10) |
made with g_object_new_with_properties (the printf constructor is not bound), set_message, set_detail, set_buttons, set_default_button, set_cancel_button, set_modal, choose, choose_finish; new, set_title, set_initial_folder, set_initial_name, set_modal, open, open_multiple, select_folder, save and their _finish (a dismissed or cancelled dialog throws GError); new, launch, launch_finish |
GFile, GListStore, GdkClipboard |
new_for_path, get_path (GLocalFile, GDaemonFile and GDummyFile box as GFile); a GListModel; set_text |
GtkEntry, GtkEntryBuffer |
new, get_buffer, get/set_placeholder_text, get/set_visibility; get/set_text |
GtkTextView, GtkTextBuffer |
new, get_buffer, get/set_editable, set_wrap_mode, get/set_monospace, get/set_cursor_visible, get_line_yrange; set_text, get_text (between two iters), begin/end_user_action, get_tag_table, get_iter_at_line, get_iter_at_line_offset, get_start/end_iter, apply/remove_tag, get_selection_bounds, select_range, get_insert, get_iter_at_mark, get_line_count |
GtkTextIter, GtkTextMark, GtkTextTag, GtkTextTagTable, GtkAdjustment |
iter (a copy that keeps its buffer alive; stale once the text changes: GtkException) get_line, get_line_offset, get_offset, has_tag, get_tags; —; new; add, remove, lookup; get/set_value, get_lower, get_upper, get_page_size (signal value-changed) |
GtkScale |
new_with_range, gtk_range_get/set_value, gtk_range_set_range, gtk_range_set_increments, get/set_draw_value |
GtkDropDown, GtkStringList, GtkStringObject |
new_from_strings, get/set_selected, get_selected_item, set_model; new, append, remove, splice, get_string, g_list_model_get_n_items; get_string |
GtkCalendar, GDateTime |
new, get_date, select_day; new_local, get_year, get_month, get_day_of_month, to_unix |
GtkProgressBar, GtkSpinner |
new, get/set_fraction, pulse, set_pulse_step; new, get/set_spinning |
GtkEventController (Key, Motion, Scroll, Focus), GtkGesture, GtkGestureSingle, GtkGestureClick |
new (scroll takes GTK_EVENT_CONTROLLER_SCROLL_* flags), get/set_propagation_phase, get_widget, get_current_event (GdkEvent, GdkScrollEvent with get_relative_direction on GTK 4.20+); scroll get/set_flags, get_unit; gesture set_state (GtkEventSequenceState); single get/set_button, get_current_button, get/set_touch_only; GtkGestureLongPress new; signals through g_signal_connect |
GtkEventControllerLegacy, GdkEvent (GdkButtonEvent, GdkTouchEvent, GdkScrollEvent) |
new; event get_event_type, get_position, get_pointer_emulated, get_modifier_state; button get_button; touch get_emulating_pointer; enum GdkEventType |
GtkShortcutController, GtkShortcut, GtkShortcutTrigger, GtkShortcutAction, GtkCallbackAction |
controller new, add/remove_shortcut, get/set_scope (and GListModel); shortcut new; trigger parse_string; action activate; callback action new (callable trampoline, released at finalize); enums GtkShortcutScope, GtkShortcutActionFlags |
GtkIMContext, GtkIMMulticontext |
multicontext new; set_client_widget, filter_keypress, focus_in, focus_out, reset; commit signal |
GtkWidget controllers, coordinates |
add_controller, remove_controller, compute_point, pick (GTK_PICK_*); GtkWindow get_surface_transform (gtk_native_get_surface_transform) |
GtkSettings, properties |
get_default; g_object_get_property, g_object_set_property, g_object_new_with_properties (any GObject property, as a signal argument converts) |
| Keyvals | gdk_keyval_to_unicode, gdk_keyval_to_lower; GDK_*_MASK constants, enums GtkPropagationPhase, GdkScrollUnit, GdkScrollRelativeDirection (4.20+) |
GtkPicture, GtkSeparator, GtkScrolledWindow |
new, new_for_filename, set_filename, get/set_content_fit, get/set_paintable, get/set_can_shrink; new; new, get/set_child, set_policy, get_hadjustment, get_vadjustment |
GtkPaned, GtkNotebook |
new(GtkOrientation), get/set_start_child, get/set_end_child (a child parented elsewhere → ValueError), get/set_position (negative unsets it), get/set_resize_start_child, get/set_resize_end_child, get/set_shrink_start_child, get/set_shrink_end_child, get/set_wide_handle; new, append_page, remove_page, page_num, get_n_pages, get/set_current_page, get_nth_page, set/get_tab_label_text, get/set_show_tabs, get/set_scrollable (GTK's own indexes: -1 the last page, past the end nothing; a tab label set on a child that is not a page, which GTK asserts on, → ValueError); signals notify::position, switch-page |
GBytes, GdkTexture, GdkMemoryTexture, GdkMemoryTextureBuilder |
new (a string's bytes, or an address and a size), get_size, get_data; get_width, get_height; new from a GBytes in a GdkMemoryFormat (null when the bytes cannot hold the image); GTK 4.16+ builder: setters returning nothing, set_bytes(GBytes), an update texture and an update region (CairoRegion), build (null when unset) |
GdkDmabufTextureBuilder |
Linux, GTK 4.14+: setDisplay, setWidth, setHeight, setFourcc, setModifier, setPremultiplied, setNPlanes, per plane setFd (borrowed: keep it open while the textures live), setStride, setOffset, setUpdateTexture, setUpdateRegion, build(?callable $destroy) ($destroy runs once GTK has finalized the texture and is done with the fds; a failed import throws its GError) |
GdkDmabufFormats |
GTK 4.14+, from GdkDisplay::getDmabufFormats(): getNFormats, getFormat ([fourcc, modifier]), contains(fourcc, modifier); empty off Linux |
GtkTreeListModel, GtkTreeListRow, GtkTreeExpander, GtkScrollInfo |
new (create func = PHP callable returning a GListModel or null, held until the model is finalized), get_row, get_child_row; get_item, get/set_expanded, is_expandable, get_depth, get_position, get_parent, get_child_row; new, get/set_child, get/set_list_row; new, get/set_enable_horizontal, get/set_enable_vertical |
GtkColumnView, GtkColumnViewColumn |
new, append/remove_column, get/set_model, set_show_row/column_separators, scroll_to (GTK_LIST_SCROLL_*); new, get/set_title, set_expand, set_resizable |
GtkSignalListItemFactory, GtkListItem, GtkSingleSelection |
new (signals setup, bind, unbind, teardown); get_position, get_item, get/set_child; new, get/set_selected, get_selected_item, set_autoselect, set_can_unselect, get_model |
GtkMediaStream, GtkMediaFile, GtkVideo |
play, pause, get/set_playing, get_ended, get_error, seek, is_seekable, get_timestamp, get_duration, get/set_muted, get/set_loop, has_video; new, new_for_filename, set_filename, clear; new, get/set_media_stream, get/set_autoplay, set_loop |
GtkPopoverMenuBar |
new_from_model, get/set_menu_model |
GtkPopover, GtkPopoverMenu |
set/get_pointing_to (a GdkRectangle; get writes it by reference and returns GTK's gboolean), set/get_has_arrow, popup, popdown, signal closed; menu new_from_model, get_menu_model; attached with its own setParent / unparent (gtk_widget_set_parent, bound for popovers only) |
GMenuModel, GMenu, GMenuItem |
get_n_items, is_mutable; new, append, append_item, append_section, append_submenu, prepend, insert, remove, remove_all, freeze; new, set_label, set_detailed_action, set_action_and_target_value, set/get_attribute_value, set_submenu, set_section |
GSimpleAction, GSimpleActionGroup |
new, new_stateful, set_enabled, set_state, g_action_get_name/enabled/state, g_action_activate, g_action_change_state; new, g_action_map_add/remove/lookup_action, g_action_group_has/list/activate_action |
GVariant |
new_boolean/string/int32/double, get_boolean/string/int32/double, get_type_string, is_of_type, print |
GMainContext |
default, get_thread_default, iteration, pending, wakeup, acquire, release, is_owner |
Enums: GApplicationFlags, GIOCondition, GtkOrientation, GtkAlign,
PangoEllipsizeMode, GtkJustification, GtkWrapMode, GtkContentFit,
GtkPolicyType. Constants: G_SOURCE_CONTINUE, G_SOURCE_REMOVE,
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION, GTK_INVALID_LIST_POSITION. Errors:
GError (a GError, with its domain; also what GtkMediaStream::getError()
returns), GtkException (a call refused because making it crashes; see below). The stubs in
stubs/ are the full declaration.
GTK answers for itself: its criticals, warnings, NULLs and clamping reach PHP
as GTK gives them. The extension refuses a call only where making it was shown
to crash the process or read freed memory, each such site with a probe in
tests/Probes: a widget before GTK is initialised; on macOS, a GDK call or a
context iteration off the process main thread; a stale GtkTextIter; a text
mark that is not the buffer's; a detailed action name GIO or GTK cannot parse;
an index outside a region's rectangles; a string shorter than the size or
character count asked of it; a class compiled against newer GTK than the one
running. A source or signal handler dispatched on a thread other than the one
whose PHP made it is dropped, and a PHP class cannot implement the native
interfaces (GAction, GListModel, GtkNative and the rest) unless its parent
class's GType does. A PHP int with no form in the C parameter's type is a
ValueError. ./configure --enable-gtk-probes builds the switch the probes use
(env PHPGTK_PROBE_OFF=<site> turns one site off); never for a release.
Video needs GTK's media backend module: libgtk-4-media-gstreamer on Debian
(the installer checks), none in Homebrew's gtk4 (the installer warns;
GtkMediaFile::new()->typeName() is GtkNoMediaFile and GtkVideo shows nothing).
Coverage
SUPPORT.md lists every constructor, method and function of GTK 4, GDK and GSK that the installed GIR files declare and do not mark deprecated, each marked bound or not; the API badge is computed from it. Regenerate both after a binding change, and check them:
php84 scripts/support.php # writes SUPPORT.md and the badge php84 scripts/support.php --check # exits 1 when either is stale
Example
$app = GtkApplication::new('com.example.App', GApplicationFlags::DEFAULT_FLAGS); g_signal_connect($app, 'activate', fn (GtkApplication $app) => null); $app->register(null); // startup: GTK initialises $app->hold(); $ctx = GMainContext::default(); // Sleep up to 16 ms, or until a source is ready, dispatching what is. $budget = g_timeout_add(16, fn (): bool => G_SOURCE_REMOVE); $ctx->iteration(true); // Wake on a descriptor (a kqueue or epoll fd works: it turns readable when its own events are pending). g_unix_fd_add($fd, GIOCondition::IN, fn (int $fd, int $condition): bool => G_SOURCE_CONTINUE); $app->release();
Install
bash install-debian-trixie.sh # Debian, Ubuntu, Raspberry Pi OS (needs libgtk-4-dev) bash install-macos.sh # Homebrew php@8.4 and php@8.4-zts (needs brew install gtk4)
Or with PIE: pie install php-io-extensions/gtk.
venusian build reads the system packages from extra.venusian.system in composer.json: apt packages to build with and the run-time ones a .deb depends on or recommends, and the Homebrew ones.
Test
composer install php vendor/bin/pest php examples/smoke.php # bridge: register, pump, sleep, wake; prints SMOKE_OK php examples/window-smoke.php # window, menu bar, actions, close-request; prints SMOKE_OK
On Linux over SSH, export the session's WAYLAND_DISPLAY, XDG_RUNTIME_DIR and
DBUS_SESSION_BUS_ADDRESS first. The video playback test runs where a media
backend is installed and is skipped elsewhere. Design notes live in the OKF bundle under .okf/.
License
MIT