diff --git a/Plugin.php b/Plugin.php index 693fb6e..2037b00 100644 --- a/Plugin.php +++ b/Plugin.php @@ -192,6 +192,7 @@ private function getStubFiles() : array { self::getVendorDir( 'vendor/php-stubs/wp-cli-stubs' ) . '/wp-cli-stubs.php', self::getVendorDir( 'vendor/php-stubs/wp-cli-stubs' ) . '/wp-cli-commands-stubs.php', self::getVendorDir( 'vendor/php-stubs/wp-cli-stubs' ) . '/wp-cli-i18n-stubs.php', + __DIR__ . '/stubs/globals.php', __DIR__ . '/stubs/overrides.php', ]; } diff --git a/README.md b/README.md index 0f0b9e0..89d44c0 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Further details about plugins can be found on [Psalm's website](https://psalm.de ### Default WordPress stubs -If you do not want to use the default WordPress stubs, which are part of this plugin, `useDefaultStubs` must be set to `false`: +If you do not want to use the default WordPress class/method/function stubs, which are part of this plugin, `useDefaultStubs` must be set to `false`: ```xml @@ -61,7 +61,7 @@ If you do not want to use the default WordPress hooks, which are part of this pl ``` -### Custom stubs +### Custom hooks You can also provide custom hooks: @@ -83,6 +83,30 @@ If a directory is provided, the plugin will search for the following files: The plugin expects a JSON representation of the hooks as per [wp-hooks/generator](https://github.com/wp-hooks/generator). +### WordPress paths + +To help Psalm analyze your project you might need to define some of WordPress' default global constants such as those for paths. + +```xml + + + + +``` + +The following example bootstrap file is for a Bedrock installation: + +```php + Plugin.php + stubs tests diff --git a/stubs/globals.php b/stubs/globals.php new file mode 100644 index 0000000..e720962 --- /dev/null +++ b/stubs/globals.php @@ -0,0 +1,55 @@ + $empty_trash_days */ $empty_trash_days = 30 ); + +define( 'MINUTE_IN_SECONDS', /** @var 60 $minute_in_seconds */ $minute_in_seconds = 60 ); +define( 'HOUR_IN_SECONDS', /** @var 3600 $hour_in_seconds */ $hour_in_seconds = 60 * MINUTE_IN_SECONDS ); +define( 'DAY_IN_SECONDS', /** @var 86400 $day_in_seconds */ $day_in_seconds = 24 * HOUR_IN_SECONDS ); +define( 'WEEK_IN_SECONDS', /** @var 604800 $week_in_seconds */ $week_in_seconds = 7 * DAY_IN_SECONDS ); +define( 'MONTH_IN_SECONDS', /** @var 2592000 $month_in_seconds */ $month_in_seconds = 30 * DAY_IN_SECONDS ); +define( 'YEAR_IN_SECONDS', /** @var 31536000 $year_in_seconds */ $year_in_seconds = 365 * DAY_IN_SECONDS ); + +define( 'KB_IN_BYTES', /** @var 1024 $kb_in_bytes */ $kb_in_bytes = 1024 ); +define( 'MB_IN_BYTES', /** @var 1048576 $mb_in_bytes */ $mb_in_bytes = 1024 * KB_IN_BYTES ); +define( 'GB_IN_BYTES', /** @var 1073741824 $gb_in_bytes */ $gb_in_bytes = 1024 * MB_IN_BYTES ); +define( 'TB_IN_BYTES', /** @var 1099511627776 $tb_in_bytes */ $tb_in_bytes = 1024 * GB_IN_BYTES ); + +// ./wp-includes/wp-db.php + +define( 'OBJECT', /** @var 'OBJECT' $object */ $object = 'OBJECT' ); +define( 'OBJECT_K', /** @var 'OBJECT_K' $object_k */ $object_k = 'OBJECT_K' ); +define( 'ARRAY_A', /** @var 'ARRAY_A' $array_a */ $array_a = 'ARRAY_A' ); +define( 'ARRAY_N', /** @var 'ARRAY_N' $array_n */ $array_n = 'ARRAY_N' ); + +// ./wp-admin/includes/file.php + +define( 'FS_CONNECT_TIMEOUT', /** @var int<0, max> $fs_connect_timeout */ $fs_connect_timeout = 30 ); +define( 'FS_TIMEOUT', /** @var int<0, max> $fs_timeout */ $fs_timeout = 30 ); +define( 'FS_CHMOD_DIR', /** @var int $fs_chmod_dir */ $fs_chmod_dir = 0755 ); +define( 'FS_CHMOD_FILE', /** @var int $fs_chmod_file */ $fs_chmod_file = 0644 ); + +// ./wp-includes/rewrite.php + +define( 'EP_NONE', /** @var 0 $ep_none */ $ep_none = 0 ); +define( 'EP_PERMALINK', /** @var 1 $ep_permalink */ $ep_permalink = 1 ); +define( 'EP_ATTACHMENT', /** @var 2 $ep_attachment */ $ep_attachment = 2 ); +define( 'EP_DATE', /** @var 4 $ep_date */ $ep_date = 4 ); +define( 'EP_YEAR', /** @var 8 $ep_year */ $ep_year = 8 ); +define( 'EP_MONTH', /** @var 16 $ep_month */ $ep_month = 16 ); +define( 'EP_DAY', /** @var 32 $ep_day */ $ep_day = 32 ); +define( 'EP_ROOT', /** @var 64 $ep_root */ $ep_root = 64 ); +define( 'EP_COMMENTS', /** @var 128 $ep_comments */ $ep_comments = 128 ); +define( 'EP_SEARCH', /** @var 256 $ep_search */ $ep_search = 256 ); +define( 'EP_CATEGORIES', /** @var 512 $ep_categories */ $ep_categories = 512 ); +define( 'EP_TAGS', /** @var 1024 $ep_tags */ $ep_tags = 1024 ); +define( 'EP_AUTHORS', /** @var 2048 $ep_authors */ $ep_authors = 2048 ); +define( 'EP_PAGES', /** @var 4096 $ep_pages */ $ep_pages = 4096 ); +define( 'EP_ALL_ARCHIVES', /** @var int-mask $ep_all_archives */ $ep_all_archives = EP_DATE | EP_YEAR | EP_MONTH | EP_DAY | EP_CATEGORIES | EP_TAGS | EP_AUTHORS ); +define( 'EP_ALL', /** @var int-mask $ep_all */ $ep_all = EP_PERMALINK | EP_ATTACHMENT | EP_ROOT | EP_COMMENTS | EP_SEARCH | EP_PAGES | EP_ALL_ARCHIVES ); diff --git a/stubs/overrides.php b/stubs/overrides.php index 353f60e..a024ebe 100644 --- a/stubs/overrides.php +++ b/stubs/overrides.php @@ -1,142 +1,172 @@ |scalar $args - * @return string|null + * @return string|void */ - public function prepare( $query, $args ) { + public function prepare( $query, $args ) {} - } /** - * Undocumented function - * - * @param string? $query - * @param 'OBJECT'|'ARRAY_A'|'ARRAY_N'|'OBJECT_K' $object + * @template TObject of ARRAY_A|ARRAY_N|OBJECT|OBJECT_K + * @param string|null $query + * @psalm-param TObject $object * @return ( - * $object is 'OBJECT' + * TObject is OBJECT * ? list> - * : ( $object is 'ARRAY_A' + * : ( TObject is ARRAY_A * ? list> - * : ( $object is 'ARRAY_N' - * ? list> - * : array> + * : ( TObject is ARRAY_N + * ? list> + * : array> * ) - * ) - * ) + * ) + * )|null */ - public function get_results( $query = null, $object = 'OBJECT' ) { - - } + public function get_results( $query = null, $object = \OBJECT ) {} } - /** - * @return array{path: string, basedir: string, baseurl: string, url: string} + * @return array{ + * path: string, + * url: string, + * subdir: string, + * basedir: string, + * baseurl: string, + * error: string|false, + * } */ -function wp_upload_dir() { - -} - -/** - * @return array{path: string, basedir: string, baseurl: string, url: string} - */ -function wp_get_upload_dir() { - -} +function wp_get_upload_dir() {} /** * @template TFilterValue - * @param string $a + * @param string $hook_name * @psalm-param TFilterValue $value * @return TFilterValue */ -function apply_filters( string $a, $value, ...$args ) {} - -function add_filter( string $filter, callable $function, int $priority = 10, int $args = 1 ) {} - -function add_action( string $filter, callable $function, int $priority = 10, int $args = 1 ) {} - -/** - * @param integer $attachment_id - * @param boolean $skip_filters - * @return array{width?: int, height?: int, sizes?: array, file: string} - */ -function wp_get_attachment_metadata( int $attachment_id, $skip_filters = false ) {} +function apply_filters( string $hook_name, $value, ...$args ) {} /** + * | Component | | + * | ---------------- | - | + * | PHP_URL_SCHEME | 0 | + * | PHP_URL_HOST | 1 | + * | PHP_URL_PORT | 2 | + * | PHP_URL_USER | 3 | + * | PHP_URL_PASS | 4 | + * | PHP_URL_PATH | 5 | + * | PHP_URL_QUERY | 6 | + * | PHP_URL_FRAGMENT | 7 | * + * @template TComponent of (-1|PHP_URL_*) * @param string $url - * @return array{path?: string, scheme?: string, host?: string, port?: int, user?: string, pass?: string, query?: string, fragment?: string} + * @param TComponent $component + * @return ( + * TComponent is -1 + * ? array{ + * scheme?: string, + * host?: string, + * port?: int, + * user?: string, + * pass?: string, + * path?: string, + * query?: string, + * fragment?: string, + * } + * : ( + * TComponent is 2 + * ? int|null + * : string|null + * ) + * )|false */ -function wp_parse_url( string $url ) {} +function wp_parse_url( string $url, int $component = -1 ) {} /** - * * @param string $option * @param mixed $default * @return mixed */ function get_option( string $option, $default = null ) {} - /** + * @return array[] { + * Array of settings error arrays. + * + * @type array ...$0 { + * Associative array of setting error data. * + * @type string $setting Slug title of the setting to which this error applies. + * @type string $code Slug-name to identify the error. Used as part of 'id' attribute in HTML output. + * @type string $message The formatted message text to display to the user (will be shown inside styled + * `
` and `

` tags). + * @type string $type Optional. Message type, controls HTML class. Possible values include 'error', + * 'success', 'warning', 'info'. Default 'error'. + * } + * } + * @psalm-return array + */ +function get_settings_errors( $setting = '', $sanitize = false ) : array {} + +/** * @param string $path - * @param "https"|"http"|"relative"|"rest" $scheme + * @param 'https'|'http'|'relative'|'rest' $scheme * @return string */ -function home_url( string $path = null, $scheme = null ) : string { - -} +function home_url( string $path = '', $scheme = null ) : string {} /** - * - * @template Args of array - * @template Defaults of array - * @psalm-param Args $args - * @psalm-param Defaults $defaults - * @psalm-return Defaults&Args + * @template TArgs of array + * @template TDefaults of array + * @psalm-param TArgs $args + * @psalm-param TDefaults $defaults + * @psalm-return TDefaults&TArgs */ -function wp_parse_args( $args, $defaults ) { -} +function wp_parse_args( $args, $defaults ) {} /** * @param WP_Error|mixed $error * @psalm-assert-if-true WP_Error $error */ -function is_wp_error( $error ) : bool { - -} +function is_wp_error( $error ) : bool {} /** * @template T @@ -145,6 +175,4 @@ function is_wp_error( $error ) : bool { * @param K $column * @return list */ -function wp_list_pluck( array $list, string $column, string $index_key = null ) : array { - -} +function wp_list_pluck( array $list, string $column, string $index_key = null ) : array {} diff --git a/test.php b/test.php index 16826e0..88c6c34 100644 --- a/test.php +++ b/test.php @@ -19,3 +19,11 @@ function filter_upload_dir( array $dir ) : array { add_filter( 'admin_notices', function () { echo 'hi'; } ); + +$uploads = wp_get_upload_dir(); + +$url_host = wp_parse_url( 'https://github.com:443/psalm/psalm-plugin-wordpress?query=1#frag', PHP_URL_HOST ); + +$url_port = wp_parse_url( 'https://github.com:443/psalm/psalm-plugin-wordpress?query=1#frag', PHP_URL_PORT ); + +$url_parts = wp_parse_url( 'https://github.com:443/psalm/psalm-plugin-wordpress?query=1#frag' );