├── .gitignore
├── composer.json
├── README.md
├── includes
├── Plugin.php
├── ThemeUpdater.php
└── PluginUpdater.php
└── composer.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wp-forge/wp-update-handler",
3 | "description": "A WordPress package for updating custom plugins and themes based on an API response from a custom update server.",
4 | "license": "GPL-2.0-or-later",
5 | "authors": [
6 | {
7 | "name": "Micah Wood",
8 | "email": "micah@wpscholar.com"
9 | }
10 | ],
11 | "autoload": {
12 | "psr-4": {
13 | "WP_Forge\\WPUpdateHandler\\": "includes/"
14 | }
15 | },
16 | "require": {
17 | "wp-forge/helpers": "^2.0"
18 | },
19 | "require-dev": {
20 | "wpscholar/phpcs-standards-wpscholar": "^1.0"
21 | },
22 | "scripts": {
23 | "lint": [
24 | "vendor/bin/phpcs -s --standard=WPScholar ."
25 | ],
26 | "fix": [
27 | "vendor/bin/phpcbf -s --standard=WPScholar ."
28 | ]
29 | },
30 | "config": {
31 | "allow-plugins": {
32 | "dealerdirect/phpcodesniffer-composer-installer": true
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WordPress Update Handler
2 |
3 | A WordPress package for updating custom plugins and themes based on an JSON REST API response from a custom update
4 | server.
5 |
6 | Check out the [WordPress GitHub Release API](https://github.com/wp-forge/worker-wp-github-release-api) repository to
7 | learn how to quickly launch a custom update server that fetches releases from GitHub using Cloudflare Workers.
8 |
9 | ## Plugins
10 |
11 | This package expects your custom plugin info API to respond with the same shape as
12 | the [WordPress plugin info API](https://codex.wordpress.org/WordPress.org_API#Plugins). However, if your API response
13 | has a different shape, you can map fields to those returned by your API.
14 |
15 | ### Usage
16 |
17 | Basic example:
18 |
19 | ```php
20 | setDataMap(
59 | [
60 | 'requires' => 'requires.wp',
61 | 'requires' => 'requires.php',
62 | 'banners.2x' => 'banners.retina',
63 | ]
64 | );
65 |
66 | /*
67 | * Explicitly set specific values that will be provided to WordPress.
68 | */
69 | $pluginUpdater->setDataOverrides(
70 | [
71 | 'banners' => [
72 | '2x' => 'https://my.cdn.com/banner-123-retina.jpg',
73 | '1x' => 'https://my.cdn.com/banner-123.jpg',
74 | ],
75 | 'icons' => [
76 | '2x' => 'https://my.cdn.com/icon-123-retina.jpg',
77 | '1x' => 'https://my.cdn.com/icon-123.jpg',
78 | ],
79 | ]
80 | );
81 |
82 | ```
83 |
84 | ## Themes
85 |
86 | This package expects your custom theme info API to respond with the same shape as
87 | the [WordPress theme info API](https://codex.wordpress.org/WordPress.org_API#Themes). However, if your API response has
88 | a different shape, you can map fields to those returned by your API.
89 |
90 | ### Usage
91 |
92 | Basic example:
93 |
94 | ```php
95 | setDataMap(
134 | [
135 | 'requires' => 'requires.wp',
136 | 'requires' => 'requires.php',
137 | 'banners.2x' => 'banners.retina',
138 | ]
139 | );
140 |
141 | /*
142 | * Explicitly set specific values that will be provided to WordPress.
143 | */
144 | $themeUpdater->setDataOverrides(
145 | [
146 | 'banners' => [
147 | '2x' => 'https://my.cdn.com/banner-123-retina.jpg',
148 | '1x' => 'https://my.cdn.com/banner-123.jpg',
149 | ],
150 | 'icons' => [
151 | '2x' => 'https://my.cdn.com/icon-123-retina.jpg',
152 | '1x' => 'https://my.cdn.com/icon-123.jpg',
153 | ],
154 | ]
155 | );
156 |
157 | ```
158 |
--------------------------------------------------------------------------------
/includes/Plugin.php:
--------------------------------------------------------------------------------
1 | 'Author',
35 | 'author_name' => 'AuthorName',
36 | 'author_uri' => 'AuthorURI',
37 | 'description' => 'Description',
38 | 'domain_path' => 'DomainPath',
39 | 'license' => 'License',
40 | 'license_uri' => 'LicenseURI',
41 | 'name' => 'Name',
42 | 'network' => 'Network',
43 | 'requires_wp' => 'RequiresWP',
44 | 'requires_php' => 'RequiresPHP',
45 | 'text_domain' => 'TextDomain',
46 | 'title' => 'Title',
47 | 'uri' => 'PluginURI',
48 | 'version' => 'Version',
49 | );
50 |
51 | /**
52 | * The absolute path to the plugin file.
53 | *
54 | * @var string
55 | */
56 | protected $file;
57 |
58 | /**
59 | * Plugin file headers.
60 | *
61 | * @var array
62 | */
63 | protected $file_headers;
64 |
65 | /**
66 | * Constructor.
67 | *
68 | * @param string $file The plugin basename, or absolute path to the plugin file.
69 | */
70 | public function __construct( $file ) {
71 |
72 | // If plugin basename is provided, convert to full file path
73 | if ( 0 !== strpos( $file, '/' ) ) {
74 | $file = WP_PLUGIN_DIR . '/' . $file;
75 | }
76 |
77 | $this->file = $file;
78 | }
79 |
80 | /**
81 | * Get the plugin basename.
82 | *
83 | * @return string
84 | */
85 | public function basename() {
86 | return plugin_basename( $this->file );
87 | }
88 |
89 | /**
90 | * Get the absolute path to the plugin file.
91 | *
92 | * @return string
93 | */
94 | public function file() {
95 | return $this->file;
96 | }
97 |
98 | /**
99 | * Get the plugin slug.
100 | *
101 | * @return string
102 | */
103 | public function slug() {
104 | return basename( plugin_dir_path( $this->file ) );
105 | }
106 |
107 | /**
108 | * Get a specific plugin file header.
109 | *
110 | * @param string $name The plugin file header name.
111 | *
112 | * @return string
113 | */
114 | protected function get_file_header( $name ) {
115 | $file_headers = $this->get_file_headers();
116 |
117 | return (string) isset( $file_headers[ $name ] ) ? $file_headers[ $name ] : '';
118 | }
119 |
120 | /**
121 | * Get all plugin file headers.
122 | *
123 | * @return array
124 | */
125 | protected function get_file_headers() {
126 |
127 | if ( isset( $this->file_headers ) ) {
128 | return $this->file_headers;
129 | }
130 |
131 | if ( ! function_exists( 'get_plugin_data' ) ) {
132 | require wp_normalize_path( ABSPATH . '/wp-admin/includes/plugin.php' );
133 | }
134 |
135 | $this->file_headers = get_plugin_data( $this->file );
136 |
137 | return $this->file_headers;
138 | }
139 |
140 | /**
141 | * Magic method for fetching data from plugin file headers.
142 | *
143 | * @param string $name The method name.
144 | * @param array $args The method parameters.
145 | *
146 | * @return string
147 | */
148 | public function __call( $name, $args ) {
149 | $value = '';
150 | if ( $this->offsetExists( $name ) ) {
151 | $value = $this->offsetGet( $name );
152 | }
153 |
154 | return $value;
155 | }
156 |
157 | /**
158 | * Check if array offset exists.
159 | *
160 | * @param string $offset Array key
161 | *
162 | * @return bool
163 | */
164 | #[\ReturnTypeWillChange]
165 | public function offsetExists( $offset ) {
166 | return array_key_exists( $offset, self::HEADERS ) || in_array( $offset, array( 'basename', 'file', 'slug' ), true );
167 | }
168 |
169 | /**
170 | * Get array offset.
171 | *
172 | * @param string $offset Array key
173 | *
174 | * @return string
175 | */
176 | #[\ReturnTypeWillChange]
177 | public function offsetGet( $offset ) {
178 | if ( method_exists( $this, $offset ) ) {
179 | return $this->{$offset}();
180 | }
181 | if ( array_key_exists( $offset, self::HEADERS ) ) {
182 | return $this->get_file_header( self::HEADERS[ $offset ] );
183 | }
184 |
185 | return null;
186 | }
187 |
188 | /**
189 | * Set array value.
190 | *
191 | * @param string $offset Array key
192 | * @param mixed $value Value to set
193 | *
194 | * @throws \Exception If called.
195 | */
196 | #[\ReturnTypeWillChange]
197 | public function offsetSet( $offset, $value ) {
198 | throw new \Exception( 'Setting plugin values is not allowed!' );
199 | }
200 |
201 | /**
202 | * Unset array value.
203 | *
204 | * @param string $offset Array key
205 | *
206 | * @throws \Exception If called.
207 | */
208 | #[\ReturnTypeWillChange]
209 | public function offsetUnset( $offset ) {
210 | throw new \Exception( 'Unsetting plugin values is not allowed!' );
211 | }
212 |
213 | /**
214 | * Convert class instance into an array.
215 | *
216 | * @return array
217 | */
218 | public function toArray() {
219 | $keys = array_merge( array_keys( self::HEADERS ), array( 'basename', 'slug' ) );
220 | asort( $keys );
221 |
222 | $values = array();
223 | foreach ( $keys as $key ) {
224 | $values[ $key ] = $this->offsetGet( $key );
225 | }
226 |
227 | return $values;
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/includes/ThemeUpdater.php:
--------------------------------------------------------------------------------
1 | setTheme( $theme )->setUrl( $url )->registerHooks();
63 | }
64 |
65 | /**
66 | * Set up theme instance.
67 | *
68 | * @param \WP_Theme $theme The theme instance.
69 | *
70 | * @return $this
71 | */
72 | public function setTheme( \WP_Theme $theme ) {
73 | $this->theme = $theme;
74 |
75 | return $this;
76 | }
77 |
78 | /**
79 | * Set the theme update API URL.
80 | *
81 | * @param string $url The theme update API URL.
82 | *
83 | * @return $this
84 | */
85 | public function setUrl( $url ) {
86 | $this->url = $url;
87 |
88 | return $this;
89 | }
90 |
91 | /**
92 | * Set the cache expiration.
93 | *
94 | * @param int $expiration Duration in seconds until cache expires.
95 | *
96 | * @return $this
97 | */
98 | public function setCacheExpiration( $expiration ) {
99 | $this->cacheExpiration = absint( $expiration );
100 |
101 | return $this;
102 | }
103 |
104 | /**
105 | * Set data map.
106 | *
107 | * @param array $map A mapping of API response fields to the expected WP fields.
108 | *
109 | * @return $this
110 | */
111 | public function setDataMap( array $map ) {
112 | $this->dataMap = $map;
113 |
114 | return $this;
115 | }
116 |
117 | /**
118 | * Set data overrides.
119 | *
120 | * @param array $overrides A key-value store of fields to replace with specific values.
121 | *
122 | * @return $this
123 | */
124 | public function setDataOverrides( array $overrides ) {
125 | $this->dataOverrides = $overrides;
126 |
127 | return $this;
128 | }
129 |
130 | /**
131 | * Check if an update is available.
132 | *
133 | * @return bool
134 | */
135 | public function hasUpdate() {
136 | $release = $this->getRelease();
137 |
138 | return isset( $release['version'] ) && version_compare( $release['version'], $this->theme->get( 'Version' ), '>' );
139 | }
140 |
141 | /**
142 | * Fetch details on the latest theme release.
143 | *
144 | * @return array The latest release data.
145 | */
146 | public function getRelease() {
147 | $cache_key = 'wp_theme_update_' . $this->theme->get_stylesheet();
148 | delete_transient( $cache_key );
149 | $payload = get_transient( $cache_key );
150 | if ( ! $payload ) {
151 | $payload = array();
152 | $response = wp_remote_get( $this->url );
153 |
154 | if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
155 | $body = wp_remote_retrieve_body( $response );
156 | if ( $body ) {
157 | $data = json_decode( $body, true );
158 | if ( ! is_null( $data ) ) {
159 | $payload = $this->mapData( $data );
160 | set_transient( $cache_key, $payload, $this->cacheExpiration );
161 | }
162 | }
163 | }
164 | }
165 |
166 | return $payload;
167 | }
168 |
169 | /**
170 | * Normalize data from the API to the expected values.
171 | *
172 | * @param array $data A mapping of the local key to the remote key using dot notation.
173 | *
174 | * @return array
175 | */
176 | public function mapData( array $data ) {
177 |
178 | $author_name = $this->theme->get( 'Author' );
179 | $author_uri = $this->theme->get( 'AuthorURI' );
180 |
181 | $author = ! empty( $author_uri ) ? "{$author_name}" : $author_name;
182 |
183 | $description = dataGet( $data, 'description', $this->theme->get( 'Description' ) );
184 |
185 | $defaults = array(
186 | 'author' => $author,
187 | 'author_name' => $author_name,
188 | 'author_uri' => $author_uri,
189 | 'description' => $description,
190 | 'download_link' => dataGet( $data, 'download_link' ),
191 | 'homepage' => dataGet( $data, 'homepage', $this->theme->get( 'ThemeURI' ) ),
192 | 'name' => $this->theme->get( 'Name' ),
193 | 'theme' => $this->theme->get_stylesheet(),
194 | 'requires' => dataGet( $data, 'requires', $this->theme->get( 'RequiresWP' ) ),
195 | 'requires_php' => dataGet( $data, 'requires_php', $this->theme->get( 'RequiresPHP' ) ),
196 | 'slug' => $this->theme->get_stylesheet(),
197 | 'tested' => dataGet( $data, 'tested' ),
198 | 'version' => dataGet( $data, 'version' ),
199 | );
200 |
201 | $payload = $defaults;
202 |
203 | // Map selected fields
204 | foreach ( $this->dataMap as $key => $target ) {
205 | dataSet( $payload, $key, dataGet( $data, $target ) );
206 | }
207 |
208 | // Override selected fields
209 | foreach ( $this->dataOverrides as $key => $value ) {
210 | dataSet( $payload, $key, $value );
211 | }
212 |
213 | $payload['new_version'] = $payload['version'];
214 | $payload['package'] = $payload['download_link'];
215 | $payload['url'] = $payload['homepage'];
216 |
217 | return $payload;
218 | }
219 |
220 | /**
221 | * Register hooks.
222 | */
223 | protected function registerHooks() {
224 |
225 | add_filter(
226 | 'site_transient_update_themes',
227 | function ( $transient ) {
228 |
229 | if ( empty( $transient ) || ! is_object( $transient ) ) {
230 | return $transient;
231 | }
232 |
233 | $release = $this->getRelease();
234 |
235 | if ( $this->hasUpdate() ) {
236 | $transient->response[ $this->theme->get_stylesheet() ] = $release;
237 | } else {
238 | $transient->no_update[ $this->theme->get_stylesheet() ] = $release;
239 | }
240 |
241 | return $transient;
242 | }
243 | );
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/includes/PluginUpdater.php:
--------------------------------------------------------------------------------
1 | setPlugin( $file )->setUrl( $url )->registerHooks();
64 | }
65 |
66 | /**
67 | * Set up plugin instance.
68 | *
69 | * @param string $file The plugin basename or absolute path to main plugin file.
70 | *
71 | * @return $this
72 | */
73 | public function setPlugin( $file ) {
74 | $this->plugin = new Plugin( $file );
75 |
76 | return $this;
77 | }
78 |
79 | /**
80 | * Set the plugin update API URL.
81 | *
82 | * @param string $url The plugin update API URL.
83 | *
84 | * @return $this
85 | */
86 | public function setUrl( $url ) {
87 | $this->url = $url;
88 |
89 | return $this;
90 | }
91 |
92 | /**
93 | * Set the cache expiration.
94 | *
95 | * @param int $expiration Duration in seconds until cache expires.
96 | *
97 | * @return $this
98 | */
99 | public function setCacheExpiration( $expiration ) {
100 | $this->cacheExpiration = absint( $expiration );
101 |
102 | return $this;
103 | }
104 |
105 | /**
106 | * Set data map.
107 | *
108 | * @param array $map A mapping of API response fields to the expected WP fields.
109 | *
110 | * @return $this
111 | */
112 | public function setDataMap( array $map ) {
113 | $this->dataMap = $map;
114 |
115 | return $this;
116 | }
117 |
118 | /**
119 | * Set data overrides.
120 | *
121 | * @param array $overrides A key-value store of fields to replace with specific values.
122 | *
123 | * @return $this
124 | */
125 | public function setDataOverrides( array $overrides ) {
126 | $this->dataOverrides = $overrides;
127 |
128 | return $this;
129 | }
130 |
131 | /**
132 | * Check if an update is available.
133 | *
134 | * @return bool
135 | */
136 | public function hasUpdate() {
137 | $release = $this->getRelease();
138 |
139 | return isset( $release->version ) && version_compare( $release->version, $this->plugin->version(), '>' );
140 | }
141 |
142 | /**
143 | * Fetch details on the latest plugin release.
144 | *
145 | * @return \stdClass The latest release data.
146 | */
147 | public function getRelease() {
148 | $cache_key = 'wp_plugin_update_' . $this->plugin->slug();
149 | $payload = get_transient( $cache_key );
150 | if ( ! $payload ) {
151 | $payload = new \stdClass();
152 | $response = wp_remote_get( $this->url );
153 |
154 | if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
155 | $body = wp_remote_retrieve_body( $response );
156 | if ( $body ) {
157 | $data = json_decode( $body, true );
158 | if ( ! is_null( $data ) ) {
159 | $payload = $this->mapData( $data );
160 | set_transient( $cache_key, $payload, $this->cacheExpiration );
161 | }
162 | }
163 | }
164 | }
165 |
166 | return $payload;
167 | }
168 |
169 | /**
170 | * Normalize data from the API to the expected values.
171 | *
172 | * @param array $data A mapping of the local key to the remote key using dot notation.
173 | *
174 | * @return \stdClass
175 | */
176 | public function mapData( array $data ) {
177 |
178 | $author_name = $this->plugin->author_name();
179 | $author_uri = $this->plugin->author_uri();
180 |
181 | $author = ! empty( $author_uri ) ? "{$author_name}" : $author_name;
182 |
183 | $description = dataGet( $data, 'description', $this->plugin->description() );
184 |
185 | $defaults = array(
186 | 'author' => $author,
187 | 'author_name' => $author_name,
188 | 'author_uri' => $author_uri,
189 | 'description' => $description,
190 | 'download_link' => dataGet( $data, 'download_link' ),
191 | 'homepage' => dataGet( $data, 'homepage', $this->plugin->uri() ),
192 | 'id' => $this->plugin->basename(),
193 | 'last_updated' => dataGet( $data, 'last_updated' ),
194 | 'name' => $this->plugin->name(),
195 | 'plugin' => $this->plugin->basename(),
196 | 'requires' => dataGet( $data, 'requires', $this->plugin->requires_wp() ),
197 | 'requires_php' => dataGet( $data, 'requires_php', $this->plugin->requires_php() ),
198 | 'sections' => array(
199 | 'description' => $description,
200 | ),
201 | 'short_description' => $description,
202 | 'slug' => $this->plugin->slug(),
203 | 'tested' => dataGet( $data, 'tested' ),
204 | 'version' => dataGet( $data, 'version' ),
205 | );
206 |
207 | $payload = $defaults;
208 |
209 | // Map selected fields
210 | foreach ( $this->dataMap as $key => $target ) {
211 | dataSet( $payload, $key, dataGet( $data, $target ) );
212 | }
213 |
214 | // Override selected fields
215 | foreach ( $this->dataOverrides as $key => $value ) {
216 | dataSet( $payload, $key, $value );
217 | }
218 |
219 | $payload['new_version'] = $payload['version'];
220 | $payload['package'] = $payload['download_link'];
221 | $payload['url'] = $payload['homepage'];
222 |
223 | if ( isset( $payload['banners']['2x'] ) && ! isset( $payload['banners']['high'] ) ) {
224 | $payload['banners']['high'] = $payload['banners']['2x'];
225 | }
226 |
227 | if ( isset( $payload['banners']['1x'] ) && ! isset( $payload['banners']['low'] ) ) {
228 | $payload['banners']['low'] = $payload['banners']['1x'];
229 | }
230 |
231 | return (object) $payload;
232 | }
233 |
234 | /**
235 | * Register hooks.
236 | */
237 | protected function registerHooks() {
238 |
239 | add_action(
240 | 'plugins_api',
241 | function ( $response, $action, $args ) {
242 |
243 | if ( isset( $args->slug ) && $args->slug === $this->plugin->slug() ) {
244 | return $this->getRelease();
245 | }
246 |
247 | return $response;
248 | },
249 | 20,
250 | 3
251 | );
252 |
253 | add_filter(
254 | 'site_transient_update_plugins',
255 | function ( $transient ) {
256 |
257 | if ( empty( $transient ) || ! is_object( $transient ) ) {
258 | return $transient;
259 | }
260 |
261 | $release = $this->getRelease();
262 |
263 | if ( $this->hasUpdate() ) {
264 | $transient->response[ $this->plugin->basename() ] = $release;
265 | } else {
266 | $transient->no_update[ $this->plugin->basename() ] = $release;
267 | }
268 |
269 | return $transient;
270 | }
271 | );
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "2be39584906963f91534d6f7ab38e7a0",
8 | "packages": [
9 | {
10 | "name": "doctrine/inflector",
11 | "version": "1.4.4",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/doctrine/inflector.git",
15 | "reference": "4bd5c1cdfcd00e9e2d8c484f79150f67e5d355d9"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/doctrine/inflector/zipball/4bd5c1cdfcd00e9e2d8c484f79150f67e5d355d9",
20 | "reference": "4bd5c1cdfcd00e9e2d8c484f79150f67e5d355d9",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "php": "^7.1 || ^8.0"
25 | },
26 | "require-dev": {
27 | "doctrine/coding-standard": "^8.0",
28 | "phpstan/phpstan": "^0.12",
29 | "phpstan/phpstan-phpunit": "^0.12",
30 | "phpstan/phpstan-strict-rules": "^0.12",
31 | "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
32 | },
33 | "type": "library",
34 | "extra": {
35 | "branch-alias": {
36 | "dev-master": "2.0.x-dev"
37 | }
38 | },
39 | "autoload": {
40 | "psr-4": {
41 | "Doctrine\\Inflector\\": "lib/Doctrine/Inflector",
42 | "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
43 | }
44 | },
45 | "notification-url": "https://packagist.org/downloads/",
46 | "license": [
47 | "MIT"
48 | ],
49 | "authors": [
50 | {
51 | "name": "Guilherme Blanco",
52 | "email": "guilhermeblanco@gmail.com"
53 | },
54 | {
55 | "name": "Roman Borschel",
56 | "email": "roman@code-factory.org"
57 | },
58 | {
59 | "name": "Benjamin Eberlei",
60 | "email": "kontakt@beberlei.de"
61 | },
62 | {
63 | "name": "Jonathan Wage",
64 | "email": "jonwage@gmail.com"
65 | },
66 | {
67 | "name": "Johannes Schmitt",
68 | "email": "schmittjoh@gmail.com"
69 | }
70 | ],
71 | "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
72 | "homepage": "https://www.doctrine-project.org/projects/inflector.html",
73 | "keywords": [
74 | "inflection",
75 | "inflector",
76 | "lowercase",
77 | "manipulation",
78 | "php",
79 | "plural",
80 | "singular",
81 | "strings",
82 | "uppercase",
83 | "words"
84 | ],
85 | "support": {
86 | "issues": "https://github.com/doctrine/inflector/issues",
87 | "source": "https://github.com/doctrine/inflector/tree/1.4.4"
88 | },
89 | "funding": [
90 | {
91 | "url": "https://www.doctrine-project.org/sponsorship.html",
92 | "type": "custom"
93 | },
94 | {
95 | "url": "https://www.patreon.com/phpdoctrine",
96 | "type": "patreon"
97 | },
98 | {
99 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
100 | "type": "tidelift"
101 | }
102 | ],
103 | "time": "2021-04-16T17:34:40+00:00"
104 | },
105 | {
106 | "name": "wp-forge/helpers",
107 | "version": "2.0",
108 | "source": {
109 | "type": "git",
110 | "url": "https://github.com/wp-forge/helpers.git",
111 | "reference": "28ebc09a3390dbff427270a4ed2b93395e8b9b78"
112 | },
113 | "dist": {
114 | "type": "zip",
115 | "url": "https://api.github.com/repos/wp-forge/helpers/zipball/28ebc09a3390dbff427270a4ed2b93395e8b9b78",
116 | "reference": "28ebc09a3390dbff427270a4ed2b93395e8b9b78",
117 | "shasum": ""
118 | },
119 | "require": {
120 | "doctrine/inflector": "^1.3",
121 | "ext-mbstring": "*"
122 | },
123 | "type": "library",
124 | "autoload": {
125 | "files": [
126 | "includes/functions.php"
127 | ],
128 | "psr-4": {
129 | "WP_Forge\\Helpers\\": "includes"
130 | }
131 | },
132 | "notification-url": "https://packagist.org/downloads/",
133 | "license": [
134 | "GPL-2.0-or-later"
135 | ],
136 | "authors": [
137 | {
138 | "name": "Micah Wood",
139 | "email": "micah@wpscholar.com"
140 | }
141 | ],
142 | "description": "A collection of helpers for WordPress and PHP development.",
143 | "support": {
144 | "issues": "https://github.com/wp-forge/helpers/issues",
145 | "source": "https://github.com/wp-forge/helpers/tree/2.0"
146 | },
147 | "time": "2022-01-06T13:10:47+00:00"
148 | }
149 | ],
150 | "packages-dev": [
151 | {
152 | "name": "dealerdirect/phpcodesniffer-composer-installer",
153 | "version": "v1.0.0",
154 | "source": {
155 | "type": "git",
156 | "url": "https://github.com/PHPCSStandards/composer-installer.git",
157 | "reference": "4be43904336affa5c2f70744a348312336afd0da"
158 | },
159 | "dist": {
160 | "type": "zip",
161 | "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
162 | "reference": "4be43904336affa5c2f70744a348312336afd0da",
163 | "shasum": ""
164 | },
165 | "require": {
166 | "composer-plugin-api": "^1.0 || ^2.0",
167 | "php": ">=5.4",
168 | "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
169 | },
170 | "require-dev": {
171 | "composer/composer": "*",
172 | "ext-json": "*",
173 | "ext-zip": "*",
174 | "php-parallel-lint/php-parallel-lint": "^1.3.1",
175 | "phpcompatibility/php-compatibility": "^9.0",
176 | "yoast/phpunit-polyfills": "^1.0"
177 | },
178 | "type": "composer-plugin",
179 | "extra": {
180 | "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
181 | },
182 | "autoload": {
183 | "psr-4": {
184 | "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
185 | }
186 | },
187 | "notification-url": "https://packagist.org/downloads/",
188 | "license": [
189 | "MIT"
190 | ],
191 | "authors": [
192 | {
193 | "name": "Franck Nijhof",
194 | "email": "franck.nijhof@dealerdirect.com",
195 | "homepage": "http://www.frenck.nl",
196 | "role": "Developer / IT Manager"
197 | },
198 | {
199 | "name": "Contributors",
200 | "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
201 | }
202 | ],
203 | "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
204 | "homepage": "http://www.dealerdirect.com",
205 | "keywords": [
206 | "PHPCodeSniffer",
207 | "PHP_CodeSniffer",
208 | "code quality",
209 | "codesniffer",
210 | "composer",
211 | "installer",
212 | "phpcbf",
213 | "phpcs",
214 | "plugin",
215 | "qa",
216 | "quality",
217 | "standard",
218 | "standards",
219 | "style guide",
220 | "stylecheck",
221 | "tests"
222 | ],
223 | "support": {
224 | "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
225 | "source": "https://github.com/PHPCSStandards/composer-installer"
226 | },
227 | "time": "2023-01-05T11:28:13+00:00"
228 | },
229 | {
230 | "name": "phpcompatibility/php-compatibility",
231 | "version": "9.3.5",
232 | "source": {
233 | "type": "git",
234 | "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
235 | "reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
236 | },
237 | "dist": {
238 | "type": "zip",
239 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
240 | "reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
241 | "shasum": ""
242 | },
243 | "require": {
244 | "php": ">=5.3",
245 | "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
246 | },
247 | "conflict": {
248 | "squizlabs/php_codesniffer": "2.6.2"
249 | },
250 | "require-dev": {
251 | "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
252 | },
253 | "suggest": {
254 | "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
255 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
256 | },
257 | "type": "phpcodesniffer-standard",
258 | "notification-url": "https://packagist.org/downloads/",
259 | "license": [
260 | "LGPL-3.0-or-later"
261 | ],
262 | "authors": [
263 | {
264 | "name": "Wim Godden",
265 | "homepage": "https://github.com/wimg",
266 | "role": "lead"
267 | },
268 | {
269 | "name": "Juliette Reinders Folmer",
270 | "homepage": "https://github.com/jrfnl",
271 | "role": "lead"
272 | },
273 | {
274 | "name": "Contributors",
275 | "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
276 | }
277 | ],
278 | "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
279 | "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
280 | "keywords": [
281 | "compatibility",
282 | "phpcs",
283 | "standards"
284 | ],
285 | "support": {
286 | "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
287 | "source": "https://github.com/PHPCompatibility/PHPCompatibility"
288 | },
289 | "time": "2019-12-27T09:44:58+00:00"
290 | },
291 | {
292 | "name": "phpcompatibility/phpcompatibility-paragonie",
293 | "version": "1.3.2",
294 | "source": {
295 | "type": "git",
296 | "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
297 | "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26"
298 | },
299 | "dist": {
300 | "type": "zip",
301 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/bba5a9dfec7fcfbd679cfaf611d86b4d3759da26",
302 | "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26",
303 | "shasum": ""
304 | },
305 | "require": {
306 | "phpcompatibility/php-compatibility": "^9.0"
307 | },
308 | "require-dev": {
309 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7",
310 | "paragonie/random_compat": "dev-master",
311 | "paragonie/sodium_compat": "dev-master"
312 | },
313 | "suggest": {
314 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
315 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
316 | },
317 | "type": "phpcodesniffer-standard",
318 | "notification-url": "https://packagist.org/downloads/",
319 | "license": [
320 | "LGPL-3.0-or-later"
321 | ],
322 | "authors": [
323 | {
324 | "name": "Wim Godden",
325 | "role": "lead"
326 | },
327 | {
328 | "name": "Juliette Reinders Folmer",
329 | "role": "lead"
330 | }
331 | ],
332 | "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
333 | "homepage": "http://phpcompatibility.com/",
334 | "keywords": [
335 | "compatibility",
336 | "paragonie",
337 | "phpcs",
338 | "polyfill",
339 | "standards",
340 | "static analysis"
341 | ],
342 | "support": {
343 | "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
344 | "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
345 | },
346 | "time": "2022-10-25T01:46:02+00:00"
347 | },
348 | {
349 | "name": "phpcompatibility/phpcompatibility-wp",
350 | "version": "2.1.4",
351 | "source": {
352 | "type": "git",
353 | "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
354 | "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5"
355 | },
356 | "dist": {
357 | "type": "zip",
358 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5",
359 | "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5",
360 | "shasum": ""
361 | },
362 | "require": {
363 | "phpcompatibility/php-compatibility": "^9.0",
364 | "phpcompatibility/phpcompatibility-paragonie": "^1.0"
365 | },
366 | "require-dev": {
367 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7"
368 | },
369 | "suggest": {
370 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
371 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
372 | },
373 | "type": "phpcodesniffer-standard",
374 | "notification-url": "https://packagist.org/downloads/",
375 | "license": [
376 | "LGPL-3.0-or-later"
377 | ],
378 | "authors": [
379 | {
380 | "name": "Wim Godden",
381 | "role": "lead"
382 | },
383 | {
384 | "name": "Juliette Reinders Folmer",
385 | "role": "lead"
386 | }
387 | ],
388 | "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
389 | "homepage": "http://phpcompatibility.com/",
390 | "keywords": [
391 | "compatibility",
392 | "phpcs",
393 | "standards",
394 | "static analysis",
395 | "wordpress"
396 | ],
397 | "support": {
398 | "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
399 | "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
400 | },
401 | "time": "2022-10-24T09:00:36+00:00"
402 | },
403 | {
404 | "name": "phpcsstandards/phpcsextra",
405 | "version": "1.2.1",
406 | "source": {
407 | "type": "git",
408 | "url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
409 | "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489"
410 | },
411 | "dist": {
412 | "type": "zip",
413 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/11d387c6642b6e4acaf0bd9bf5203b8cca1ec489",
414 | "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489",
415 | "shasum": ""
416 | },
417 | "require": {
418 | "php": ">=5.4",
419 | "phpcsstandards/phpcsutils": "^1.0.9",
420 | "squizlabs/php_codesniffer": "^3.8.0"
421 | },
422 | "require-dev": {
423 | "php-parallel-lint/php-console-highlighter": "^1.0",
424 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
425 | "phpcsstandards/phpcsdevcs": "^1.1.6",
426 | "phpcsstandards/phpcsdevtools": "^1.2.1",
427 | "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
428 | },
429 | "type": "phpcodesniffer-standard",
430 | "extra": {
431 | "branch-alias": {
432 | "dev-stable": "1.x-dev",
433 | "dev-develop": "1.x-dev"
434 | }
435 | },
436 | "notification-url": "https://packagist.org/downloads/",
437 | "license": [
438 | "LGPL-3.0-or-later"
439 | ],
440 | "authors": [
441 | {
442 | "name": "Juliette Reinders Folmer",
443 | "homepage": "https://github.com/jrfnl",
444 | "role": "lead"
445 | },
446 | {
447 | "name": "Contributors",
448 | "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
449 | }
450 | ],
451 | "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
452 | "keywords": [
453 | "PHP_CodeSniffer",
454 | "phpcbf",
455 | "phpcodesniffer-standard",
456 | "phpcs",
457 | "standards",
458 | "static analysis"
459 | ],
460 | "support": {
461 | "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
462 | "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy",
463 | "source": "https://github.com/PHPCSStandards/PHPCSExtra"
464 | },
465 | "funding": [
466 | {
467 | "url": "https://github.com/PHPCSStandards",
468 | "type": "github"
469 | },
470 | {
471 | "url": "https://github.com/jrfnl",
472 | "type": "github"
473 | },
474 | {
475 | "url": "https://opencollective.com/php_codesniffer",
476 | "type": "open_collective"
477 | }
478 | ],
479 | "time": "2023-12-08T16:49:07+00:00"
480 | },
481 | {
482 | "name": "phpcsstandards/phpcsutils",
483 | "version": "1.0.9",
484 | "source": {
485 | "type": "git",
486 | "url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
487 | "reference": "908247bc65010c7b7541a9551e002db12e9dae70"
488 | },
489 | "dist": {
490 | "type": "zip",
491 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/908247bc65010c7b7541a9551e002db12e9dae70",
492 | "reference": "908247bc65010c7b7541a9551e002db12e9dae70",
493 | "shasum": ""
494 | },
495 | "require": {
496 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
497 | "php": ">=5.4",
498 | "squizlabs/php_codesniffer": "^3.8.0 || 4.0.x-dev@dev"
499 | },
500 | "require-dev": {
501 | "ext-filter": "*",
502 | "php-parallel-lint/php-console-highlighter": "^1.0",
503 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
504 | "phpcsstandards/phpcsdevcs": "^1.1.6",
505 | "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0"
506 | },
507 | "type": "phpcodesniffer-standard",
508 | "extra": {
509 | "branch-alias": {
510 | "dev-stable": "1.x-dev",
511 | "dev-develop": "1.x-dev"
512 | }
513 | },
514 | "autoload": {
515 | "classmap": [
516 | "PHPCSUtils/"
517 | ]
518 | },
519 | "notification-url": "https://packagist.org/downloads/",
520 | "license": [
521 | "LGPL-3.0-or-later"
522 | ],
523 | "authors": [
524 | {
525 | "name": "Juliette Reinders Folmer",
526 | "homepage": "https://github.com/jrfnl",
527 | "role": "lead"
528 | },
529 | {
530 | "name": "Contributors",
531 | "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
532 | }
533 | ],
534 | "description": "A suite of utility functions for use with PHP_CodeSniffer",
535 | "homepage": "https://phpcsutils.com/",
536 | "keywords": [
537 | "PHP_CodeSniffer",
538 | "phpcbf",
539 | "phpcodesniffer-standard",
540 | "phpcs",
541 | "phpcs3",
542 | "standards",
543 | "static analysis",
544 | "tokens",
545 | "utility"
546 | ],
547 | "support": {
548 | "docs": "https://phpcsutils.com/",
549 | "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
550 | "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy",
551 | "source": "https://github.com/PHPCSStandards/PHPCSUtils"
552 | },
553 | "funding": [
554 | {
555 | "url": "https://github.com/PHPCSStandards",
556 | "type": "github"
557 | },
558 | {
559 | "url": "https://github.com/jrfnl",
560 | "type": "github"
561 | },
562 | {
563 | "url": "https://opencollective.com/php_codesniffer",
564 | "type": "open_collective"
565 | }
566 | ],
567 | "time": "2023-12-08T14:50:00+00:00"
568 | },
569 | {
570 | "name": "squizlabs/php_codesniffer",
571 | "version": "3.9.0",
572 | "source": {
573 | "type": "git",
574 | "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
575 | "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b"
576 | },
577 | "dist": {
578 | "type": "zip",
579 | "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
580 | "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
581 | "shasum": ""
582 | },
583 | "require": {
584 | "ext-simplexml": "*",
585 | "ext-tokenizer": "*",
586 | "ext-xmlwriter": "*",
587 | "php": ">=5.4.0"
588 | },
589 | "require-dev": {
590 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
591 | },
592 | "bin": [
593 | "bin/phpcbf",
594 | "bin/phpcs"
595 | ],
596 | "type": "library",
597 | "extra": {
598 | "branch-alias": {
599 | "dev-master": "3.x-dev"
600 | }
601 | },
602 | "notification-url": "https://packagist.org/downloads/",
603 | "license": [
604 | "BSD-3-Clause"
605 | ],
606 | "authors": [
607 | {
608 | "name": "Greg Sherwood",
609 | "role": "Former lead"
610 | },
611 | {
612 | "name": "Juliette Reinders Folmer",
613 | "role": "Current lead"
614 | },
615 | {
616 | "name": "Contributors",
617 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
618 | }
619 | ],
620 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
621 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
622 | "keywords": [
623 | "phpcs",
624 | "standards",
625 | "static analysis"
626 | ],
627 | "support": {
628 | "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
629 | "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
630 | "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
631 | "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
632 | },
633 | "funding": [
634 | {
635 | "url": "https://github.com/PHPCSStandards",
636 | "type": "github"
637 | },
638 | {
639 | "url": "https://github.com/jrfnl",
640 | "type": "github"
641 | },
642 | {
643 | "url": "https://opencollective.com/php_codesniffer",
644 | "type": "open_collective"
645 | }
646 | ],
647 | "time": "2024-02-16T15:06:51+00:00"
648 | },
649 | {
650 | "name": "wp-coding-standards/wpcs",
651 | "version": "3.0.1",
652 | "source": {
653 | "type": "git",
654 | "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
655 | "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1"
656 | },
657 | "dist": {
658 | "type": "zip",
659 | "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
660 | "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
661 | "shasum": ""
662 | },
663 | "require": {
664 | "ext-filter": "*",
665 | "ext-libxml": "*",
666 | "ext-tokenizer": "*",
667 | "ext-xmlreader": "*",
668 | "php": ">=5.4",
669 | "phpcsstandards/phpcsextra": "^1.1.0",
670 | "phpcsstandards/phpcsutils": "^1.0.8",
671 | "squizlabs/php_codesniffer": "^3.7.2"
672 | },
673 | "require-dev": {
674 | "php-parallel-lint/php-console-highlighter": "^1.0.0",
675 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
676 | "phpcompatibility/php-compatibility": "^9.0",
677 | "phpcsstandards/phpcsdevtools": "^1.2.0",
678 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
679 | },
680 | "suggest": {
681 | "ext-iconv": "For improved results",
682 | "ext-mbstring": "For improved results"
683 | },
684 | "type": "phpcodesniffer-standard",
685 | "notification-url": "https://packagist.org/downloads/",
686 | "license": [
687 | "MIT"
688 | ],
689 | "authors": [
690 | {
691 | "name": "Contributors",
692 | "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
693 | }
694 | ],
695 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
696 | "keywords": [
697 | "phpcs",
698 | "standards",
699 | "static analysis",
700 | "wordpress"
701 | ],
702 | "support": {
703 | "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
704 | "source": "https://github.com/WordPress/WordPress-Coding-Standards",
705 | "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
706 | },
707 | "funding": [
708 | {
709 | "url": "https://opencollective.com/thewpcc/contribute/wp-php-63406",
710 | "type": "custom"
711 | }
712 | ],
713 | "time": "2023-09-14T07:06:09+00:00"
714 | },
715 | {
716 | "name": "wpscholar/phpcs-standards-wpscholar",
717 | "version": "1.0.6",
718 | "source": {
719 | "type": "git",
720 | "url": "https://github.com/wpscholar/phpcs-standards-wpscholar.git",
721 | "reference": "f25cbbf74cf3c0324efb2dd65339274d4f2d7de5"
722 | },
723 | "dist": {
724 | "type": "zip",
725 | "url": "https://api.github.com/repos/wpscholar/phpcs-standards-wpscholar/zipball/f25cbbf74cf3c0324efb2dd65339274d4f2d7de5",
726 | "reference": "f25cbbf74cf3c0324efb2dd65339274d4f2d7de5",
727 | "shasum": ""
728 | },
729 | "require": {
730 | "dealerdirect/phpcodesniffer-composer-installer": "@stable",
731 | "phpcompatibility/phpcompatibility-wp": "@stable",
732 | "squizlabs/php_codesniffer": "@stable",
733 | "wp-coding-standards/wpcs": "@stable"
734 | },
735 | "type": "phpcodesniffer-standard",
736 | "notification-url": "https://packagist.org/downloads/",
737 | "license": [
738 | "GPL-2.0-or-later"
739 | ],
740 | "authors": [
741 | {
742 | "name": "Micah Wood",
743 | "email": "micah@wpscholar.com"
744 | }
745 | ],
746 | "description": "PHP Code Sniffer Standards for WP Scholar",
747 | "support": {
748 | "issues": "https://github.com/wpscholar/phpcs-standards-wpscholar/issues",
749 | "source": "https://github.com/wpscholar/phpcs-standards-wpscholar/tree/1.0.6"
750 | },
751 | "time": "2023-06-16T13:33:40+00:00"
752 | }
753 | ],
754 | "aliases": [],
755 | "minimum-stability": "stable",
756 | "stability-flags": [],
757 | "prefer-stable": false,
758 | "prefer-lowest": false,
759 | "platform": [],
760 | "platform-dev": [],
761 | "plugin-api-version": "2.6.0"
762 | }
763 |
--------------------------------------------------------------------------------