From 772ba530226f29cbcb52445d2a39f5bf72690087 Mon Sep 17 00:00:00 2001 From: dgw Date: Tue, 18 Aug 2026 10:15:13 -0700 Subject: [PATCH 1/4] Add clearer guidance for filter return values Some shunt-using plugins out there return values like `false` to the shunt filter if their conditions aren't met. This used to work, but was never "correct" according to the API contract. We should have had clearer guidance on this sooner, though. --- docs/development/plugins.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/development/plugins.md b/docs/development/plugins.md index e2be331..d6d118f 100644 --- a/docs/development/plugins.md +++ b/docs/development/plugins.md @@ -125,6 +125,12 @@ The steps to create a filter function are: 1. hook your function to the particular filter, using YOURLS function `yourls_add_filter()`; 1. put your custom function and its hook into a plugin file. +:::warning + +Even when your Filter doesn't alter the passed value in any way, it **must _return_** the unmodified value! + +::: + #### Example of a filter function When a short URL is created with no custom keyword provided, a random (actually, sequential) keyword is generated. Then this keyword goes through [the following filter](https://github.com/YOURLS/YOURLS/blob/1.7/includes/functions.php#L269): @@ -151,6 +157,28 @@ function my_silly_function( $original_keyword ) { } ``` +#### Special case: Shunts + +Certain filter hooks, which have names starting with `shunt_`, are used to let plugins conditionally tell YOURLS it should skip some actions. For example, you can skip logging/click-tracking for users matching a set of conditions defined by a custom function. Shunts are registered like any other filter: + +```php +yourls_add_filter( 'shunt_update_clicks', 'yp_dont_log_conditional' ); +yourls_add_filter( 'shunt_log_redirect', 'yp_dont_log_conditional' ); + +function yp_dont_log_conditional($value = yourls_shunt_default()) { + if (yp_dont_log_conditions_match()) { + return true; + } + return $value; +} +``` + +:::danger + +**Any** non-default return value will trigger the shunt, even `false` or `0`. Unless your plugin's own conditions are met, it should **never** return anything other than the passed-in `$value`. + +::: + ### Advanced syntax for hooks There are different ways to hook functions into actions or filters: read the [advanced hook syntax page](/docs/development/hooks). From 85453d97e41376e9c5731dd489de3736a5dc11f7 Mon Sep 17 00:00:00 2001 From: "yourls-bot[bot]" <104057305+yourls-bot[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 17:20:13 +0000 Subject: [PATCH 2/4] Fix linting issues --- docs/development/plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/plugins.md b/docs/development/plugins.md index d6d118f..19055b1 100644 --- a/docs/development/plugins.md +++ b/docs/development/plugins.md @@ -210,7 +210,7 @@ First we need to create the plugin directory and file: 1. in `user/plugins/` create a new directory, named for instance `my_first_plugin` 1. create a new empty file named `plugin.php` -**ProTip**: On GitHub, clone this skeleton repository to get yours in seconds: https://github.com/YOURLS/plugin-sample +**ProTip**: On GitHub, clone this skeleton repository to get yours in seconds: In this empty `plugin.php` paste the following code: From 8e6693ec2d565777490f65cb227f804ad00b04c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Colombaro?= Date: Tue, 18 Aug 2026 21:32:01 +0200 Subject: [PATCH 3/4] Update link format in plugins documentation --- docs/development/plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/plugins.md b/docs/development/plugins.md index 19055b1..5172210 100644 --- a/docs/development/plugins.md +++ b/docs/development/plugins.md @@ -210,7 +210,7 @@ First we need to create the plugin directory and file: 1. in `user/plugins/` create a new directory, named for instance `my_first_plugin` 1. create a new empty file named `plugin.php` -**ProTip**: On GitHub, clone this skeleton repository to get yours in seconds: +**ProTip**: On GitHub, clone this skeleton repository to get yours in seconds: [YOURLS/plugin-sample](https://github.com/YOURLS/plugin-sample) In this empty `plugin.php` paste the following code: From a6649afabe27b916aff3542d9ccb683cfa75320e Mon Sep 17 00:00:00 2001 From: dgw Date: Tue, 18 Aug 2026 12:42:24 -0700 Subject: [PATCH 4/4] =?UTF-8?q?Thank=20you=20L=C3=A9o!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Léo Colombaro --- docs/development/plugins.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/development/plugins.md b/docs/development/plugins.md index 5172210..16e2500 100644 --- a/docs/development/plugins.md +++ b/docs/development/plugins.md @@ -127,7 +127,7 @@ The steps to create a filter function are: :::warning -Even when your Filter doesn't alter the passed value in any way, it **must _return_** the unmodified value! +Even when your filter doesn't alter the passed value in any way, it **must _return_** the unmodified value! ::: @@ -162,8 +162,8 @@ function my_silly_function( $original_keyword ) { Certain filter hooks, which have names starting with `shunt_`, are used to let plugins conditionally tell YOURLS it should skip some actions. For example, you can skip logging/click-tracking for users matching a set of conditions defined by a custom function. Shunts are registered like any other filter: ```php -yourls_add_filter( 'shunt_update_clicks', 'yp_dont_log_conditional' ); -yourls_add_filter( 'shunt_log_redirect', 'yp_dont_log_conditional' ); +yourls_add_filter('shunt_update_clicks', 'yp_dont_log_conditional'); +yourls_add_filter('shunt_log_redirect', 'yp_dont_log_conditional'); function yp_dont_log_conditional($value = yourls_shunt_default()) { if (yp_dont_log_conditions_match()) {