今日已更新 233 条资讯 | 累计 20205 条内容
关于我们

标签:#HP

找到 96 篇相关文章

产品设计

Renaming wp-login isn't the same as making wp-admin disappear

"How do I hide wp-admin" is one of the most-searched WordPress security questions, and most answers give the same advice: install a plugin that renames your login URL. That advice isn't wrong. It's just answering a smaller question than the one being asked. Renaming /wp-login.php to /my-login moves the login form. It does not change what answers at the old path, what your plugin folders advertise, or what your home page tells a scanner about the stack underneath. If your only problem is the password-guessing bot hammering the default form, a renamer solves it. If your problem is "stop my site from being identified and targeted as WordPress," you've solved maybe a third of it. Here are the three leaks a login rename leaves open. Leak 1: the old path still costs a full WordPress boot When a login-URL renamer "blocks" the default path, the request to /wp-login.php still loads WordPress. PHP starts, the plugin stack initializes, and only then does the plugin decide to return a 404 to the logged-out visitor. The visitor sees a 404. Your server still did the work of booting WordPress to produce it. On a quiet site, nobody notices. On a site taking tens of thousands of probes a day, that's tens of thousands of full WordPress boots spent generating 404s. Your security dashboard's "attempts blocked" counter looks great. Your CPU graph disagrees. The architectural alternative is to reject the request at the rewrite layer, before PHP runs: # Apache .htaccess — reject the default login path at the server level < IfModule mod_rewrite.c > RewriteEngine On RewriteCond %{REQUEST_URI} ^/(wp-login\.php|wp-admin) [NC] RewriteCond %{HTTP_COOKIE} !wordpress_logged_in [NC] RewriteRule .* - [R=404,L] </ IfModule > # nginx — same idea, requires a config reload after change location ~ * ^/(wp-login \ .php|wp-admin) { if ( $http_cookie !~* "wordpress_logged_in") { return 404 ; } } The probe to the old path returns 404 from the server, WordPress never loads, and the request costs almost nothi

2026-06-10 原文 →
AI 资讯

Learnings about authentication and authorization.

At the beginning of my Engineering career, I worked in a place where I had a lot of freedom to implement and experiment any technology I found interesting. I tried many technologies like PHP, Java, EJBs, SOAP, Rest and JavaScript. This gave me a lot of perspective, but I lacked the guidance and mentoring from more experienced developers. One of the most problematic things I built was a login. I would like to share in this document things that I did in the past so you understand why it is problematic and how I would build them today. Earlier Mistakes My First PHP Login. This is not a terrible option when using a single host, small project, but the biggest problem comes when we need to scale horizontally. Session variables exist only on the servers they are created. If you add more servers + Load Balancer, there is no guarantee that your requests go to the same server. In terms of vulnerabilities, if somebody manages to read your session id, they can impersonate you, act on your behalf. This is not really different from other methods like JWT so it is important to set up SameSite cookies or CSRF tokens, but do you think I did that for my first login ? of course NOT! My first Password storage. If you are thinking on implementing a login please NEVER do what I am about to describe: The first time I implemented a password, I was worried that somebody would find out the "actual" password. I wasn't actually thinking about using HTTP (instead of HTTPs), so my take on this was "encrypting" the password into MD5. Then the password was saved in MD5, but I was NOT doing anything different than just sending the password AS is. Let me explain the problems with this approach: Over HTTP an MD5 password can be read, and anybody can simply replicate the request with the same MD5 MD5 was actually NOT encrypting, it was a hashing. There are databases all over the internet mapping MD5 and other hashed passwords available so finding an MD5 can actually be translated to an actual password

2026-06-09 原文 →
AI 资讯

How I stopped hardcoding business rules in PHP - and built a rule engine to fix it

