Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 94 additions & 6 deletions includes/abilities-api.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,103 @@
<?php
declare( strict_types = 1 );

/**
* Abilities API
* Queries registered abilities with flexible filtering options.
* Supports filtering by namespace, meta, and arbitrary ability properties.
*
* Defines functions for managing abilities in WordPress.
* @since 0.2.0
*
* @package WordPress
* @subpackage Abilities_API
* @since 0.1.0
* @param array $args Array of query arguments. Supports property, meta, namespace, and special keys:
* - 'namespace' (string|array): Filter by ability namespace(s).
* - '?meta_key' (bool): Require presence of meta key.
* - '!property' (mixed): Negate match for property.
* - Other keys: Match ability property or meta value.
* @return WP_Ability[] Array of matching abilities.

Check failure on line 15 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Class name \WP_Ability in @return should be referenced via a fully qualified name.
*/
function wp_query_abilities( array $args = array() ): array {

Check failure on line 17 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHP static analysis

Function wp_query_abilities() has parameter $args with no value type specified in iterable type array.
$abilities = wp_get_abilities();
if ( empty( $args ) ) {
/**
* Filter the list of abilities returned by wp_query_abilities when no args are provided.
*
* @since 0.2.0
*
* @param WP_Ability[] $abilities Array of all abilities.

Check failure on line 25 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Class name \WP_Ability in @param should be referenced via a fully qualified name.
* @param array $args Query arguments (empty).
*/
return apply_filters( 'wp_query_abilities', $abilities, $args );
}

$filtered = array();
foreach ( $abilities as $name => $ability ) {
$pass = true;
foreach ( $args as $key => $expected ) {
$negate = false;

Check warning on line 35 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space
$require_key = false;
$property = $key;

Check warning on line 37 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

if ( is_string( $key ) ) {
if ( $key[0] === '!' ) {

Check failure on line 40 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Use Yoda Condition checks, you must.
$negate = true;

Check warning on line 41 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space
$property = substr( $key, 1 );
} elseif ( $key[0] === '?' ) {

Check failure on line 43 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Use Yoda Condition checks, you must.
$require_key = true;
$property = substr( $key, 1 );

Check warning on line 45 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space
}
}

// Namespace filter (special case)

Check warning on line 49 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

This comment is 45% valid code; is this commented out code?
if ( $property === 'namespace' ) {

Check failure on line 50 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Use Yoda Condition checks, you must.
$ability_ns = explode( '/', $ability->get_name() )[0] ?? '';

Check warning on line 51 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space
$expected_ns = (array) $expected;
$match = in_array( $ability_ns, $expected_ns, true );

Check warning on line 53 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space
$pass = $negate ? !$match : $match;

Check failure on line 54 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Expected 1 space after "!"; 0 found

Check warning on line 54 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 8 spaces but found 1 space
if ( ! $pass ) break;

Check failure on line 55 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Inline control structures are not allowed
continue;
}

// Check meta
if ( $ability->get_meta() && array_key_exists( $property, $ability->get_meta() ) ) {
$actual = $ability->get_meta()[ $property ];
} elseif ( method_exists( $ability, 'get_' . $property ) ) {
$actual = $ability->{'get_' . $property}();
} else {
$actual = null;
}

if ( $require_key ) {
$has_key = $actual !== null;

Check failure on line 69 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Use Yoda Condition checks, you must.
$pass = (bool) $has_key === (bool) $expected;

Check warning on line 70 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space
} else {

Check failure on line 71 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

If control structure block found as the only statement within an "else" block. Use elseif instead.
if ( is_array( $expected ) ) {
$actArr = is_array( $actual ) ? $actual : ( null !== $actual ? [ $actual ] : [] );

Check failure on line 73 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Variable "$actArr" is not in valid snake_case format, try "$act_arr"
$pass = ! empty( array_intersect( $expected, $actArr ) );

Check warning on line 74 in includes/abilities-api.php

View workflow job for this annotation

GitHub Actions / Run PHPCS coding standards checks

Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space
} else {
$pass = ( $actual === $expected );
}
}
if ( $negate ) {
$pass = ! $pass;
}
if ( ! $pass ) break;
}
if ( $pass ) {
$filtered[ $name ] = $ability;
}
}

/**
* Filter the list of abilities returned by wp_query_abilities.
*
* @since 0.2.0
*
* @param WP_Ability[] $filtered Array of filtered abilities.
* @param array $args Query arguments.
*/
return apply_filters( 'wp_query_abilities', $filtered, $args );
}


declare( strict_types = 1 );

/**
* Registers a new ability using Abilities API.
Expand Down
90 changes: 90 additions & 0 deletions tests/unit/abilities-api/wpQueryAbilities.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php declare( strict_types=1 );

/**
* Tests for wp_query_abilities function.
*
* @covers wp_query_abilities
*
* @group abilities-api
*/
class Test_Abilities_API_WpQueryAbilities extends WP_UnitTestCase {
public static $test_abilities = array();

public function set_up(): void {
parent::set_up();
self::$test_abilities = array(
'foo/one' => array(
'label' => 'Foo One',
'description' => 'Ability one',
'input_schema' => array(),
'output_schema' => array(),
'execute_callback' => '__return_null',
'permission_callback' => '__return_true',
'meta' => array('type' => 'resource', 'tags' => array('a','b'), 'audience' => array('internal')),
),
'foo/two' => array(
'label' => 'Foo Two',
'description' => 'Ability two',
'input_schema' => array(),
'output_schema' => array(),
'execute_callback' => '__return_null',
'permission_callback' => '__return_true',
'meta' => array('type' => 'tool', 'tags' => array('b','c')),
),
'bar/three' => array(
'label' => 'Bar Three',
'description' => 'Ability three',
'input_schema' => array(),
'output_schema' => array(),
'execute_callback' => '__return_null',
'permission_callback' => '__return_true',
'meta' => array('type' => 'resource', 'audience' => array('external')),
),
);
foreach ( self::$test_abilities as $name => $args ) {
wp_register_ability( $name, $args );
}
}

public function tear_down(): void {
foreach ( array_keys( self::$test_abilities ) as $name ) {
wp_unregister_ability( $name );
}
parent::tear_down();
}

public function test_query_by_namespace() {
$result = wp_query_abilities( array( 'namespace' => 'foo' ) );
$this->assertCount( 2, $result );
foreach ( $result as $ability ) {
$this->assertStringStartsWith( 'foo/', $ability->get_name() );
}
}

public function test_query_by_meta_type() {
$result = wp_query_abilities( array( 'type' => 'resource' ) );
$this->assertCount( 2, $result );
}

public function test_query_by_meta_tags_includes() {
$result = wp_query_abilities( array( 'tags' => array('b') ) );
$this->assertCount( 2, $result );
}

public function test_query_by_audience_internal() {
$result = wp_query_abilities( array( 'audience' => array('internal') ) );
$this->assertCount( 1, $result );
$ability = reset( $result );
$this->assertEquals( 'foo/one', $ability->get_name() );
}

public function test_query_by_meta_key_presence() {
$result = wp_query_abilities( array( '?tags' => true ) );
$this->assertCount( 2, $result );
}

public function test_query_by_negation() {
$result = wp_query_abilities( array( '!type' => 'tool' ) );
$this->assertCount( 2, $result );
}
}
Loading