{"schemaVersion":"1.0","type":"Article","types":["Article"],"slug":"wp-bones-3-0-when-your-plugin-says-nothing-qrxvk","url":"https://zyvop.com/wp-bones-3-0-when-your-plugin-says-nothing-qrxvk","title":"WP Bones 3.0 — when your plugin says nothing","subtitle":"A major release: migrations run once per site, what declares no permission is for administrators, forms carry a nonce, and generated files leave the plugin folder.","tldr":"WP Bones 3.0 is out. It is a major release, and the theme is a single question: what does the framework do when your plugin says nothing? In 2.x the answer was...","keywords":["plugins","wordpress","WordPress REST API","Web Development","WordPress"],"entities":["Giovambattista Fazioli","Senior Full-stack Engineer, Lead Developer","plugins","wordpress","WordPress REST API","Web Development","WordPress","ZyVOP"],"keyTakeaways":["In 2.x the answer was usually \"the convenient thing\".","In 3.0 it is the safe thing, and when you want the convenient one, you say so.","Migrations run once per site In 2.x every migration, and every seeder, ran again on each activation and on each update made from the dashboard, inside the request that installed the update, with the old code still in memory."],"headings":["Migrations run once per site","What declares nothing is for administrators","Requests that change something carry a nonce","Generated files leave the plugin folder","One command to start","Upgrading"],"outboundLinks":["https://wpbones.com/docs","https://github.com/wpbones/WPBones"],"contentText":"WP Bones 3.0 is out. It is a major release, and the theme is a single question: what does the framework do when your plugin says nothing? In 2.x the answer was usually \"the convenient thing\". In 3.0 it is the safe thing, and when you want the convenient one, you say so. Migrations run once per site In 2.x every migration, and every seeder, ran again on each activation and on each update made from the dashboard, inside the request that installed the update, with the old code still in memory. An update by ZIP, FTP, git or Composer never ran them at all. In 3.0 each migration runs once per site, in file-name order, and its name is recorded in an option. The pending ones run when the plugin is activated, on the first request after its Version header changes, however the update arrived, or when you run php bones migrate. A lock keeps two requests from running them at once, and a failure stops the run and tells the administrators until it runs. Seeders are gone: seed data is a migration now. This design came out of a long thread on GitHub issue #40 with @balazsnasz, who pushed for a real record of what ran. Thank you. What declares nothing is for administrators A page in config/routes.php or pages/, or a menu in config/menus.php, that declares no capability now needs manage_options. A REST route without a permission_callback refuses every request with WordPress's own rest_forbidden. If a page is meant for every logged-in user, or a route is public on purpose, say so: 'capability' =&gt; 'read', 'permission_callback' =&gt; '__return_true'. Requests that change something carry a nonce In 2.x a form on another site could post to a WP Bones admin page with an administrator's cookies, and the page would run its controller. In 3.0 every request to an admin page that is not a GET carries the plugin's nonce: your forms print it with $plugin-&gt;csrfField(), and the check runs before any load callback or controller method. Logged-in Ajax actions need a $nonceHash, and make:ajax now sets one for you. Generated files leave the plugin folder Compiled Blade views and log files used to live inside the plugin folder, which the web server serves. They now go to wp-content/uploads/wpbones/&lt;your plugin&gt;/, behind an .htaccess, and the log files carry a name keyed with your site's salt, so they cannot be guessed on servers that ignore .htaccess. The \"What changed\" table of the WP Bones 3.0 upgrade guide: migrations, seeders, pages and menus, REST routes, POST to an admin page, logged Ajax actions, useHTTPPost(), logs and compiled Blade views, each with its 2.x and 3.0 behaviour One command to start php bones migrate:to-v3 converts your migrations and seeders, then lists what it leaves to you: every page, menu and REST route that declares nothing, every POST form without the nonce, every Ajax provider without one. It rewrites nothing on that list: who may open a page is your decision. Upgrading Read the upgrade guide first, then composer require \"wpbones/wpbones:^3.0\" and php bones migrate:to-v3. A plugin that stays on ^2 keeps receiving 2.1.3. PHP stays at 8.1+, React stays at 18, and the build does not change. The 14 boilerplates and their Playground demos are already on 3.0, the suite has 350 tests, and the full notes are on GitHub. WP Bones is free and open source (LGPL-3.0), runs on PHP 8.1+, and installs with Composer. Website: wpbones.com Docs: https://wpbones.com/docs GitHub: https://github.com/wpbones/WPBones","contentHash":"sha256:8c4514e721b4d629da8e9bac144b8caf1913b5596541ae3f1a47083f79ab3460","authorName":"Giovambattista Fazioli","authorUrl":"https://zyvop.com/author/giovambattista","authorSameAs":["https://gfazioli.github.io","https://github.com/gfazioli","https://x.com/gfazioli","https://www.linkedin.com/in/giovambattistafazioli/"],"category":"Web Development","tags":["plugins","wordpress","WordPress REST API"],"audience":"Developers, software engineers, and students learning Web Development","tone":"Professional, senior full-stack engineer, lead developer perspective","readingTimeMinutes":3,"wordCount":616,"faqs":null,"primaryTopic":"Web Development","publishedAt":"2026-10-07T09:56:25.398Z","updatedAt":"2026-10-07T09:57:51.363Z","canonicalUrl":"https://zyvop.com/wp-bones-3-0-when-your-plugin-says-nothing-qrxvk"}