Every PHP developer knows this situation: a client calls and says "I want free shipping for VIP customers on weekends, but only if the cart total is above €100." You open your code. You find the shipping module. You add an if. You deploy. Three weeks later: "Actually, make it €80. And also for the 'Premium' group." You open your code again. This loop : client request -> find logic in code -> modify -> deploy, was costing me a lot of time. And it's not just shipping. I build custom ecommerce solutions: payment modules, synchronization systems, pricing calculators. Business rules are everywhere, and they change constantly. The obvious solution I didn't want Symfony's ExpressionLanguage exists and it's impressive. But it pulls in dependencies, it can traverse objects and call methods (which is a security concern when rules are authored by users), and when something goes wrong, it doesn't tell you why. It's a black box. I needed something smaller, stricter, and transparent. So I built php-ruler I started with the classic pipeline: Lexer → AST → Evaluator. Strict typing from the start — 1 = '1' is a type error, not true. No silent coercion. Then I added features one real problem at a time. Problem: when something fails, why? -> I built an explain mode that returns the full evaluation tree: which sub-conditions passed, which failed, which were short-circuited, and why a variable was missing. Problem: in production, the context is sometimes incomplete -> I built a safe mode that doesn't throw on missing variables — it collects them all and lets you decide what to do. Problem: customer.group.name is not user-friendly -> I built an alias resolver. As a developer, I expose what I want: $resolver = ( new AliasResolver ()) -> add ( 'customer.group' , 'customer group' ) -> add ( 'cart.total' , 'cart amount' ); Now a non-developer can write: customer group = 'VIP' AND cart amount > 100 And I control exactly what variables are available to them. A real example Here's the shipping

2026-06-09 原文 →
AI 资讯

I shipped a support desk by deleting a dependency

