diff --git a/docs/getting-started/docker.md b/docs/getting-started/docker.md new file mode 100644 index 0000000..0a83c2b --- /dev/null +++ b/docs/getting-started/docker.md @@ -0,0 +1,13 @@ +--- +title: Docker +--- + +Run PHP Debugger in a container using an image with the debugger already compiled +into the interpreter, with nothing to install. + +:::note +This page is a work in progress — full documentation is coming soon. +::: + +In the meantime, the [installer](./installation.mdx) covers installing directly +onto your machine. diff --git a/docs/getting-started/install-options.md b/docs/getting-started/install-options.md new file mode 100644 index 0000000..0f56d28 --- /dev/null +++ b/docs/getting-started/install-options.md @@ -0,0 +1,12 @@ +--- +title: More install options +--- + +Ways to install PHP Debugger beyond the installer — prebuilt binaries, package +managers, and building from source. + +:::note +This page is a work in progress — full documentation is coming soon. +::: + +For the recommended route, see [Installation](./installation.mdx). diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md deleted file mode 100644 index 89cec62..0000000 --- a/docs/getting-started/installation.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Installation ---- - -Installing PHP Debugger is straightforward and similar to installing other PHP extensions. The extension is available through PIE and can also be compiled from source. Once installed, you'll need to configure your php.ini file to enable the extension and set up your IDE to communicate with the debugger using the DBGp protocol. - -## Configuration - -Add the following to your `php.ini` file: - -```ini -zend_extension=php_debugger -php_debugger.mode=debug -php_debugger.start_with_request=trigger -php_debugger.client_host=127.0.0.1 -php_debugger.client_port=9003 -``` - -## Usage Example - -You can trigger breakpoints programmatically in your PHP code: - -```php - + +The script downloads the latest release and drops the `php-debugger` binary into +the current directory. Set `INSTALL_DIR` if you would rather it landed somewhere +else. + +:::tip[Prefer Docker?] + +Use a container image instead — see [Docker](./docker.md). + +::: + +## 2. Install the debugger + +The installer offers two ways to get a working debugger. Run it with no arguments +for the default: + +```bash +php-debugger install +``` + +That installs a **self-contained PHP interpreter** with the debugger compiled +straight into it, and makes it the active `php` on your PATH. Nothing else on your +system needs to change. + +If you would rather keep the PHP you already have, install **only the extension** +into it: + +```bash +php-debugger install --extension-only +``` + +The two are mutually exclusive, and the installer knows it: installing the +interpreter removes any extension you had installed, so you never end up running +both at once. + +A few flags are worth knowing: + +| Flag | What it does | +| --- | --- | +| `-u, --user` | Install into a per-user directory, so no `sudo` is needed. The default is system-wide. | +| `-p, --php ` | Pick the PHP version to install. Defaults to the latest, and applies to the interpreter only. | +| `-e, --extension-only` | Install just the extension into your current PHP. | +| `-z, --zts` | Install a thread-safe build instead of the default non-thread-safe one. | +| `-y, --yes` | Answer yes to every prompt, for unattended or CI use. | + +If the directory it installs into is not on your `PATH`, the installer prints the +exact line you need to add. + +## Changing your mind + +Nothing here is a one-way door. Whether you installed the interpreter or just the +extension, whatever it replaced is backed up first — the PHP you already had, or +the ini configuration it changed. To put things back as they were: + +```bash +php-debugger uninstall +``` + +That removes whatever was installed and restores your previous setup from the +backup — the interpreter it replaced, or the ini configuration it changed. You do +not have to tell it which kind you installed; it works that out for itself. + +The installer also tries hard not to leave you with a broken `php` in the first +place. It runs the downloaded interpreter before changing anything, so a build +that cannot run on your system is never installed. After activating it, it checks +that `php` still runs and reports the debugger module, and rolls everything back to +the previous working state if either check fails. + +## Staying up to date + +To move to the latest release: + +```bash +php-debugger update +``` + +It takes no arguments. Whether you installed the interpreter or just the +extension, it detects what is there and reinstalls the same thing against the +latest version. + +:::info[Prefer to install some other way?] + +See [More install options](./install-options.md). + +::: + +## Next steps + +With the debugger installed, point your editor at it and set your first +breakpoint: + +- [Quick Start](./quick-start.md) +- [Configuration](./configuration.md) diff --git a/docs/getting-started/introduction.mdx b/docs/getting-started/introduction.mdx index 1f48a3d..e51f349 100644 --- a/docs/getting-started/introduction.mdx +++ b/docs/getting-started/introduction.mdx @@ -15,5 +15,5 @@ because when you are not using it you can barely tell it is there. Ready to get started? Install PHP Debugger and try the quick start guide: -- [Installation Guide](./installation.md) +- [Installation Guide](./installation.mdx) - [Quick Start](./quick-start.md) diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index 4bd3673..25cf144 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -2,7 +2,7 @@ title: Quick Start --- -Get up and running with PHP Debugger in a few minutes. This guide assumes you have already [installed](./installation.md) the extension. +Get up and running with PHP Debugger in a few minutes. This guide assumes you have already [installed](./installation.mdx) the extension. ## 1. Enable the debugger diff --git a/docusaurus.config.js b/docusaurus.config.js index 8873b53..bb2491c 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -125,7 +125,7 @@ const config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, - additionalLanguages: ['php', 'ini', 'bash'], + additionalLanguages: ['php', 'ini', 'bash', 'powershell'], }, }), }; diff --git a/sidebars.js b/sidebars.js index d753885..adb8c64 100644 --- a/sidebars.js +++ b/sidebars.js @@ -12,6 +12,8 @@ const sidebars = { items: [ 'getting-started/introduction', 'getting-started/installation', + 'getting-started/docker', + 'getting-started/install-options', 'getting-started/quick-start', 'getting-started/configuration', ], diff --git a/src/components/InstallCommand/index.js b/src/components/InstallCommand/index.js new file mode 100644 index 0000000..95127da --- /dev/null +++ b/src/components/InstallCommand/index.js @@ -0,0 +1,150 @@ +import {useEffect, useRef, useState} from 'react'; +import clsx from 'clsx'; +import CodeBlock from '@theme/CodeBlock'; +import styles from './styles.module.css'; + +/* macOS and Linux run the same script -- it detects the OS and architecture + itself -- so both tabs deliberately show the same command. */ +const UNIX_COMMAND = + 'curl -fsSL https://github.com/php-debugger/installer/releases/latest/download/install.sh | sh'; +const WINDOWS_COMMAND = + 'powershell -c "irm https://github.com/php-debugger/installer/releases/latest/download/install.ps1 | iex"'; + +const iconProps = { + width: 15, + height: 15, + viewBox: '0 0 24 24', + fill: 'none', + stroke: 'currentColor', + strokeWidth: 2, + strokeLinecap: 'round', + strokeLinejoin: 'round', + 'aria-hidden': true, +}; + +const CopyIcon = () => ( + + + + +); + +const CheckIcon = () => ( + + + +); + +const PLATFORMS = [ + {id: 'macos', label: 'macOS', language: 'bash', command: UNIX_COMMAND}, + {id: 'linux', label: 'Linux', language: 'bash', command: UNIX_COMMAND}, + {id: 'windows', label: 'Windows', language: 'powershell', command: WINDOWS_COMMAND}, +]; + +function detectPlatform() { + const ua = navigator.userAgent; + if (/Windows/i.test(ua)) { + return 'windows'; + } + if (/Mac OS X|Macintosh/i.test(ua)) { + return 'macos'; + } + /* Android reports Linux too, and the command is the same either way. */ + if (/Linux|Android|X11/i.test(ua)) { + return 'linux'; + } + return null; +} + +export default function InstallCommand() { + /* The page is prerendered without knowing the visitor's OS, so start on macOS + and correct it after mounting. Detecting in an effect rather than during + render keeps the first client render identical to the server's, which is + what hydration compares. A visitor who picks a tab keeps their choice -- + the effect only runs on mount. */ + const [platform, setPlatform] = useState('macos'); + + useEffect(() => { + const detected = detectPlatform(); + if (detected) { + setPlatform(detected); + } + }, []); + + const active = PLATFORMS.find((p) => p.id === platform) ?? PLATFORMS[0]; + const [copied, setCopied] = useState(false); + const resetTimer = useRef(null); + + useEffect(() => () => clearTimeout(resetTimer.current), []); + + /* Fallback for when the async Clipboard API is unavailable or refused: it needs + a secure context, and browsers decline it when the document is not focused. + A throwaway textarea and execCommand works in those cases -- it is what + Docusaurus's own copy button relies on. */ + function copyViaTextarea(text) { + const field = document.createElement('textarea'); + field.value = text; + field.setAttribute('readonly', ''); + field.style.position = 'fixed'; + field.style.opacity = '0'; + document.body.appendChild(field); + field.select(); + let ok = false; + try { + ok = document.execCommand('copy'); + } catch { + ok = false; + } + document.body.removeChild(field); + return ok; + } + + async function copy(event) { + event.currentTarget.blur(); + let ok = false; + try { + await navigator.clipboard.writeText(active.command); + ok = true; + } catch { + ok = copyViaTextarea(active.command); + } + /* Only confirm a copy that actually happened. If both routes fail the label + stays put rather than claiming something untrue -- the whole command is on + screen and can still be selected by hand. */ + if (!ok) { + return; + } + setCopied(true); + clearTimeout(resetTimer.current); + resetTimer.current = setTimeout(() => setCopied(false), 2000); + } + + return ( +
+
+
+ {PLATFORMS.map((p) => ( + + ))} +
+ +
+ {active.command} +
+ ); +} diff --git a/src/components/InstallCommand/styles.module.css b/src/components/InstallCommand/styles.module.css new file mode 100644 index 0000000..db8e60b --- /dev/null +++ b/src/components/InstallCommand/styles.module.css @@ -0,0 +1,82 @@ +.wrapper { + margin-bottom: 1.5rem; +} + +.tabs { + display: flex; + gap: 0.25rem; +} + +.tab { + border: 1px solid var(--phpdbg-card-border); + background: transparent; + color: var(--ifm-color-emphasis-700); + border-radius: 0.5rem; + padding: 0.35rem 0.9rem; + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; +} + +.tab:hover { + border-color: var(--ifm-color-primary); + color: var(--ifm-color-primary); +} + +.tabActive, +.tabActive:hover { + border-color: var(--ifm-color-primary); + background: var(--ifm-color-primary); + color: #fff; +} + +.bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + margin-bottom: 0.5rem; +} + +.copy { + display: inline-flex; + align-items: center; + gap: 0.4rem; + border: 1px solid var(--phpdbg-card-border); + background: transparent; + color: var(--ifm-color-emphasis-700); + border-radius: 0.5rem; + padding: 0.35rem 0.9rem; + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; +} + +/* Hover stays deliberately neutral. The brand red is what the tabs beside this + button use to mean "selected", so an accented Copy button reads as still being + active -- which is exactly how it looked after a click, with the pointer still + resting on it. The accent is reserved for the moment the copy succeeds. */ +.copy:hover { + border-color: var(--ifm-color-emphasis-400); + color: var(--ifm-color-emphasis-800); +} + +.copied, +.copied:hover { + border-color: var(--ifm-color-primary); + color: var(--ifm-color-primary); +} + +/* Docusaurus floats its own copy and word-wrap buttons over the top-right of the + block. The install command is one long line, so it runs underneath them however + far it is scrolled. Copy lives in the bar above instead, and the command wraps + so all of it is readable without scrolling sideways. */ +.wrapper :global(div[class*='buttonGroup']) { + display: none; +} + +.wrapper :global(pre code) { + white-space: pre-wrap; + overflow-wrap: anywhere; +} diff --git a/src/css/custom.css b/src/css/custom.css index ed1fa02..9dc2a16 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -156,3 +156,28 @@ article a { #page-top { scroll-margin-top: calc(var(--ifm-navbar-height) + 1rem); } + +/* Admonitions ship in Infima's green/blue/grey, which reads as a foreign palette + next to the neutral surfaces and single red accent used everywhere else. Put + the informational ones on the same card surface as the rest of the site, with + the brand red as the accent. + + Deliberately not warning and danger: those are meant to stop the reader, and + they can only do that if they still look different from an ordinary aside. */ +.theme-admonition-note, +.theme-admonition-tip, +.theme-admonition-info { + --ifm-alert-background-color: var(--phpdbg-surface); + --ifm-alert-background-color-highlight: transparent; + --ifm-alert-foreground-color: var(--ifm-font-color-base); + --ifm-alert-border-color: var(--ifm-color-primary); + border: 1px solid var(--phpdbg-card-border); + border-left: 3px solid var(--ifm-color-primary); + border-radius: 0.75rem; +} + +.theme-admonition-note [class*='admonitionIcon'] svg, +.theme-admonition-tip [class*='admonitionIcon'] svg, +.theme-admonition-info [class*='admonitionIcon'] svg { + fill: var(--ifm-color-primary); +}