{"id":21611514,"url":"https://github.com/underpin-wp/decision-list-loader","last_synced_at":"2026-05-22T14:02:33.616Z","repository":{"id":47146680,"uuid":"363817838","full_name":"Underpin-WP/decision-list-loader","owner":"Underpin-WP","description":null,"archived":false,"fork":false,"pushed_at":"2021-09-11T17:00:43.000Z","size":21,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-08T01:01:52.241Z","etag":null,"topics":["hacktoberfest","underpin","wordpress"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Underpin-WP.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-03T04:41:02.000Z","updated_at":"2021-10-25T18:33:08.000Z","dependencies_parsed_at":"2022-08-24T14:37:37.290Z","dependency_job_id":null,"html_url":"https://github.com/Underpin-WP/decision-list-loader","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Underpin-WP%2Fdecision-list-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Underpin-WP%2Fdecision-list-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Underpin-WP%2Fdecision-list-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Underpin-WP%2Fdecision-list-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Underpin-WP","download_url":"https://codeload.github.com/Underpin-WP/decision-list-loader/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244257304,"owners_count":20424131,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","underpin","wordpress"],"created_at":"2024-11-24T21:12:59.626Z","updated_at":"2026-05-22T14:02:33.533Z","avatar_url":"https://github.com/Underpin-WP.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Underpin decision list Loader\n\nLoader That assists with adding decision lists to a WordPress website.\n\n## Installation\n\n### Using Composer\n\n`composer require underpin/decision-list-loader`\n\n### Manually\n\nThis plugin uses a built-in autoloader, so as long as it is required _before_\nUnderpin, it should work as-expected.\n\n`require_once(__DIR__ . '/underpin-decision-lists/decision-lists.php');`\n\n## Setup\n\n1. Install Underpin. See [Underpin Docs](https://www.github.com/underpin-wp/underpin)\n1. Register new decision lists as-needed.\n\n## Decision Lists\n\nTypically, WordPress plugins rely solely on WordPress hooks to determine extended logic. This works for simple\nsolutions, but it becomes cumbersome very fast as soon as several plugins are attempting to override one-another. The\nbiggest issue is that the actual logic that determines the decision is _decentralized_. Since there isn't a single\nsource-of-truth to dictate the order of logic, let alone what the actual _choices are_, you have no easy way of\nunderstanding _why_ a plugin decided to-do what it did.\n\nDecision lists aim to make this easier to work with by making the extensions all _centralized_ in a single registry.\nThis registry is exported in the Underpin console when `WP_DEBUG` is enabled, so it is abundantly clear _what_ the\nactual hierarchy is for this site.\n\nIf you're debugging a live site, you can output the decision list using a PHP console tool, such as debug bar console.\n\n ```php\n var_dump(plugin_name_replace_me()-\u003edecision_lists()-\u003eget('email'));\n```\n\n### Set Up\n\nFundamentally a Decision List is nothing more than a loader class, and can be treated in the same way.\n\nLet's say we wanted to create a decision list that allows people to override an email address in a plugin. This would\nneed to check an options value for an email address, and fallback to a hard-coded address. It also needs to be possible\nto override this value with other plugins.\n\nIn traditional WordPress, you would probably see this done using `apply_filters` at the end of the function, something\nlike this:\n\n ```php\nfunction get_email_address(){\n  \n  return apply_filters('plugin_name_replace_me_email_address',get_option('email_address', 'admin@webmaster.com'));\n}\n```\n\nWith a decision list, however, this is put inside of a class, and that class can be extended. Like so:\n\n```php\n\n/**\n * Class Email To\n * Class Email to list\n *\n * @since   1.1.0\n * @package DFS_Monitor\\Factories\n */\nclass Email_To extends Decision_List {\n\n\tpublic $dedecision listion = 'Determines which email address this plugin should use.';\n\tpublic $name = 'Email Address';\n\n\t/**\n\t * @inheritDoc\n\t */\n\tprotected function set_default_items() {\n\n\t\t$this-\u003eadd( 'option', new class extends Integration_Frequency_Decision {\n\n\t\t\tpublic $id = 'option';\n\t\t\tpublic $name = 'Option Value';\n\t\t\tpublic $dedecision listion = 'Uses the value of the db option, if it is set.';\n\t\t\tpublic $priority = 100;\n\n\n\t\t\tpublic function is_valid( $params = [] ) {\n\t\t\t\tif(!is_email(get_option('email_address'))){\n\t\t\t\t  return plugin_name_replace_me()-\u003elogger()-\u003elog(\n\t\t\t\t    'notice',\n                    'email_address_option_invalid',\n                    'A decision tree did not use the option value because it is not set.'\n                  );\n                } else{\n                  return true;  \n              }             \n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @inheritDoc\n\t\t\t */\n\t\t\tpublic function valid_actions( $params = [] ) {\n\t\t\t\treturn get_option('email_address');\n\t\t\t}\n\t\t} );\n\n\n\t\t$this-\u003eadd( 'hard_coded', new class extends Integration_Frequency_Decision {\n\n\t\t\tpublic $id = 'hard_coded';\n\t\t\tpublic $name = 'Hard coded email';\n\t\t\tpublic $dedecision listion = 'Uses a hard-coded email address for this site.';\n\t\t\tpublic $priority = 1000;\n\n\t\t\tpublic function is_valid( $params = [] ) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tpublic function valid_actions( $params = [] ) {\n\t\t\t\treturn 'admin@webmaster.com';\n\t\t\t}\n\t\t} );\n\t}\n}\n```\n\nNotice that I'm using anonymous classes here, just to keep everything in a single file. You absolutely _do not_ have to\nuse anonymous classes. In fact, in most cases you shouldn't. If you pass a reference to the class as a string, it will\nnot instantiate the class unless it's explicitly called. This saves on resources and keeps things fast.\n\nThe `$priority` value inside each class tells the decision tree which option to try to use first. If it returns\na `WP_Error`, it moves on to the next one. As soon as it finds an option that returns `true`, it grabs the value from\nthe `valid_actions` method, and move on.\n\nLike the custom logger class, this needs to be registered inside `Service_Locator`.\n\n```php\n\t/**\n\t * Set up active loader classes.\n\t *\n\t * This is where you can add anything that needs \"registered\" to WordPress,\n\t * such as shortcodes, rest endpoints, blocks, and cron jobs.\n\t *\n\t * All supported loaders come pre-packaged with this plugin, they just need un-commented here\n\t * to begin using.\n\t *\n\t * @since 1.0.0\n\t */\n\tprotected function _setup() {\n      plugin_name_replace_me()-\u003edecision_lists()-\u003eadd('email', '\\Plugin_Name_Replace_Me\\Decision_Lists\\Email_To');\n\t}\n```\n\nFinally, we can use this decision list directly in our `get_email_address` function:\n\n ```php\nfunction get_email_address(){\n  \n  // Decide which action we should take.\n  $decide = plugin_name_replace_me()-\u003edecision_lists()-\u003eget('email')-\u003edecide();\n\n  // Return the valid decision.\n  if(!is_wp_error($decide) \u0026\u0026 $decide['decision'] instanceof Decision){\n    return $decide['decision']-\u003evalid_actions();\n  }\n\n  // Bubble up the error, otherwise.\n  return $decide;\n}\n```\n\nNow that we have this set up, it can be extended by other plugins using the `add` method. The example below would force\nthe decision list to run this _before_ any other option.\n\n```php\nplugin_name_replace_me()-\u003edecision_lists()-\u003eget('email')-\u003eadd('custom_option',new class extends \\Underpin\\Abstracts\\Decision{\n\n  // Force this to run before all other options\n  public $priority = 50;\n  public $name = 'Custom Option Name';\n  public $dedecision listion = 'This custom name is used in an extension, and overrides the default';\n\n  public function is_valid($params = []){\n    // TODO: Implement is_valid() method.\n  }\n\n  public function valid_actions($params = []){\n  // TODO: Implement valid_actions() method.\n  }\n\n\n});\n```\n\n## Example\n\nA very basic example could look something like this.\n\n```php\n\\Underpin\\underpin()-\u003edecision_lists()-\u003eadd( 'example_decision_list', [\n\t// Decision one\n\t[\n\t\t'valid_callback'         =\u003e '__return_true',\n\t\t'valid_actions_callback' =\u003e '__return_empty_string',\n\t\t'name'                   =\u003e 'Test Decision',\n\t\t'description'            =\u003e 'A single decision',\n\t\t'priority'               =\u003e 500,\n\t],\n\n\t// Decision two\n\t[\n\t\t'valid_callback'         =\u003e '__return_true',\n\t\t'valid_actions_callback' =\u003e '__return_empty_array',\n\t\t'name'                   =\u003e 'Test Decision Two',\n\t\t'description'            =\u003e 'A single decision',\n\t\t'priority'               =\u003e 1000,\n\t],\n] );\n```\n\nAlternatively, you can extend `decision list` and reference the extended class directly, like so:\n\n```php\nunderpin()-\u003edecision_lists()-\u003eadd('key','Namespace\\To\\Class');\n```\n\nThis is especially useful when using decision lists, since they have a tendency to get quite long, and nest deep.\n\n## Getting Decision Results\n\nWhen a decision list determines an action, it does three things:\n\n1. Sorts all decisions by priority, smallest numbers first.\n1. Loops through each item, and stops on the first decision that passes their respective test.\n1. Returns the result of the decision's `valid_actions` callback.\n\nThe example above would return `''`, because the first item to pass would be the item with the smallest priority that will pass.\n\n```php\nunderpin()-\u003edecision_lists()-\u003edecide('example_decision_list', [] );\n```\n\nThe second argument, `$params` is an array of arguments that are passed to both the `valid_callback` and `valid_actions_callback`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funderpin-wp%2Fdecision-list-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funderpin-wp%2Fdecision-list-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funderpin-wp%2Fdecision-list-loader/lists"}