I added a support desk to LaraFoundry this week. The first commit in the slice removed a package instead of adding one. LaraFoundry is a reusable SaaS core for Laravel that I'm extracting in public from an older app of mine. Auth, multi-tenancy, roles, activity log, notifications, billing seam, and now support tickets. The rule for every module is the same: lift the proven idea out of the old code, modernise it, harden it, and make it something you can composer require into a fresh Laravel app without inheriting a pile of assumptions. Tickets is where that rule got interesting, because the old code didn't own its ticket model. It leaned on a third-party ticket library. Why a ticket package is wrong for a reusable core A third-party ticket package is a perfectly reasonable choice when you're building one app. You get tables, a model, a status enum and a UI scaffold for free. It's the wrong choice for a core that other apps install. Pull it into the core and every host app inherits that package's migrations, its table names, its status vocabulary and its idea of what a ticket is. The dependency becomes load-bearing in projects that never asked for it, and the day it lags a Laravel release, every downstream app waits. So I cut it (decision D-4.2-1 in my notes) and wrote the model by hand. The model is about 180 lines. There is no magic. Two tables, a uuid, a status, a couple of scopes. The diff against "depend on the package" was less code in the core, not more, because I only kept the behaviour I actually use. Here's the top of the model, with the extraction notes I leave for future me: /** * A support ticket: the channel between a host user and the platform operator. * * Extracted from the donor App\Models\Ticket, which sat on a third-party * ticket package. That dependency is cut: this is a self-contained model. * Categories and labels are JSON slug arrays driven by config, not pivot * tables. The dead assigned_to column and the donor's invalid-operator * query are

2026-06-08 原文 →
AI 资讯

Typed Eloquent boundaries without building a second ORM

Most Laravel teams do not need to "fix" Eloquent. They need to stop letting raw model state leak too far into code that makes real business decisions. That is the practical version of this debate. Typed objects around Eloquent can be a big improvement, but only when they are used as boundaries . If you push the pattern too far, you end up with a second object model shadowing the first one. At that point you are not improving Laravel. You are building a parallel ORM that adds mapping code, cognitive load, and friction on every change. So the right question is not, "Should we replace Eloquent with typed objects?" The right question is, where does untyped Eloquent stop being cheap? Once you frame it that way, the migration path becomes much clearer. Keep Eloquent where it is good at persistence, hydration, scopes, relationships, and query composition. Introduce typed objects where the shape is messy, the values carry business meaning, or invalid combinations are too easy to represent. That is the version that pays off. The Core Recommendation If you only remember one thing from this article, make it this: add typed boundaries around unstable or meaningful data, not around every model . That usually means one of four cases: a JSON column that multiple parts of the app interpret differently domain values like money, status, addresses, or billing configuration data crossing from Eloquent into services, jobs, or integrations code paths where stringly typed state has already caused confusion or bugs Everything else should be guilty until proven useful. This is where a lot of teams go wrong. They see a good example of typed objects and immediately generalize it into an architecture rule. Then every model gets a FooData , FooView , FooState , FooRecord , and FooMapper . The app becomes more "designed" and less understandable. A Laravel codebase does not get better because it has more classes. It gets better because responsibility becomes clearer . Why Raw Eloquent Starts Hurt

2026-06-08 原文 →
AI 资讯

Install PHP 8.5 with ASDF on Arch Linux

This quick tutorial shows how to install ASDF Version Manager on Arch Linux and use it to install PHP 8.5. Install Required Dependencies First, install the required packages and build dependencies: yay -S base-devel libpng postgresql-libs re2c gd oniguruma libzip libsodium You may also want to install additional common dependencies: yay -S curl git openssl zlib libxml2 sqlite Install ASDF Clone the ASDF repository: git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.18.0 Add ASDF to your shell configuration. Bash echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc echo '. "$HOME/.asdf/completions/asdf.bash"' >> ~/.bashrc source ~/.bashrc ZSH echo '. "$HOME/.asdf/asdf.sh"' >> ~/.zshrc echo '. "$HOME/.asdf/completions/asdf.bash"' >> ~/.zshrc source ~/.zshrc Verify installation: asdf --version Add the PHP Plugin Install the PHP plugin for ASDF: asdf plugin add php https://github.com/asdf-community/asdf-php.git Install PHP 8.5 List available PHP versions: asdf list all php Install PHP 8.5: asdf install php 8.5.0 Set PHP 8.5 as the global default: asdf global php 8.5.0 Reload your shell: exec $SHELL Verify the installation: php -v Expected output: PHP 8.5.x ( cli ) Useful ASDF Commands List installed PHP versions: asdf list php Install another PHP version: asdf install php 8.4.0 Switch globally: asdf global php 8.4.0 Switch locally for a project: asdf local php 8.5.0 Optional: Install Composer After installing PHP, install Composer globally: php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" php composer-setup.php sudo mv composer.phar /usr/local/bin/composer rm composer-setup.php Verify: composer --version

2026-06-05 原文 →
AI 资讯

Building a SaaS engine in public: shipping the billing seam, not billing

I tagged v0.9.0 of LaraFoundry this week: billing. Except the honest headline is that I shipped the billing seam , not billing. The free core now has the whole shape of a subscription system, a payment-gateway contract, a driver manager, a real access gate over subscription columns, and it cannot take a single cent. That is on purpose, and the reason is the most interesting part of the phase. LaraFoundry is a SaaS core I'm extracting in public from a live CRM, one module at a time. The deal I made with myself early on: the core is free and stays free for everything except money. Auth, multi-tenancy, RBAC, the admin console, the activity log, i18n, files: all free. The day a business wants to charge its customers , that is the paid part. So billing could not just be "another module." It had to split cleanly down a line, with the free side carrying real, useful structure and the paid side carrying the parts that actually move money. The donor habit I would not carry Here is what the original CRM did when a company "paid" for its subscription: // TODO: real payment gateway integration // TEMPORARY: every payment is successful, for testing $paymentStatus = 'success' ; That success was hardcoded. There was no Stripe, no Paddle, no gateway at all. "Paying" wrote a row into a company_payments table and flipped the subscription date forward. For a CRM I run myself, with one real user, that was fine: I never needed the real thing, so the placeholder sat there indefinitely. The moment this becomes a reusable core, that placeholder is poison. A success that is always true is worse than no gateway, because it looks like billing works. So the rule for this phase was simple: the fake gateway does not get extracted. Whatever stands in its place has to be honest about the fact that it takes no money. What the seam actually is The free core ships a PaymentGatewayInterface : subscribe, cancel, refund, status, and verify a webhook. It describes only the mechanics of moving money. It d

2026-06-04 原文 →
AI 资讯

Building a API in PHP

Books API Structure Create folders app/ app/controllers/ app/core/ app/models/ app/models/DAOs/ app/models/DTOs/ app/models/entities/ app/utils/ config/ public/ api/composer.json Configure Composer and the PSR-4 autoload so that classes with the namespace App\ are searched inside the app/ folder. Key content: { "name": "user/api", "autoload": { "psr-4": { "App\": "app/" } } } After creating it, run this command inside the api folder: composer dump-autoload api/config/config.php Defines the base URL of the project. The router removes it from REQUEST_URI to keep only routes such as /books/get. <?php define('BASE_URL', '/proyect/api/public'); api/config/dbconf.json MySQL DB connection: { "host": "localhost", "user": "root", "password": "", "db_name": "books_db" } api/public/.htaccess Makes Apache send all routes to index.php. RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.php [QSA,L] api/public/index.php This is the entry point. It loads Composer, loads the configuration and calls the router. <?php use App\Core\Router; require_once DIR . '/../vendor/autoload.php'; require_once DIR . '/../config/config.php'; (new Router())->dispatch($_SERVER['REQUEST_URI']); api/app/core/Router.php <?php namespace App\Core; class Router { protected array $routes = [ '/' => 'HomeController@index', '/books' => 'BookController@index', '/books/get' => 'BookController@getAll', '/books/getById' => 'BookController@getById', '/books/create' => 'BookController@create', '/books/update' => 'BookController@update', '/books/delete' => 'BookController@delete', ]; public function add($route, $params): void { $this->routes[$route] = $params; } public function dispatch($uri): void { $uri = parse_url(str_replace(BASE_URL, '', $uri), PHP_URL_PATH); if (!isset($this->routes[$uri])) { $this->sendNotFound(); return; } [$controller, $method] = explode('@', $this->routes[$uri]); $controller = 'App\\Controllers\\' . $controller; if (!class_exists($co

2026-06-04 原文 →
AI 资讯

Hello dev — I ship AI voice + web chat on PHP sites (elionmusic.com)

Hi — I'm E Lion (Eric), Hawaii-based builder at Coral Crown Solutions . I ship production code on my own domains—not tutorials: elionmusic.com — 400+ promo pages, vinyl-style player UX, Vapi phone agent + OpenAI "Shine" chat (one knowledge base, unified CSV log, webhook follow-up emails) prayerauthority.com — faith-tech at scale; WebM flying angels , SOAP journal, oracle tools Digital Zion — Three.js metaverse + native 3D desk fork + localhost bridge APIs Stack: PHP 8, vanilla JS, webhooks, JSON-LD / Search Console, ElevenLabs, Playwright, Electron (Shine assistant), Cursor pair-programming. Looking for: peers who respect hard integration work (SMTP, CORS, cPanel, webhook auth) and clients who need a real AI front desk or artist/ministry platform. Live demos: coralcrownsolutions.com · elionmusic.com Happy to give honest feedback on your builds—drop a link.

2026-06-03 原文 →
AI 资讯

Building a Car Showroom Website for Only $50 (800,000 IDR)

Recently, I started building a website for a local car showroom. The budget? 800,000 Indonesian Rupiah (around $50 USD). At first, it sounded impossible. But when working with small businesses in Indonesia, budgets are often very different from what many developers in the US or Europe are used to. Instead of building a complex custom platform, I focused on solving the showroom's real problems. What the client gets Vehicle Management Add and edit car listings Manage prices Vehicle specifications Featured inventory Vehicle Search & Filters Visitors can filter cars by: Brand Model Year Price Condition Built-in CMS The showroom can publish: Car buying guides Automotive news SEO articles Promotions Lead Generation Every car listing includes direct WhatsApp contact buttons to maximize inquiries. Extra Services Included For the same price: Free maintenance for simple issues Free consultation and support 3 free blog articles during the first month AI-powered statistics assistant Why WordPress? Many developers immediately think about Laravel, React, Next.js, microservices, and other modern stacks. For this project, WordPress was the right tool. The client needed: A website they could update themselves Better Google visibility A simple inventory system More WhatsApp leads WordPress delivered all of that quickly. A Lesson I've Learned Small businesses rarely care about technology. They care about outcomes. They don't ask: "Does it use React?" They ask: "Will this help me sell more cars?" And honestly, that's probably the better question. What would you include in a low-budget car showroom website?

2026-06-02 原文 →
AI 资讯

My Days at Laravel Live Japan 2026

Japanese version available on note . Hi, I'm chatii @chatii . I recently attended Laravel Live Japan 2026. Here's what inspired me and what I took home from the conference. Profile Organizer of PHP Conference Kagawa Encountered PHP back in the 4.x era Freelancer English level: "Can read reasonably well," "Can write a little," "Can listen a bit," "Cannot speak at all." 5/23 PHP×Tokyo - Laravel Live Japan PRE-PARTY PHP×Tokyo - Laravel Live Japan PRE-PARTY - connpass (English follows Japanese) PHP×Tokyoは、PHPやLaravelが好きなエンジニアのためのインターナショナルなミートアップです。 日英のライブ翻訳付きなので、英語が得意でなくても大丈夫です!言語の壁を越えて、PHP/Laravelについて語り合いましょう! 登壇者も募集中です!登壇を希望する方はこちらからご応募ください。 登壇は日本語・英語どちらでも大丈夫です。 #### タイムテーブル * 13:00 - 13:30 受付 & ネットワーキング * 13:30 - 13:40 オープニング * 13:40 - 14:10 "Man... phpxtky.connpass.com I first participated in "PHP×Tokyo March 2026." It was my first time attending a meetup with international participants. I couldn't speak English, but I hoped to be able to communicate somehow. Back in March, David helped me immensely with translation, which made me feel a bit apologetic... At the PRE-PARTY, I took the plunge. During the networking session, I managed to approach Victor Ukam , who gave the talk "Manage AI Prompts as Versioned Files in PHP," and said in English, "I have a question...!" Well... communication after that relied on Google Translate, but I was able to overcome the "first hurdle." You could say I successfully executed <?= "Hello, World" ?> . Also, it was great to see Ivan again, who came from Russia. I first met him in March, and I was so happy he came over to say hello! 5/25 Eve of the Conference, Gyoza Restaurant Zumi organized an unofficial pre-party via the laravel-live-jp channel on the "Laravel Japan" Discord. Participating in these "fringe events" around a conference is always fun. I had booked a hotel from the day before, so I joined in. The real-time translation app that Albert Chen built was incredibly high-performance... 5/26 Day 1 ...Actually, I couldn't sleep at

2026-06-02 原文 →
AI 资讯

These are the first Nvidia RTX Spark laptops

Nvidia has officially entered the world of consumer laptop chips with the RTX Spark, and several device makers already have hardware lined up for it. Microsoft, Asus, HP, MSI, Lenovo, and Dell are expected to launch RTX Spark laptops sometime this fall, and some of those partner companies have shared details about what we can […]

2026-06-01 原文 →
AI 资讯

How I Fixed a PHP Version Mismatch on Hostinger Shared Hosting (And What Actually Made It Work)

I spent way too long staring at this error. If you're here, you probably are too. Your requirements could not be resolved to an installable set of packages. Problem 1 - Root composer.json requires php ^8.3 but your php version (8.2.30) does not satisfy that requirement. My Laravel 13 app needed PHP 8.3. My Hostinger server was running 8.2. composer install refused to budge. Here's exactly what happened and the one-liner that fixed it. The Setup I was deploying a Laravel 13 + Inertia + React app to Hostinger shared hosting. Laravel 13 requires PHP 8.3 minimum — and so do its locked Symfony 8.x and PHPUnit 12.x dependencies. My composer.lock had been generated on a local machine with PHP 8.3, but Hostinger's CLI was defaulting to 8.2. The hPanel showed PHP 8.3 selected under PHP Configuration . The website itself was running fine on 8.3. But SSH? Still on 8.2. $ php -v PHP 8.2.30 ( cli ) That disconnect — hPanel vs. CLI — is the trap. What I Tried First composer update My first instinct was to just let Composer resolve newer compatible versions: composer update No luck. The root composer.json itself declared "php": "^8.3" , so Composer refused before even touching the lock file. The PHP constraint wasn't just in dependencies — it was in my own project requirements. composer install --ignore-platform-reqs This flag skips platform checks and forces the install anyway. It works , but it's a lie — you end up with packages that may behave incorrectly or fail at runtime because they genuinely require PHP 8.3 features. Not a real fix. Changing PHP in hPanel Hostinger's control panel has a PHP version switcher under Hosting → Manage → PHP Configuration . I had already set this to 8.3. This controls the web server / FPM version — what runs your .php files in the browser. It does not change what php points to in your SSH terminal. That's the key distinction most tutorials miss. What Actually Fixed It Hostinger installs multiple PHP versions in parallel. They live in /opt/alt/ph

2026-06-01 原文 →
AI 资讯

When WP-CLI fatals on the plugin you came to rescue

A WordPress plugin update breaks the site. You SSH in to roll back the bad plugin with WP-CLI, and you get this: Fatal error : Uncaught Error : ... in / path / to / broken - plugin / main . php : 42 The plugin you came to fix has now stopped the tool you came to fix it with. It looks contradictory, but it makes sense once you know how WP-CLI starts up — and there's a flag pair that gets you out. Why WP-CLI itself crashes When you run a rollback command like wp plugin install <name> --version=X --force , WP-CLI internally boots WordPress before doing anything else . Plugin registration and option loading all happen during WordPress's startup, so a broken plugin gets loaded there, throws a fatal, and takes the WP-CLI process down with it. The sequence: WP-CLI boots WordPress WordPress loads the active plugins The broken plugin throws a fatal WP-CLI exits before ever reaching the file-replace step The actual rollback (downloading the PHAR, overwriting the plugin directory) never gets a chance to run. The fix — safe-mode flags WP-CLI has two startup flags, --skip-plugins and --skip-themes . With both set, WordPress's startup skips loading any plugins and themes at all . wp plugin install <name> --version = X --force --skip-plugins --skip-themes File-system operations (downloading the PHAR, unpacking it, replacing files) don't depend on plugin code, so they run fine. The broken plugin never gets loaded at boot, so it never fatals, and the rollback completes. Should you set these flags everywhere? You might think "why not just add these to every WP-CLI command by default?" But some commands genuinely need plugins or themes loaded. wp cache flush relies on the object-cache plugin's hooks. wp doctor reads diagnostic information that plugins register. Setting safe-mode flags on those would break them in subtle ways. The practical split: file-operation commands always get --skip-plugins --skip-themes . Cache and diagnostic commands don't. That single rule eliminates the worst

2026-05-31 原文 →
AI 资讯

Adding a full docker setup to the Filament Mastery Starters

For a while, my starter kits didn't include any Docker configuration. The foundation was solid with auth, roles, MFA, Horizon, Logs Viewer, but the deployment side was left to whoever cloned the project. That was a deliberate choice at first. Docker setups vary a lot depending on the infrastructure: some people use a reverse proxy, others have Cloudflare in front, some run on bare metal, others on managed platforms. I didn't want to ship something that would need to be ripped out immediately. But over time I changed my mind. Here's why and what the process taught me. The problem with "just configure it yourself" Leaving deployment out of a starter kit sounds reasonable. In practice, it means every project starts with the same 4-6 hours of Docker work that never really changes. Multi-stage Dockerfile. PHP-FPM config. Nginx with HTTPS. PostgreSQL and Redis wired up. Horizon and the scheduler running as proper services. Healthchecks everywhere so Docker knows when things are actually ready. None of it is so complicated. But it's time-consuming, easy to get subtly wrong, and almost identical from one project to the next. Once I admitted that, the question wasn't whether to include Docker, it was how to do it in a way that's actually useful without being too opinionated about production infrastructure. What I ended up building The setup I settled on covers the full local development stack: A multi-stage Dockerfile : separate stages for Composer dependencies, Node assets, and the final PHP-FPM image. Keeps the production image lean. Nginx with HTTP-to-HTTPS redirect and a self-signed certificate for local dev, already included, no setup needed. PostgreSQL and Redis as services with proper healthchecks. Horizon and the scheduler as dedicated services, not crammed into the main app container. A bootstrap service that runs php artisan migrate --force before the app starts. The Dockerfile uses three stages to keep the final image as lean as possible: FROM php:8.4-fpm-alpine A

2026-05-29 原文 →
AI 资讯

Meet phpvm: The PHP Version Manager for Linux (v2.5.1 Released)

Every Linux PHP developer knows the dance. You need to switch from PHP 8.1 to 8.3. You run your sudo commands, update your global symlinks, and then realize your local development server in the other window just crashed because it was running on the old version. Why should managing PHP versions be a system-wide struggle? The Solution: Per-Shell Version Isolation phpvm brings the seamless developer experience of tools like pyenv , rbenv , or nvm to the PHP ecosystem on Linux. Instead of changing /usr/bin/php globally, it uses a lightweight shim directory prepended to your PATH . When you call php , the shim inspects your environment variables and forwards the execution to the correct binary. It supports three layers of resolution, falling back gracefully: Shell pin : Pinned manually via phpvm shell <version> Project default : Resolved from .php-version or composer.json requirements when you cd into a directory Global default : The system fallback managed by update-alternatives Effortless Provisioning No need to look up repository installation guides. The built-in installer automatically detects your distribution (Ubuntu or Debian) and configures the appropriate upstream repositories (Ond?ej Sur�'s PPA or deb.sury.org ) to fetch the exact CLI and FPM packages you need. Polish in v2.5.1 Our latest release focuses on making the environment rock-solid: Tray App Auto-Start : Spawns the GTK desktop tray app immediately after installation by resolving the graphical session environment from active processes. PATH Priority : Actively prevents IDEs, login shells, or snap profiles from overriding the shim's position in PATH . Clean Cleanup : Ensures all background processes are terminated during uninstallation. Getting Started You can install or upgrade using the interactive script: curl -fsSL https://raw.githubusercontent.com/rijverse/phpvm/main/install.sh | sudo bash If you are already running v2.5.0, simply run: phpvm --self-update Check out the project website, or find the

2026-05-29 原文 →