From aa39116c4bd5205c279b32510a6e0785a246697d Mon Sep 17 00:00:00 2001 From: Alex Kirk Date: Sat, 16 Mar 2024 06:16:27 +0100 Subject: [PATCH 1/2] Add the Hooks Documentation in the Wiki --- bin/extract-hooks.php | 239 ++++++++++++++++++++++++++++++++ includes/class-mastodon-api.php | 22 +++ 2 files changed, 261 insertions(+) create mode 100644 bin/extract-hooks.php diff --git a/bin/extract-hooks.php b/bin/extract-hooks.php new file mode 100644 index 00000000..b5a7a8e6 --- /dev/null +++ b/bin/extract-hooks.php @@ -0,0 +1,239 @@ +getExtension() !== 'php' ) { + continue; + } + $dir = substr( $file->getPath(), $b ); + $main_dir = strtok( $dir, '/' ); + if ( in_array( + $main_dir, + array( + 'tests', + ) + ) ) { + continue; + } + $tokens = token_get_all( file_get_contents( $file ) ); + foreach ( $tokens as $i => $token ) { + if ( ! is_array( $token ) || ! isset( $token[1] ) ) { + continue; + } + if ( ! in_array( ltrim( $token[1], '\\' ), array( 'apply_filters', 'do_action' ) ) ) { + continue; + } + + $comment = false; + $hook = false; + + for ( $j = $i; $j > max( 0, $i - 10 ); $j-- ) { + if ( ! is_array( $tokens[ $j ] ) ) { + continue; + } + + if ( T_DOC_COMMENT === $tokens[ $j ][0] ) { + $comment = $tokens[ $j ][1]; + break; + } + + if ( T_COMMENT === $tokens[ $j ][0] ) { + $comment = $tokens[ $j ][1]; + break; + } + } + + for ( $j = $i + 1; $j < $i + 10; $j++ ) { + if ( ! is_array( $tokens[ $j ] ) ) { + continue; + } + + if ( T_CONSTANT_ENCAPSED_STRING === $tokens[ $j ][0] ) { + $hook = trim( $tokens[ $j ][1], '"\'' ); + break; + } + } + + if ( + $hook + && ! in_array( $hook, $ignore_filters ) + && ! preg_match( '/^(friends_)/', $hook ) + + ) { + if ( ! isset( $filters[ $hook ] ) ) { + $filters[ $hook ] = array( + 'files' => array(), + 'section' => $file->getFilename(), + 'type' => $token[1], + ); + } + + $filters[ $hook ]['files'][] = $dir . '/' . $file->getFilename() . ':' . $token[2]; + $filters[ $hook ]['base_dirs'][ $main_dir ] = true; + + if ( $comment ) { + $docblock = parse_docblock( $comment ); + if ( ! empty( $docblock['comment'] ) && ! preg_match( '#^Documented in#i', $docblock['comment'] ) ) { + $filters[ $hook ] = array_merge( $docblock, $filters[ $hook ] ); + } + } + } + } +} +uksort( + $filters, + function ( $a, $b ) use ( $filters ) { + if ( $filters[ $a ]['section'] === $filters[ $b ]['section'] ) { + return $a < $b ? -1 : 1; + } + return $filters[ $a ]['section'] < $filters[ $b ]['section'] ? -1 : 1; + } +); + +function parse_docblock( $raw_comment ) { + // Adapted from https://github.com/kamermans/docblock-reflection. + $tags = array(); + $lines = explode( PHP_EOL, trim( $raw_comment ) ); + $matches = null; + $comment = ''; + + switch ( count( $lines ) ) { + case 1: + // Handle single-line docblock. + if ( ! preg_match( '#\\/\\*\\*([^*]*)\\*\\/#', $lines[0], $matches ) ) { + return array( + 'comment' => trim( ltrim( $lines[0], "/ \t" ) ), + ); + } + $lines[0] = \substr( $lines[0], 3, -2 ); + break; + + case 2: + // Probably malformed. + return array(); + + default: + // Handle multi-line docblock. + array_shift( $lines ); + array_pop( $lines ); + break; + } + + foreach ( $lines as $line ) { + $line = preg_replace( '#^[ \t]*\* ?#', '', $line ); + + if ( preg_match( '#@([^ ]+)(.*)#', $line, $matches ) ) { + $tag_name = $matches[1]; + $tag_value = \trim( $matches[2] ); + + // If this tag was already parsed, make its value an array. + if ( isset( $tags[ $tag_name ] ) ) { + if ( ! \is_array( $tags[ $tag_name ] ) ) { + $tags[ $tag_name ] = array( $tags[ $tag_name ] ); + } + + $tags[ $tag_name ][] = $tag_value; + } else { + $tags[ $tag_name ] = $tag_value; + } + continue; + } + + $comment .= "$line\n"; + } + $ret = array_filter( + array_merge( + $tags, + array( + 'comment' => trim( $comment ), + ) + ) + ); + if ( empty( $ret ) ) { + return array(); + } + + return $ret; +} + +$docs = $base . '/../enable-mastodon-apps.wiki/'; +if ( ! file_exists( $docs ) ) { + mkdir( $docs, 0777, true ); +} + +$index = ''; +$section = ''; +foreach ( $filters as $hook => $data ) { + if ( $section !== $data['section'] ) { + $section = $data['section']; + $index .= PHP_EOL . '## ' . $section . PHP_EOL . PHP_EOL; + } + $doc = ''; + $index .= "- [`$hook`]($hook)"; + + if ( ! empty( $data['comment'] ) ) { + $index .= ' ' . strtok( $data['comment'], PHP_EOL ); + $doc .= PHP_EOL . $data['comment'] . PHP_EOL . PHP_EOL; + } + + $index .= PHP_EOL; + + if ( ! empty( $data['param'] ) ) { + $doc .= "## Parameters\n"; + foreach ( (array) $data['param'] as $param ) { + $p = explode( ' ', $param, 3 ); + if ( '\\' === substr( $p[0], 0, 1 ) ) { + $p[0] = substr( $p[0], 1 ); + } elseif ( ! in_array( $p[0], array( 'int', 'string', 'bool', 'array' ) ) ) { + $p[0] = 'Enable_Mastodon_Apps\\' . $p[0]; + } + $doc .= "\n- *`{$p[0]}`* `{$p[1]}` {$p[2]}"; + } + + $doc .= PHP_EOL . PHP_EOL; + } + + if ( ! empty( $data['return'] ) ) { + $doc .= "## Returns\n"; + $p = explode( ' ', $data['return'], 2 ); + if ( '\\' === substr( $p[0], 0, 1 ) ) { + $p[0] = substr( $p[0], 1 ); + } elseif ( ! in_array( $p[0], array( 'int', 'string', 'bool', 'array' ) ) ) { + $p[0] = 'Enable_Mastodon_Apps\\' . $p[0]; + } + $doc .= "\n`{$p[0]}` {$p[1]}"; + + $doc .= PHP_EOL . PHP_EOL; + } + + $doc .= "## Files\n\n"; + foreach ( $data['files'] as $file ) { + $doc .= "- [$file](https://github.com/akirk/enable-mastodon-apps/blob/main/" . str_replace( ':', '#L', $file ) . ")\n"; + } + $doc .= "\n\n[Hooks](Hooks)\n"; + + file_put_contents( + $docs . "/$hook.md", + $doc + ); + +} +file_put_contents( + $docs . '/Hooks.md', + $index +); + +echo 'Genearated ' . count( $filters ) . ' hooks documentation files.' . PHP_EOL; diff --git a/includes/class-mastodon-api.php b/includes/class-mastodon-api.php index f82edb2c..5929d54b 100644 --- a/includes/class-mastodon-api.php +++ b/includes/class-mastodon-api.php @@ -2326,6 +2326,28 @@ public function api_preferences( $request ) { public function api_account( $request ) { $user_id = $this->get_user_id_from_request( $request ); + /** + * Modify the account data returned for `/api/account/{user_id}` requests. + * + * @param Entity\Account|null $account The account data. + * @param int $user_id The requested user ID. + * @param \WP_REST_Request $request The request object. + * @return Entity\Account|null The modified account data. + * + * Example: + * ```php + * add_filter( 'mastodon_api_account', function( $user_data, $user_id ) { + * $user = get_user_by( 'ID', $user_id ); + * + * $account = new Account_Entity(); + * $account->id = strval( $user->ID ); + * $account->username = $user->user_login; + * $account->display_name = $user->display_name; + * + * return $account; + * } ); + * ``` + */ $account = \apply_filters( 'mastodon_api_account', null, $user_id, $request ); if ( ! $account instanceof Entity\Account ) { From 53c315d05cb9d348603ff0984e9e14bf0b5aba2a Mon Sep 17 00:00:00 2001 From: Alex Kirk Date: Sat, 16 Mar 2024 06:28:41 +0100 Subject: [PATCH 2/2] Fix package name --- bin/extract-hooks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/extract-hooks.php b/bin/extract-hooks.php index b5a7a8e6..64ce4a28 100644 --- a/bin/extract-hooks.php +++ b/bin/extract-hooks.php @@ -2,7 +2,7 @@ /** * Extract hooks from the codebase. * - * @package Friends + * @package Enable_Mastodon_Apps */ $ignore_filters = array(); @@ -236,4 +236,4 @@ function parse_docblock( $raw_comment ) { $index ); -echo 'Genearated ' . count( $filters ) . ' hooks documentation files.' . PHP_EOL; +echo 'Genearated ' . count( $filters ) . ' hooks documentation files in ' . realpath( $docs ) . PHP_EOL; // phpcs:ignore