diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa3ce050d4..b070215b5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: - name: Install Playwright Browsers run: sudo npx playwright install --with-deps - name: Prepare app deploy and offline mode - run: npx nx e2e:playwright:prepare-app-deploy-and-offline-mode playground-website + run: CORS_PROXY_URL=http://127.0.0.1:5263/cors-proxy.php? npx nx e2e:playwright:prepare-app-deploy-and-offline-mode playground-website - name: Zip dist run: zip -r dist.zip dist - name: Upload dist diff --git a/packages/php-wasm/universal/src/lib/php-worker.ts b/packages/php-wasm/universal/src/lib/php-worker.ts index 5d8c4e83d8..b35020c216 100644 --- a/packages/php-wasm/universal/src/lib/php-worker.ts +++ b/packages/php-wasm/universal/src/lib/php-worker.ts @@ -229,8 +229,8 @@ export class PHPWorker implements LimitedPHPApi { } /** @inheritDoc @php-wasm/universal!/PHP.onMessage */ - onMessage(listener: MessageListener): void { - _private.get(this)!.php!.onMessage(listener); + onMessage(listener: MessageListener) { + return _private.get(this)!.php!.onMessage(listener); } /** @inheritDoc @php-wasm/universal!/PHP.defineConstant */ diff --git a/packages/php-wasm/universal/src/lib/php.ts b/packages/php-wasm/universal/src/lib/php.ts index d63e7ce889..16ef240570 100644 --- a/packages/php-wasm/universal/src/lib/php.ts +++ b/packages/php-wasm/universal/src/lib/php.ts @@ -160,6 +160,11 @@ export class PHP implements Disposable { */ onMessage(listener: MessageListener) { this.#messageListeners.push(listener); + return async () => { + this.#messageListeners = this.#messageListeners.filter( + (l) => l !== listener + ); + }; } async setSpawnHandler(handler: SpawnHandler | string) { diff --git a/packages/php-wasm/web/src/lib/tcp-over-fetch-websocket.ts b/packages/php-wasm/web/src/lib/tcp-over-fetch-websocket.ts index 37f1556a38..0b6d2dfa58 100644 --- a/packages/php-wasm/web/src/lib/tcp-over-fetch-websocket.ts +++ b/packages/php-wasm/web/src/lib/tcp-over-fetch-websocket.ts @@ -45,6 +45,7 @@ import { ContentTypes } from './tls/1_2/types'; export type TCPOverFetchOptions = { CAroot: GeneratedCertificate; + corsProxyUrl?: string; }; /** @@ -67,6 +68,7 @@ export const tcpOverFetchWebsocket = (tcpOptions: TCPOverFetchOptions) => { constructor(url: string, wsOptions: string[]) { super(url, wsOptions, { CAroot: tcpOptions.CAroot, + corsProxyUrl: tcpOptions.corsProxyUrl, }); } }; @@ -85,6 +87,7 @@ export interface TCPOverFetchWebsocketOptions { * clientDownstream stream and tracking the closure of that stream. */ outputType?: 'messages' | 'stream'; + corsProxyUrl?: string; } export class TCPOverFetchWebsocket { @@ -101,6 +104,7 @@ export class TCPOverFetchWebsocket { port = 0; listeners = new Map(); CAroot?: GeneratedCertificate; + corsProxyUrl?: string; clientUpstream = new TransformStream(); clientUpstreamWriter = this.clientUpstream.writable.getWriter(); @@ -111,13 +115,18 @@ export class TCPOverFetchWebsocket { constructor( public url: string, public options: string[], - { CAroot, outputType = 'messages' }: TCPOverFetchWebsocketOptions = {} + { + CAroot, + corsProxyUrl, + outputType = 'messages', + }: TCPOverFetchWebsocketOptions = {} ) { const wsUrl = new URL(url); this.host = wsUrl.searchParams.get('host')!; this.port = parseInt(wsUrl.searchParams.get('port')!, 10); this.binaryType = 'arraybuffer'; + this.corsProxyUrl = corsProxyUrl; this.CAroot = CAroot; if (outputType === 'messages') { this.clientDownstream.readable @@ -307,9 +316,10 @@ export class TCPOverFetchWebsocket { 'https' ); try { - await RawBytesFetch.fetchRawResponseBytes(request).pipeTo( - tlsConnection.serverEnd.downstream.writable - ); + await RawBytesFetch.fetchRawResponseBytes( + request, + this.corsProxyUrl + ).pipeTo(tlsConnection.serverEnd.downstream.writable); } catch (e) { // Ignore errors from fetch() // They are handled in the constructor @@ -327,9 +337,10 @@ export class TCPOverFetchWebsocket { 'http' ); try { - await RawBytesFetch.fetchRawResponseBytes(request).pipeTo( - this.clientDownstream.writable - ); + await RawBytesFetch.fetchRawResponseBytes( + request, + this.corsProxyUrl + ).pipeTo(this.clientDownstream.writable); } catch (e) { // Ignore errors from fetch() // They are handled in the constructor @@ -409,7 +420,11 @@ class RawBytesFetch { /** * Streams a HTTP response including the status line and headers. */ - static fetchRawResponseBytes(request: Request) { + static fetchRawResponseBytes(request: Request, corsProxyUrl?: string) { + const targetRequest = corsProxyUrl + ? new Request(`${corsProxyUrl}${request.url}`, request) + : request; + // This initially used a TransformStream and piped the response // body to the writable side of the TransformStream. // @@ -419,13 +434,34 @@ class RawBytesFetch { async start(controller) { let response: Response; try { - response = await fetch(request); - controller.enqueue(RawBytesFetch.headersAsBytes(response)); + response = await fetch(targetRequest); } catch (error) { + /** + * Pretend we've got a 400 Bad Request response whenever + * the fetch() call fails. + * + * Just propagating an error and closing a WebSocket does + * not make PHP aware the socket closed abruptly. This means + * the AsyncHttp\Client will keep polling the socket indefinitely + * until the request times out. This isn't perfect, as we want + * to close the socket as soon as possible to avoid, e.g., 10 seconds + * of unnecessary waitin for the timeout + * + * The root cause is unknown and likely related to the low-level + * implementation of polling file descriptors. The following + * workaround is far from ideal, but it must suffice until we + * have a platform-level resolution. + */ + controller.enqueue( + new TextEncoder().encode( + 'HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n' + ) + ); controller.error(error); return; } + controller.enqueue(RawBytesFetch.headersAsBytes(response)); const reader = response.body?.getReader(); if (!reader) { controller.close(); diff --git a/packages/playground/blueprints/public/blueprint-schema-validator.js b/packages/playground/blueprints/public/blueprint-schema-validator.js index 299fc51480..48e55bae71 100644 --- a/packages/playground/blueprints/public/blueprint-schema-validator.js +++ b/packages/playground/blueprints/public/blueprint-schema-validator.js @@ -452,6 +452,13 @@ const schema11 = { $ref: '#/definitions/FileReference', description: 'The file to import', }, + importer: { + type: 'string', + enum: ['data-liberation', 'default'], + description: + 'The importer to use. Possible values:\n\n- `default`: The importer from https://github.com/humanmade/WordPress-Importer\n- `data-liberation`: The experimental Data Liberation WXR importer developed at https://github.com/WordPress/wordpress-playground/issues/1894\n\nThis option is deprecated. The syntax will not be removed, but once the Data Liberation importer matures, it will become the only supported importer and the `importer` option will be ignored.', + deprecated: true, + }, }, required: ['file', 'step'], }, @@ -3143,6 +3150,13 @@ const schema22 = { $ref: '#/definitions/FileReference', description: 'The file to import', }, + importer: { + type: 'string', + enum: ['data-liberation', 'default'], + description: + 'The importer to use. Possible values:\n\n- `default`: The importer from https://github.com/humanmade/WordPress-Importer\n- `data-liberation`: The experimental Data Liberation WXR importer developed at https://github.com/WordPress/wordpress-playground/issues/1894\n\nThis option is deprecated. The syntax will not be removed, but once the Data Liberation importer matures, it will become the only supported importer and the `importer` option will be ignored.', + deprecated: true, + }, }, required: ['file', 'step'], }, @@ -9755,7 +9769,8 @@ function validate14( !( key13 === 'progress' || key13 === 'step' || - key13 === 'file' + key13 === 'file' || + key13 === 'importer' ) ) { validate14.errors = [ @@ -9998,6 +10013,75 @@ function validate14( } else { var valid21 = true; } + if (valid21) { + if ( + data.importer !== + undefined + ) { + let data40 = + data.importer; + const _errs109 = errors; + if ( + typeof data40 !== + 'string' + ) { + validate14.errors = + [ + { + instancePath: + instancePath + + '/importer', + schemaPath: + '#/oneOf/6/properties/importer/type', + keyword: + 'type', + params: { + type: 'string', + }, + message: + 'must be string', + }, + ]; + return false; + } + if ( + !( + data40 === + 'data-liberation' || + data40 === + 'default' + ) + ) { + validate14.errors = + [ + { + instancePath: + instancePath + + '/importer', + schemaPath: + '#/oneOf/6/properties/importer/enum', + keyword: + 'enum', + params: { + allowedValues: + schema22 + .oneOf[6] + .properties + .importer + .enum, + }, + message: + 'must be equal to one of the allowed values', + }, + ]; + return false; + } + var valid21 = + _errs109 === errors; + } else { + var valid21 = true; + } + } } } } @@ -10016,8 +10100,8 @@ function validate14( } } } else if (tag0 === 'importThemeStarterContent') { - const _errs109 = errors; - if (errors === _errs109) { + const _errs111 = errors; + if (errors === _errs111) { if ( data && typeof data == 'object' && @@ -10044,7 +10128,7 @@ function validate14( ]; return false; } else { - const _errs111 = errors; + const _errs113 = errors; for (const key15 in data) { if ( !( @@ -10072,18 +10156,18 @@ function validate14( break; } } - if (_errs111 === errors) { + if (_errs113 === errors) { if (data.progress !== undefined) { - let data40 = data.progress; - const _errs112 = errors; - if (errors === _errs112) { + let data41 = data.progress; + const _errs114 = errors; + if (errors === _errs114) { if ( - data40 && - typeof data40 == 'object' && - !Array.isArray(data40) + data41 && + typeof data41 == 'object' && + !Array.isArray(data41) ) { - const _errs114 = errors; - for (const key16 in data40) { + const _errs116 = errors; + for (const key16 in data41) { if ( !( key16 === @@ -10114,21 +10198,21 @@ function validate14( break; } } - if (_errs114 === errors) { + if (_errs116 === errors) { if ( - data40.weight !== + data41.weight !== undefined ) { - let data41 = - data40.weight; - const _errs115 = + let data42 = + data41.weight; + const _errs117 = errors; if ( !( - typeof data41 == + typeof data42 == 'number' && isFinite( - data41 + data42 ) ) ) { @@ -10152,20 +10236,20 @@ function validate14( return false; } var valid25 = - _errs115 === + _errs117 === errors; } else { var valid25 = true; } if (valid25) { if ( - data40.caption !== + data41.caption !== undefined ) { - const _errs117 = + const _errs119 = errors; if ( - typeof data40.caption !== + typeof data41.caption !== 'string' ) { validate14.errors = @@ -10188,7 +10272,7 @@ function validate14( return false; } var valid25 = - _errs117 === + _errs119 === errors; } else { var valid25 = true; @@ -10214,16 +10298,16 @@ function validate14( return false; } } - var valid24 = _errs112 === errors; + var valid24 = _errs114 === errors; } else { var valid24 = true; } if (valid24) { if (data.step !== undefined) { - let data43 = data.step; - const _errs119 = errors; + let data44 = data.step; + const _errs121 = errors; if ( - typeof data43 !== 'string' + typeof data44 !== 'string' ) { validate14.errors = [ { @@ -10244,7 +10328,7 @@ function validate14( } if ( 'importThemeStarterContent' !== - data43 + data44 ) { validate14.errors = [ { @@ -10265,7 +10349,7 @@ function validate14( return false; } var valid24 = - _errs119 === errors; + _errs121 === errors; } else { var valid24 = true; } @@ -10273,7 +10357,7 @@ function validate14( if ( data.themeSlug !== undefined ) { - const _errs121 = errors; + const _errs123 = errors; if ( typeof data.themeSlug !== 'string' @@ -10296,7 +10380,7 @@ function validate14( return false; } var valid24 = - _errs121 === errors; + _errs123 === errors; } else { var valid24 = true; } @@ -10318,8 +10402,8 @@ function validate14( } } } else if (tag0 === 'importWordPressFiles') { - const _errs123 = errors; - if (errors === _errs123) { + const _errs125 = errors; + if (errors === _errs125) { if ( data && typeof data == 'object' && @@ -10348,7 +10432,7 @@ function validate14( ]; return false; } else { - const _errs125 = errors; + const _errs127 = errors; for (const key17 in data) { if ( !( @@ -10377,18 +10461,18 @@ function validate14( break; } } - if (_errs125 === errors) { + if (_errs127 === errors) { if (data.progress !== undefined) { - let data45 = data.progress; - const _errs126 = errors; - if (errors === _errs126) { + let data46 = data.progress; + const _errs128 = errors; + if (errors === _errs128) { if ( - data45 && - typeof data45 == 'object' && - !Array.isArray(data45) + data46 && + typeof data46 == 'object' && + !Array.isArray(data46) ) { - const _errs128 = errors; - for (const key18 in data45) { + const _errs130 = errors; + for (const key18 in data46) { if ( !( key18 === @@ -10419,21 +10503,21 @@ function validate14( break; } } - if (_errs128 === errors) { + if (_errs130 === errors) { if ( - data45.weight !== + data46.weight !== undefined ) { - let data46 = - data45.weight; - const _errs129 = + let data47 = + data46.weight; + const _errs131 = errors; if ( !( - typeof data46 == + typeof data47 == 'number' && isFinite( - data46 + data47 ) ) ) { @@ -10457,20 +10541,20 @@ function validate14( return false; } var valid28 = - _errs129 === + _errs131 === errors; } else { var valid28 = true; } if (valid28) { if ( - data45.caption !== + data46.caption !== undefined ) { - const _errs131 = + const _errs133 = errors; if ( - typeof data45.caption !== + typeof data46.caption !== 'string' ) { validate14.errors = @@ -10493,7 +10577,7 @@ function validate14( return false; } var valid28 = - _errs131 === + _errs133 === errors; } else { var valid28 = true; @@ -10519,16 +10603,16 @@ function validate14( return false; } } - var valid27 = _errs126 === errors; + var valid27 = _errs128 === errors; } else { var valid27 = true; } if (valid27) { if (data.step !== undefined) { - let data48 = data.step; - const _errs133 = errors; + let data49 = data.step; + const _errs135 = errors; if ( - typeof data48 !== 'string' + typeof data49 !== 'string' ) { validate14.errors = [ { @@ -10549,7 +10633,7 @@ function validate14( } if ( 'importWordPressFiles' !== - data48 + data49 ) { validate14.errors = [ { @@ -10570,7 +10654,7 @@ function validate14( return false; } var valid27 = - _errs133 === errors; + _errs135 === errors; } else { var valid27 = true; } @@ -10579,7 +10663,7 @@ function validate14( data.wordPressFilesZip !== undefined ) { - const _errs135 = errors; + const _errs137 = errors; if ( !validate12( data.wordPressFilesZip, @@ -10604,7 +10688,7 @@ function validate14( errors = vErrors.length; } var valid27 = - _errs135 === errors; + _errs137 === errors; } else { var valid27 = true; } @@ -10613,7 +10697,7 @@ function validate14( data.pathInZip !== undefined ) { - const _errs136 = errors; + const _errs138 = errors; if ( typeof data.pathInZip !== 'string' @@ -10638,7 +10722,7 @@ function validate14( return false; } var valid27 = - _errs136 === errors; + _errs138 === errors; } else { var valid27 = true; } @@ -10661,8 +10745,8 @@ function validate14( } } } else if (tag0 === 'installPlugin') { - const _errs138 = errors; - if (errors === _errs138) { + const _errs140 = errors; + if (errors === _errs140) { if ( data && typeof data == 'object' && @@ -10691,7 +10775,7 @@ function validate14( ]; return false; } else { - const _errs140 = errors; + const _errs142 = errors; for (const key19 in data) { if ( !( @@ -10723,18 +10807,18 @@ function validate14( break; } } - if (_errs140 === errors) { + if (_errs142 === errors) { if (data.progress !== undefined) { - let data51 = data.progress; - const _errs141 = errors; - if (errors === _errs141) { + let data52 = data.progress; + const _errs143 = errors; + if (errors === _errs143) { if ( - data51 && - typeof data51 == 'object' && - !Array.isArray(data51) + data52 && + typeof data52 == 'object' && + !Array.isArray(data52) ) { - const _errs143 = errors; - for (const key20 in data51) { + const _errs145 = errors; + for (const key20 in data52) { if ( !( key20 === @@ -10765,21 +10849,21 @@ function validate14( break; } } - if (_errs143 === errors) { + if (_errs145 === errors) { if ( - data51.weight !== + data52.weight !== undefined ) { - let data52 = - data51.weight; - const _errs144 = + let data53 = + data52.weight; + const _errs146 = errors; if ( !( - typeof data52 == + typeof data53 == 'number' && isFinite( - data52 + data53 ) ) ) { @@ -10803,20 +10887,20 @@ function validate14( return false; } var valid31 = - _errs144 === + _errs146 === errors; } else { var valid31 = true; } if (valid31) { if ( - data51.caption !== + data52.caption !== undefined ) { - const _errs146 = + const _errs148 = errors; if ( - typeof data51.caption !== + typeof data52.caption !== 'string' ) { validate14.errors = @@ -10839,7 +10923,7 @@ function validate14( return false; } var valid31 = - _errs146 === + _errs148 === errors; } else { var valid31 = true; @@ -10865,7 +10949,7 @@ function validate14( return false; } } - var valid30 = _errs141 === errors; + var valid30 = _errs143 === errors; } else { var valid30 = true; } @@ -10874,11 +10958,11 @@ function validate14( data.ifAlreadyInstalled !== undefined ) { - let data54 = + let data55 = data.ifAlreadyInstalled; - const _errs148 = errors; + const _errs150 = errors; if ( - typeof data54 !== 'string' + typeof data55 !== 'string' ) { validate14.errors = [ { @@ -10899,10 +10983,10 @@ function validate14( } if ( !( - data54 === + data55 === 'overwrite' || - data54 === 'skip' || - data54 === 'error' + data55 === 'skip' || + data55 === 'error' ) ) { validate14.errors = [ @@ -10928,16 +11012,16 @@ function validate14( return false; } var valid30 = - _errs148 === errors; + _errs150 === errors; } else { var valid30 = true; } if (valid30) { if (data.step !== undefined) { - let data55 = data.step; - const _errs150 = errors; + let data56 = data.step; + const _errs152 = errors; if ( - typeof data55 !== + typeof data56 !== 'string' ) { validate14.errors = [ @@ -10959,7 +11043,7 @@ function validate14( } if ( 'installPlugin' !== - data55 + data56 ) { validate14.errors = [ { @@ -10981,7 +11065,7 @@ function validate14( return false; } var valid30 = - _errs150 === errors; + _errs152 === errors; } else { var valid30 = true; } @@ -10990,15 +11074,15 @@ function validate14( data.pluginData !== undefined ) { - let data56 = + let data57 = data.pluginData; - const _errs152 = errors; - const _errs153 = errors; - let valid32 = false; const _errs154 = errors; + const _errs155 = errors; + let valid32 = false; + const _errs156 = errors; if ( !validate12( - data56, + data57, { instancePath: instancePath + @@ -11021,15 +11105,15 @@ function validate14( vErrors.length; } var _valid0 = - _errs154 === errors; + _errs156 === errors; valid32 = valid32 || _valid0; if (!valid32) { - const _errs155 = + const _errs157 = errors; if ( !validate18( - data56, + data57, { instancePath: instancePath + @@ -11053,7 +11137,7 @@ function validate14( vErrors.length; } var _valid0 = - _errs155 === + _errs157 === errors; valid32 = valid32 || @@ -11088,13 +11172,13 @@ function validate14( vErrors; return false; } else { - errors = _errs153; + errors = _errs155; if ( vErrors !== null ) { - if (_errs153) { + if (_errs155) { vErrors.length = - _errs153; + _errs155; } else { vErrors = null; @@ -11102,7 +11186,7 @@ function validate14( } } var valid30 = - _errs152 === errors; + _errs154 === errors; } else { var valid30 = true; } @@ -11111,7 +11195,7 @@ function validate14( data.pluginZipFile !== undefined ) { - const _errs156 = + const _errs158 = errors; if ( !validate12( @@ -11139,7 +11223,7 @@ function validate14( vErrors.length; } var valid30 = - _errs156 === + _errs158 === errors; } else { var valid30 = true; @@ -11149,27 +11233,27 @@ function validate14( data.options !== undefined ) { - let data58 = + let data59 = data.options; - const _errs157 = + const _errs159 = errors; - const _errs158 = + const _errs160 = errors; if ( errors === - _errs158 + _errs160 ) { if ( - data58 && - typeof data58 == + data59 && + typeof data59 == 'object' && !Array.isArray( - data58 + data59 ) ) { - const _errs160 = + const _errs162 = errors; - for (const key21 in data58) { + for (const key21 in data59) { if ( !( key21 === @@ -11201,17 +11285,17 @@ function validate14( } } if ( - _errs160 === + _errs162 === errors ) { if ( - data58.activate !== + data59.activate !== undefined ) { - const _errs161 = + const _errs163 = errors; if ( - typeof data58.activate !== + typeof data59.activate !== 'boolean' ) { validate14.errors = @@ -11234,7 +11318,7 @@ function validate14( return false; } var valid34 = - _errs161 === + _errs163 === errors; } else { var valid34 = true; @@ -11243,13 +11327,13 @@ function validate14( valid34 ) { if ( - data58.targetFolderName !== + data59.targetFolderName !== undefined ) { - const _errs163 = + const _errs165 = errors; if ( - typeof data58.targetFolderName !== + typeof data59.targetFolderName !== 'string' ) { validate14.errors = @@ -11272,7 +11356,7 @@ function validate14( return false; } var valid34 = - _errs163 === + _errs165 === errors; } else { var valid34 = true; @@ -11301,7 +11385,7 @@ function validate14( } } var valid30 = - _errs157 === + _errs159 === errors; } else { var valid30 = true; @@ -11327,8 +11411,8 @@ function validate14( } } } else if (tag0 === 'installTheme') { - const _errs165 = errors; - if (errors === _errs165) { + const _errs167 = errors; + if (errors === _errs167) { if ( data && typeof data == 'object' && @@ -11357,7 +11441,7 @@ function validate14( ]; return false; } else { - const _errs167 = errors; + const _errs169 = errors; for (const key22 in data) { if ( !( @@ -11389,18 +11473,18 @@ function validate14( break; } } - if (_errs167 === errors) { + if (_errs169 === errors) { if (data.progress !== undefined) { - let data61 = data.progress; - const _errs168 = errors; - if (errors === _errs168) { + let data62 = data.progress; + const _errs170 = errors; + if (errors === _errs170) { if ( - data61 && - typeof data61 == 'object' && - !Array.isArray(data61) + data62 && + typeof data62 == 'object' && + !Array.isArray(data62) ) { - const _errs170 = errors; - for (const key23 in data61) { + const _errs172 = errors; + for (const key23 in data62) { if ( !( key23 === @@ -11431,21 +11515,21 @@ function validate14( break; } } - if (_errs170 === errors) { + if (_errs172 === errors) { if ( - data61.weight !== + data62.weight !== undefined ) { - let data62 = - data61.weight; - const _errs171 = + let data63 = + data62.weight; + const _errs173 = errors; if ( !( - typeof data62 == + typeof data63 == 'number' && isFinite( - data62 + data63 ) ) ) { @@ -11469,20 +11553,20 @@ function validate14( return false; } var valid37 = - _errs171 === + _errs173 === errors; } else { var valid37 = true; } if (valid37) { if ( - data61.caption !== + data62.caption !== undefined ) { - const _errs173 = + const _errs175 = errors; if ( - typeof data61.caption !== + typeof data62.caption !== 'string' ) { validate14.errors = @@ -11505,7 +11589,7 @@ function validate14( return false; } var valid37 = - _errs173 === + _errs175 === errors; } else { var valid37 = true; @@ -11531,7 +11615,7 @@ function validate14( return false; } } - var valid36 = _errs168 === errors; + var valid36 = _errs170 === errors; } else { var valid36 = true; } @@ -11540,11 +11624,11 @@ function validate14( data.ifAlreadyInstalled !== undefined ) { - let data64 = + let data65 = data.ifAlreadyInstalled; - const _errs175 = errors; + const _errs177 = errors; if ( - typeof data64 !== 'string' + typeof data65 !== 'string' ) { validate14.errors = [ { @@ -11565,10 +11649,10 @@ function validate14( } if ( !( - data64 === + data65 === 'overwrite' || - data64 === 'skip' || - data64 === 'error' + data65 === 'skip' || + data65 === 'error' ) ) { validate14.errors = [ @@ -11594,16 +11678,16 @@ function validate14( return false; } var valid36 = - _errs175 === errors; + _errs177 === errors; } else { var valid36 = true; } if (valid36) { if (data.step !== undefined) { - let data65 = data.step; - const _errs177 = errors; + let data66 = data.step; + const _errs179 = errors; if ( - typeof data65 !== + typeof data66 !== 'string' ) { validate14.errors = [ @@ -11625,7 +11709,7 @@ function validate14( } if ( 'installTheme' !== - data65 + data66 ) { validate14.errors = [ { @@ -11647,7 +11731,7 @@ function validate14( return false; } var valid36 = - _errs177 === errors; + _errs179 === errors; } else { var valid36 = true; } @@ -11656,15 +11740,15 @@ function validate14( data.themeData !== undefined ) { - let data66 = + let data67 = data.themeData; - const _errs179 = errors; - const _errs180 = errors; - let valid38 = false; const _errs181 = errors; + const _errs182 = errors; + let valid38 = false; + const _errs183 = errors; if ( !validate12( - data66, + data67, { instancePath: instancePath + @@ -11687,15 +11771,15 @@ function validate14( vErrors.length; } var _valid1 = - _errs181 === errors; + _errs183 === errors; valid38 = valid38 || _valid1; if (!valid38) { - const _errs182 = + const _errs184 = errors; if ( !validate18( - data66, + data67, { instancePath: instancePath + @@ -11719,7 +11803,7 @@ function validate14( vErrors.length; } var _valid1 = - _errs182 === + _errs184 === errors; valid38 = valid38 || @@ -11754,13 +11838,13 @@ function validate14( vErrors; return false; } else { - errors = _errs180; + errors = _errs182; if ( vErrors !== null ) { - if (_errs180) { + if (_errs182) { vErrors.length = - _errs180; + _errs182; } else { vErrors = null; @@ -11768,7 +11852,7 @@ function validate14( } } var valid36 = - _errs179 === errors; + _errs181 === errors; } else { var valid36 = true; } @@ -11777,7 +11861,7 @@ function validate14( data.themeZipFile !== undefined ) { - const _errs183 = + const _errs185 = errors; if ( !validate12( @@ -11805,7 +11889,7 @@ function validate14( vErrors.length; } var valid36 = - _errs183 === + _errs185 === errors; } else { var valid36 = true; @@ -11815,27 +11899,27 @@ function validate14( data.options !== undefined ) { - let data68 = + let data69 = data.options; - const _errs184 = + const _errs186 = errors; - const _errs185 = + const _errs187 = errors; if ( errors === - _errs185 + _errs187 ) { if ( - data68 && - typeof data68 == + data69 && + typeof data69 == 'object' && !Array.isArray( - data68 + data69 ) ) { - const _errs187 = + const _errs189 = errors; - for (const key24 in data68) { + for (const key24 in data69) { if ( !( key24 === @@ -11869,17 +11953,17 @@ function validate14( } } if ( - _errs187 === + _errs189 === errors ) { if ( - data68.activate !== + data69.activate !== undefined ) { - const _errs188 = + const _errs190 = errors; if ( - typeof data68.activate !== + typeof data69.activate !== 'boolean' ) { validate14.errors = @@ -11902,7 +11986,7 @@ function validate14( return false; } var valid40 = - _errs188 === + _errs190 === errors; } else { var valid40 = true; @@ -11911,13 +11995,13 @@ function validate14( valid40 ) { if ( - data68.importStarterContent !== + data69.importStarterContent !== undefined ) { - const _errs190 = + const _errs192 = errors; if ( - typeof data68.importStarterContent !== + typeof data69.importStarterContent !== 'boolean' ) { validate14.errors = @@ -11940,7 +12024,7 @@ function validate14( return false; } var valid40 = - _errs190 === + _errs192 === errors; } else { var valid40 = true; @@ -11949,13 +12033,13 @@ function validate14( valid40 ) { if ( - data68.targetFolderName !== + data69.targetFolderName !== undefined ) { - const _errs192 = + const _errs194 = errors; if ( - typeof data68.targetFolderName !== + typeof data69.targetFolderName !== 'string' ) { validate14.errors = @@ -11978,7 +12062,7 @@ function validate14( return false; } var valid40 = - _errs192 === + _errs194 === errors; } else { var valid40 = true; @@ -12008,7 +12092,7 @@ function validate14( } } var valid36 = - _errs184 === + _errs186 === errors; } else { var valid36 = true; @@ -12034,8 +12118,8 @@ function validate14( } } } else if (tag0 === 'login') { - const _errs194 = errors; - if (errors === _errs194) { + const _errs196 = errors; + if (errors === _errs196) { if ( data && typeof data == 'object' && @@ -12062,7 +12146,7 @@ function validate14( ]; return false; } else { - const _errs196 = errors; + const _errs198 = errors; for (const key25 in data) { if ( !( @@ -12091,18 +12175,18 @@ function validate14( break; } } - if (_errs196 === errors) { + if (_errs198 === errors) { if (data.progress !== undefined) { - let data72 = data.progress; - const _errs197 = errors; - if (errors === _errs197) { + let data73 = data.progress; + const _errs199 = errors; + if (errors === _errs199) { if ( - data72 && - typeof data72 == 'object' && - !Array.isArray(data72) + data73 && + typeof data73 == 'object' && + !Array.isArray(data73) ) { - const _errs199 = errors; - for (const key26 in data72) { + const _errs201 = errors; + for (const key26 in data73) { if ( !( key26 === @@ -12133,21 +12217,21 @@ function validate14( break; } } - if (_errs199 === errors) { + if (_errs201 === errors) { if ( - data72.weight !== + data73.weight !== undefined ) { - let data73 = - data72.weight; - const _errs200 = + let data74 = + data73.weight; + const _errs202 = errors; if ( !( - typeof data73 == + typeof data74 == 'number' && isFinite( - data73 + data74 ) ) ) { @@ -12171,20 +12255,20 @@ function validate14( return false; } var valid43 = - _errs200 === + _errs202 === errors; } else { var valid43 = true; } if (valid43) { if ( - data72.caption !== + data73.caption !== undefined ) { - const _errs202 = + const _errs204 = errors; if ( - typeof data72.caption !== + typeof data73.caption !== 'string' ) { validate14.errors = @@ -12207,7 +12291,7 @@ function validate14( return false; } var valid43 = - _errs202 === + _errs204 === errors; } else { var valid43 = true; @@ -12233,16 +12317,16 @@ function validate14( return false; } } - var valid42 = _errs197 === errors; + var valid42 = _errs199 === errors; } else { var valid42 = true; } if (valid42) { if (data.step !== undefined) { - let data75 = data.step; - const _errs204 = errors; + let data76 = data.step; + const _errs206 = errors; if ( - typeof data75 !== 'string' + typeof data76 !== 'string' ) { validate14.errors = [ { @@ -12261,7 +12345,7 @@ function validate14( ]; return false; } - if ('login' !== data75) { + if ('login' !== data76) { validate14.errors = [ { instancePath: @@ -12281,7 +12365,7 @@ function validate14( return false; } var valid42 = - _errs204 === errors; + _errs206 === errors; } else { var valid42 = true; } @@ -12289,7 +12373,7 @@ function validate14( if ( data.username !== undefined ) { - const _errs206 = errors; + const _errs208 = errors; if ( typeof data.username !== 'string' @@ -12312,7 +12396,7 @@ function validate14( return false; } var valid42 = - _errs206 === errors; + _errs208 === errors; } else { var valid42 = true; } @@ -12321,7 +12405,7 @@ function validate14( data.password !== undefined ) { - const _errs208 = errors; + const _errs210 = errors; if ( typeof data.password !== 'string' @@ -12346,7 +12430,7 @@ function validate14( return false; } var valid42 = - _errs208 === errors; + _errs210 === errors; } else { var valid42 = true; } @@ -12369,8 +12453,8 @@ function validate14( } } } else if (tag0 === 'mkdir') { - const _errs210 = errors; - if (errors === _errs210) { + const _errs212 = errors; + if (errors === _errs212) { if ( data && typeof data == 'object' && @@ -12399,7 +12483,7 @@ function validate14( ]; return false; } else { - const _errs212 = errors; + const _errs214 = errors; for (const key27 in data) { if ( !( @@ -12427,18 +12511,18 @@ function validate14( break; } } - if (_errs212 === errors) { + if (_errs214 === errors) { if (data.progress !== undefined) { - let data78 = data.progress; - const _errs213 = errors; - if (errors === _errs213) { + let data79 = data.progress; + const _errs215 = errors; + if (errors === _errs215) { if ( - data78 && - typeof data78 == 'object' && - !Array.isArray(data78) + data79 && + typeof data79 == 'object' && + !Array.isArray(data79) ) { - const _errs215 = errors; - for (const key28 in data78) { + const _errs217 = errors; + for (const key28 in data79) { if ( !( key28 === @@ -12469,21 +12553,21 @@ function validate14( break; } } - if (_errs215 === errors) { + if (_errs217 === errors) { if ( - data78.weight !== + data79.weight !== undefined ) { - let data79 = - data78.weight; - const _errs216 = + let data80 = + data79.weight; + const _errs218 = errors; if ( !( - typeof data79 == + typeof data80 == 'number' && isFinite( - data79 + data80 ) ) ) { @@ -12507,20 +12591,20 @@ function validate14( return false; } var valid46 = - _errs216 === + _errs218 === errors; } else { var valid46 = true; } if (valid46) { if ( - data78.caption !== + data79.caption !== undefined ) { - const _errs218 = + const _errs220 = errors; if ( - typeof data78.caption !== + typeof data79.caption !== 'string' ) { validate14.errors = @@ -12543,7 +12627,7 @@ function validate14( return false; } var valid46 = - _errs218 === + _errs220 === errors; } else { var valid46 = true; @@ -12569,16 +12653,16 @@ function validate14( return false; } } - var valid45 = _errs213 === errors; + var valid45 = _errs215 === errors; } else { var valid45 = true; } if (valid45) { if (data.step !== undefined) { - let data81 = data.step; - const _errs220 = errors; + let data82 = data.step; + const _errs222 = errors; if ( - typeof data81 !== 'string' + typeof data82 !== 'string' ) { validate14.errors = [ { @@ -12597,7 +12681,7 @@ function validate14( ]; return false; } - if ('mkdir' !== data81) { + if ('mkdir' !== data82) { validate14.errors = [ { instancePath: @@ -12617,13 +12701,13 @@ function validate14( return false; } var valid45 = - _errs220 === errors; + _errs222 === errors; } else { var valid45 = true; } if (valid45) { if (data.path !== undefined) { - const _errs222 = errors; + const _errs224 = errors; if ( typeof data.path !== 'string' @@ -12646,7 +12730,7 @@ function validate14( return false; } var valid45 = - _errs222 === errors; + _errs224 === errors; } else { var valid45 = true; } @@ -12668,8 +12752,8 @@ function validate14( } } } else if (tag0 === 'mv') { - const _errs224 = errors; - if (errors === _errs224) { + const _errs226 = errors; + if (errors === _errs226) { if ( data && typeof data == 'object' && @@ -12700,7 +12784,7 @@ function validate14( ]; return false; } else { - const _errs226 = errors; + const _errs228 = errors; for (const key29 in data) { if ( !( @@ -12729,18 +12813,18 @@ function validate14( break; } } - if (_errs226 === errors) { + if (_errs228 === errors) { if (data.progress !== undefined) { - let data83 = data.progress; - const _errs227 = errors; - if (errors === _errs227) { + let data84 = data.progress; + const _errs229 = errors; + if (errors === _errs229) { if ( - data83 && - typeof data83 == 'object' && - !Array.isArray(data83) + data84 && + typeof data84 == 'object' && + !Array.isArray(data84) ) { - const _errs229 = errors; - for (const key30 in data83) { + const _errs231 = errors; + for (const key30 in data84) { if ( !( key30 === @@ -12771,21 +12855,21 @@ function validate14( break; } } - if (_errs229 === errors) { + if (_errs231 === errors) { if ( - data83.weight !== + data84.weight !== undefined ) { - let data84 = - data83.weight; - const _errs230 = + let data85 = + data84.weight; + const _errs232 = errors; if ( !( - typeof data84 == + typeof data85 == 'number' && isFinite( - data84 + data85 ) ) ) { @@ -12809,20 +12893,20 @@ function validate14( return false; } var valid49 = - _errs230 === + _errs232 === errors; } else { var valid49 = true; } if (valid49) { if ( - data83.caption !== + data84.caption !== undefined ) { - const _errs232 = + const _errs234 = errors; if ( - typeof data83.caption !== + typeof data84.caption !== 'string' ) { validate14.errors = @@ -12845,7 +12929,7 @@ function validate14( return false; } var valid49 = - _errs232 === + _errs234 === errors; } else { var valid49 = true; @@ -12871,16 +12955,16 @@ function validate14( return false; } } - var valid48 = _errs227 === errors; + var valid48 = _errs229 === errors; } else { var valid48 = true; } if (valid48) { if (data.step !== undefined) { - let data86 = data.step; - const _errs234 = errors; + let data87 = data.step; + const _errs236 = errors; if ( - typeof data86 !== 'string' + typeof data87 !== 'string' ) { validate14.errors = [ { @@ -12899,7 +12983,7 @@ function validate14( ]; return false; } - if ('mv' !== data86) { + if ('mv' !== data87) { validate14.errors = [ { instancePath: @@ -12919,7 +13003,7 @@ function validate14( return false; } var valid48 = - _errs234 === errors; + _errs236 === errors; } else { var valid48 = true; } @@ -12927,7 +13011,7 @@ function validate14( if ( data.fromPath !== undefined ) { - const _errs236 = errors; + const _errs238 = errors; if ( typeof data.fromPath !== 'string' @@ -12950,7 +13034,7 @@ function validate14( return false; } var valid48 = - _errs236 === errors; + _errs238 === errors; } else { var valid48 = true; } @@ -12959,7 +13043,7 @@ function validate14( data.toPath !== undefined ) { - const _errs238 = errors; + const _errs240 = errors; if ( typeof data.toPath !== 'string' @@ -12984,7 +13068,7 @@ function validate14( return false; } var valid48 = - _errs238 === errors; + _errs240 === errors; } else { var valid48 = true; } @@ -13007,8 +13091,8 @@ function validate14( } } } else if (tag0 === 'resetData') { - const _errs240 = errors; - if (errors === _errs240) { + const _errs242 = errors; + if (errors === _errs242) { if ( data && typeof data == 'object' && @@ -13035,7 +13119,7 @@ function validate14( ]; return false; } else { - const _errs242 = errors; + const _errs244 = errors; for (const key31 in data) { if ( !( @@ -13062,18 +13146,18 @@ function validate14( break; } } - if (_errs242 === errors) { + if (_errs244 === errors) { if (data.progress !== undefined) { - let data89 = data.progress; - const _errs243 = errors; - if (errors === _errs243) { + let data90 = data.progress; + const _errs245 = errors; + if (errors === _errs245) { if ( - data89 && - typeof data89 == 'object' && - !Array.isArray(data89) + data90 && + typeof data90 == 'object' && + !Array.isArray(data90) ) { - const _errs245 = errors; - for (const key32 in data89) { + const _errs247 = errors; + for (const key32 in data90) { if ( !( key32 === @@ -13104,21 +13188,21 @@ function validate14( break; } } - if (_errs245 === errors) { + if (_errs247 === errors) { if ( - data89.weight !== + data90.weight !== undefined ) { - let data90 = - data89.weight; - const _errs246 = + let data91 = + data90.weight; + const _errs248 = errors; if ( !( - typeof data90 == + typeof data91 == 'number' && isFinite( - data90 + data91 ) ) ) { @@ -13142,20 +13226,20 @@ function validate14( return false; } var valid52 = - _errs246 === + _errs248 === errors; } else { var valid52 = true; } if (valid52) { if ( - data89.caption !== + data90.caption !== undefined ) { - const _errs248 = + const _errs250 = errors; if ( - typeof data89.caption !== + typeof data90.caption !== 'string' ) { validate14.errors = @@ -13178,7 +13262,7 @@ function validate14( return false; } var valid52 = - _errs248 === + _errs250 === errors; } else { var valid52 = true; @@ -13204,16 +13288,16 @@ function validate14( return false; } } - var valid51 = _errs243 === errors; + var valid51 = _errs245 === errors; } else { var valid51 = true; } if (valid51) { if (data.step !== undefined) { - let data92 = data.step; - const _errs250 = errors; + let data93 = data.step; + const _errs252 = errors; if ( - typeof data92 !== 'string' + typeof data93 !== 'string' ) { validate14.errors = [ { @@ -13232,7 +13316,7 @@ function validate14( ]; return false; } - if ('resetData' !== data92) { + if ('resetData' !== data93) { validate14.errors = [ { instancePath: @@ -13252,7 +13336,7 @@ function validate14( return false; } var valid51 = - _errs250 === errors; + _errs252 === errors; } else { var valid51 = true; } @@ -13273,8 +13357,8 @@ function validate14( } } } else if (tag0 === 'request') { - const _errs252 = errors; - if (errors === _errs252) { + const _errs254 = errors; + if (errors === _errs254) { if ( data && typeof data == 'object' && @@ -13303,7 +13387,7 @@ function validate14( ]; return false; } else { - const _errs254 = errors; + const _errs256 = errors; for (const key33 in data) { if ( !( @@ -13331,18 +13415,18 @@ function validate14( break; } } - if (_errs254 === errors) { + if (_errs256 === errors) { if (data.progress !== undefined) { - let data93 = data.progress; - const _errs255 = errors; - if (errors === _errs255) { + let data94 = data.progress; + const _errs257 = errors; + if (errors === _errs257) { if ( - data93 && - typeof data93 == 'object' && - !Array.isArray(data93) + data94 && + typeof data94 == 'object' && + !Array.isArray(data94) ) { - const _errs257 = errors; - for (const key34 in data93) { + const _errs259 = errors; + for (const key34 in data94) { if ( !( key34 === @@ -13373,21 +13457,21 @@ function validate14( break; } } - if (_errs257 === errors) { + if (_errs259 === errors) { if ( - data93.weight !== + data94.weight !== undefined ) { - let data94 = - data93.weight; - const _errs258 = + let data95 = + data94.weight; + const _errs260 = errors; if ( !( - typeof data94 == + typeof data95 == 'number' && isFinite( - data94 + data95 ) ) ) { @@ -13411,20 +13495,20 @@ function validate14( return false; } var valid55 = - _errs258 === + _errs260 === errors; } else { var valid55 = true; } if (valid55) { if ( - data93.caption !== + data94.caption !== undefined ) { - const _errs260 = + const _errs262 = errors; if ( - typeof data93.caption !== + typeof data94.caption !== 'string' ) { validate14.errors = @@ -13447,7 +13531,7 @@ function validate14( return false; } var valid55 = - _errs260 === + _errs262 === errors; } else { var valid55 = true; @@ -13473,16 +13557,16 @@ function validate14( return false; } } - var valid54 = _errs255 === errors; + var valid54 = _errs257 === errors; } else { var valid54 = true; } if (valid54) { if (data.step !== undefined) { - let data96 = data.step; - const _errs262 = errors; + let data97 = data.step; + const _errs264 = errors; if ( - typeof data96 !== 'string' + typeof data97 !== 'string' ) { validate14.errors = [ { @@ -13501,7 +13585,7 @@ function validate14( ]; return false; } - if ('request' !== data96) { + if ('request' !== data97) { validate14.errors = [ { instancePath: @@ -13521,7 +13605,7 @@ function validate14( return false; } var valid54 = - _errs262 === errors; + _errs264 === errors; } else { var valid54 = true; } @@ -13529,7 +13613,7 @@ function validate14( if ( data.request !== undefined ) { - const _errs264 = errors; + const _errs266 = errors; if ( !validate28( data.request, @@ -13554,7 +13638,7 @@ function validate14( errors = vErrors.length; } var valid54 = - _errs264 === errors; + _errs266 === errors; } else { var valid54 = true; } @@ -13576,8 +13660,8 @@ function validate14( } } } else if (tag0 === 'rm') { - const _errs265 = errors; - if (errors === _errs265) { + const _errs267 = errors; + if (errors === _errs267) { if ( data && typeof data == 'object' && @@ -13606,7 +13690,7 @@ function validate14( ]; return false; } else { - const _errs267 = errors; + const _errs269 = errors; for (const key35 in data) { if ( !( @@ -13634,18 +13718,18 @@ function validate14( break; } } - if (_errs267 === errors) { + if (_errs269 === errors) { if (data.progress !== undefined) { - let data98 = data.progress; - const _errs268 = errors; - if (errors === _errs268) { + let data99 = data.progress; + const _errs270 = errors; + if (errors === _errs270) { if ( - data98 && - typeof data98 == 'object' && - !Array.isArray(data98) + data99 && + typeof data99 == 'object' && + !Array.isArray(data99) ) { - const _errs270 = errors; - for (const key36 in data98) { + const _errs272 = errors; + for (const key36 in data99) { if ( !( key36 === @@ -13676,21 +13760,21 @@ function validate14( break; } } - if (_errs270 === errors) { + if (_errs272 === errors) { if ( - data98.weight !== + data99.weight !== undefined ) { - let data99 = - data98.weight; - const _errs271 = + let data100 = + data99.weight; + const _errs273 = errors; if ( !( - typeof data99 == + typeof data100 == 'number' && isFinite( - data99 + data100 ) ) ) { @@ -13714,20 +13798,20 @@ function validate14( return false; } var valid58 = - _errs271 === + _errs273 === errors; } else { var valid58 = true; } if (valid58) { if ( - data98.caption !== + data99.caption !== undefined ) { - const _errs273 = + const _errs275 = errors; if ( - typeof data98.caption !== + typeof data99.caption !== 'string' ) { validate14.errors = @@ -13750,7 +13834,7 @@ function validate14( return false; } var valid58 = - _errs273 === + _errs275 === errors; } else { var valid58 = true; @@ -13776,16 +13860,16 @@ function validate14( return false; } } - var valid57 = _errs268 === errors; + var valid57 = _errs270 === errors; } else { var valid57 = true; } if (valid57) { if (data.step !== undefined) { - let data101 = data.step; - const _errs275 = errors; + let data102 = data.step; + const _errs277 = errors; if ( - typeof data101 !== 'string' + typeof data102 !== 'string' ) { validate14.errors = [ { @@ -13804,7 +13888,7 @@ function validate14( ]; return false; } - if ('rm' !== data101) { + if ('rm' !== data102) { validate14.errors = [ { instancePath: @@ -13824,13 +13908,13 @@ function validate14( return false; } var valid57 = - _errs275 === errors; + _errs277 === errors; } else { var valid57 = true; } if (valid57) { if (data.path !== undefined) { - const _errs277 = errors; + const _errs279 = errors; if ( typeof data.path !== 'string' @@ -13853,7 +13937,7 @@ function validate14( return false; } var valid57 = - _errs277 === errors; + _errs279 === errors; } else { var valid57 = true; } @@ -13875,8 +13959,8 @@ function validate14( } } } else if (tag0 === 'rmdir') { - const _errs279 = errors; - if (errors === _errs279) { + const _errs281 = errors; + if (errors === _errs281) { if ( data && typeof data == 'object' && @@ -13905,7 +13989,7 @@ function validate14( ]; return false; } else { - const _errs281 = errors; + const _errs283 = errors; for (const key37 in data) { if ( !( @@ -13933,19 +14017,19 @@ function validate14( break; } } - if (_errs281 === errors) { + if (_errs283 === errors) { if (data.progress !== undefined) { - let data103 = data.progress; - const _errs282 = errors; - if (errors === _errs282) { + let data104 = data.progress; + const _errs284 = errors; + if (errors === _errs284) { if ( - data103 && - typeof data103 == + data104 && + typeof data104 == 'object' && - !Array.isArray(data103) + !Array.isArray(data104) ) { - const _errs284 = errors; - for (const key38 in data103) { + const _errs286 = errors; + for (const key38 in data104) { if ( !( key38 === @@ -13976,21 +14060,21 @@ function validate14( break; } } - if (_errs284 === errors) { + if (_errs286 === errors) { if ( - data103.weight !== + data104.weight !== undefined ) { - let data104 = - data103.weight; - const _errs285 = + let data105 = + data104.weight; + const _errs287 = errors; if ( !( - typeof data104 == + typeof data105 == 'number' && isFinite( - data104 + data105 ) ) ) { @@ -14014,20 +14098,20 @@ function validate14( return false; } var valid61 = - _errs285 === + _errs287 === errors; } else { var valid61 = true; } if (valid61) { if ( - data103.caption !== + data104.caption !== undefined ) { - const _errs287 = + const _errs289 = errors; if ( - typeof data103.caption !== + typeof data104.caption !== 'string' ) { validate14.errors = @@ -14050,7 +14134,7 @@ function validate14( return false; } var valid61 = - _errs287 === + _errs289 === errors; } else { var valid61 = true; @@ -14076,16 +14160,16 @@ function validate14( return false; } } - var valid60 = _errs282 === errors; + var valid60 = _errs284 === errors; } else { var valid60 = true; } if (valid60) { if (data.step !== undefined) { - let data106 = data.step; - const _errs289 = errors; + let data107 = data.step; + const _errs291 = errors; if ( - typeof data106 !== 'string' + typeof data107 !== 'string' ) { validate14.errors = [ { @@ -14104,7 +14188,7 @@ function validate14( ]; return false; } - if ('rmdir' !== data106) { + if ('rmdir' !== data107) { validate14.errors = [ { instancePath: @@ -14124,13 +14208,13 @@ function validate14( return false; } var valid60 = - _errs289 === errors; + _errs291 === errors; } else { var valid60 = true; } if (valid60) { if (data.path !== undefined) { - const _errs291 = errors; + const _errs293 = errors; if ( typeof data.path !== 'string' @@ -14153,7 +14237,7 @@ function validate14( return false; } var valid60 = - _errs291 === errors; + _errs293 === errors; } else { var valid60 = true; } @@ -14175,8 +14259,8 @@ function validate14( } } } else if (tag0 === 'runPHP') { - const _errs293 = errors; - if (errors === _errs293) { + const _errs295 = errors; + if (errors === _errs295) { if ( data && typeof data == 'object' && @@ -14205,7 +14289,7 @@ function validate14( ]; return false; } else { - const _errs295 = errors; + const _errs297 = errors; for (const key39 in data) { if ( !( @@ -14233,19 +14317,19 @@ function validate14( break; } } - if (_errs295 === errors) { + if (_errs297 === errors) { if (data.progress !== undefined) { - let data108 = data.progress; - const _errs296 = errors; - if (errors === _errs296) { + let data109 = data.progress; + const _errs298 = errors; + if (errors === _errs298) { if ( - data108 && - typeof data108 == + data109 && + typeof data109 == 'object' && - !Array.isArray(data108) + !Array.isArray(data109) ) { - const _errs298 = errors; - for (const key40 in data108) { + const _errs300 = errors; + for (const key40 in data109) { if ( !( key40 === @@ -14276,21 +14360,21 @@ function validate14( break; } } - if (_errs298 === errors) { + if (_errs300 === errors) { if ( - data108.weight !== + data109.weight !== undefined ) { - let data109 = - data108.weight; - const _errs299 = + let data110 = + data109.weight; + const _errs301 = errors; if ( !( - typeof data109 == + typeof data110 == 'number' && isFinite( - data109 + data110 ) ) ) { @@ -14314,20 +14398,20 @@ function validate14( return false; } var valid64 = - _errs299 === + _errs301 === errors; } else { var valid64 = true; } if (valid64) { if ( - data108.caption !== + data109.caption !== undefined ) { - const _errs301 = + const _errs303 = errors; if ( - typeof data108.caption !== + typeof data109.caption !== 'string' ) { validate14.errors = @@ -14350,7 +14434,7 @@ function validate14( return false; } var valid64 = - _errs301 === + _errs303 === errors; } else { var valid64 = true; @@ -14376,16 +14460,16 @@ function validate14( return false; } } - var valid63 = _errs296 === errors; + var valid63 = _errs298 === errors; } else { var valid63 = true; } if (valid63) { if (data.step !== undefined) { - let data111 = data.step; - const _errs303 = errors; + let data112 = data.step; + const _errs305 = errors; if ( - typeof data111 !== 'string' + typeof data112 !== 'string' ) { validate14.errors = [ { @@ -14404,7 +14488,7 @@ function validate14( ]; return false; } - if ('runPHP' !== data111) { + if ('runPHP' !== data112) { validate14.errors = [ { instancePath: @@ -14424,13 +14508,13 @@ function validate14( return false; } var valid63 = - _errs303 === errors; + _errs305 === errors; } else { var valid63 = true; } if (valid63) { if (data.code !== undefined) { - const _errs305 = errors; + const _errs307 = errors; if ( typeof data.code !== 'string' @@ -14453,7 +14537,7 @@ function validate14( return false; } var valid63 = - _errs305 === errors; + _errs307 === errors; } else { var valid63 = true; } @@ -14475,8 +14559,8 @@ function validate14( } } } else if (tag0 === 'runPHPWithOptions') { - const _errs307 = errors; - if (errors === _errs307) { + const _errs309 = errors; + if (errors === _errs309) { if ( data && typeof data == 'object' && @@ -14505,7 +14589,7 @@ function validate14( ]; return false; } else { - const _errs309 = errors; + const _errs311 = errors; for (const key41 in data) { if ( !( @@ -14533,19 +14617,19 @@ function validate14( break; } } - if (_errs309 === errors) { + if (_errs311 === errors) { if (data.progress !== undefined) { - let data113 = data.progress; - const _errs310 = errors; - if (errors === _errs310) { + let data114 = data.progress; + const _errs312 = errors; + if (errors === _errs312) { if ( - data113 && - typeof data113 == + data114 && + typeof data114 == 'object' && - !Array.isArray(data113) + !Array.isArray(data114) ) { - const _errs312 = errors; - for (const key42 in data113) { + const _errs314 = errors; + for (const key42 in data114) { if ( !( key42 === @@ -14576,21 +14660,21 @@ function validate14( break; } } - if (_errs312 === errors) { + if (_errs314 === errors) { if ( - data113.weight !== + data114.weight !== undefined ) { - let data114 = - data113.weight; - const _errs313 = + let data115 = + data114.weight; + const _errs315 = errors; if ( !( - typeof data114 == + typeof data115 == 'number' && isFinite( - data114 + data115 ) ) ) { @@ -14614,20 +14698,20 @@ function validate14( return false; } var valid67 = - _errs313 === + _errs315 === errors; } else { var valid67 = true; } if (valid67) { if ( - data113.caption !== + data114.caption !== undefined ) { - const _errs315 = + const _errs317 = errors; if ( - typeof data113.caption !== + typeof data114.caption !== 'string' ) { validate14.errors = @@ -14650,7 +14734,7 @@ function validate14( return false; } var valid67 = - _errs315 === + _errs317 === errors; } else { var valid67 = true; @@ -14676,16 +14760,16 @@ function validate14( return false; } } - var valid66 = _errs310 === errors; + var valid66 = _errs312 === errors; } else { var valid66 = true; } if (valid66) { if (data.step !== undefined) { - let data116 = data.step; - const _errs317 = errors; + let data117 = data.step; + const _errs319 = errors; if ( - typeof data116 !== 'string' + typeof data117 !== 'string' ) { validate14.errors = [ { @@ -14706,7 +14790,7 @@ function validate14( } if ( 'runPHPWithOptions' !== - data116 + data117 ) { validate14.errors = [ { @@ -14727,7 +14811,7 @@ function validate14( return false; } var valid66 = - _errs317 === errors; + _errs319 === errors; } else { var valid66 = true; } @@ -14735,7 +14819,7 @@ function validate14( if ( data.options !== undefined ) { - const _errs319 = errors; + const _errs321 = errors; if ( !validate30( data.options, @@ -14760,7 +14844,7 @@ function validate14( errors = vErrors.length; } var valid66 = - _errs319 === errors; + _errs321 === errors; } else { var valid66 = true; } @@ -14782,8 +14866,8 @@ function validate14( } } } else if (tag0 === 'runWpInstallationWizard') { - const _errs320 = errors; - if (errors === _errs320) { + const _errs322 = errors; + if (errors === _errs322) { if ( data && typeof data == 'object' && @@ -14812,7 +14896,7 @@ function validate14( ]; return false; } else { - const _errs322 = errors; + const _errs324 = errors; for (const key43 in data) { if ( !( @@ -14840,19 +14924,19 @@ function validate14( break; } } - if (_errs322 === errors) { + if (_errs324 === errors) { if (data.progress !== undefined) { - let data118 = data.progress; - const _errs323 = errors; - if (errors === _errs323) { + let data119 = data.progress; + const _errs325 = errors; + if (errors === _errs325) { if ( - data118 && - typeof data118 == + data119 && + typeof data119 == 'object' && - !Array.isArray(data118) + !Array.isArray(data119) ) { - const _errs325 = errors; - for (const key44 in data118) { + const _errs327 = errors; + for (const key44 in data119) { if ( !( key44 === @@ -14883,21 +14967,21 @@ function validate14( break; } } - if (_errs325 === errors) { + if (_errs327 === errors) { if ( - data118.weight !== + data119.weight !== undefined ) { - let data119 = - data118.weight; - const _errs326 = + let data120 = + data119.weight; + const _errs328 = errors; if ( !( - typeof data119 == + typeof data120 == 'number' && isFinite( - data119 + data120 ) ) ) { @@ -14921,20 +15005,20 @@ function validate14( return false; } var valid70 = - _errs326 === + _errs328 === errors; } else { var valid70 = true; } if (valid70) { if ( - data118.caption !== + data119.caption !== undefined ) { - const _errs328 = + const _errs330 = errors; if ( - typeof data118.caption !== + typeof data119.caption !== 'string' ) { validate14.errors = @@ -14957,7 +15041,7 @@ function validate14( return false; } var valid70 = - _errs328 === + _errs330 === errors; } else { var valid70 = true; @@ -14983,16 +15067,16 @@ function validate14( return false; } } - var valid69 = _errs323 === errors; + var valid69 = _errs325 === errors; } else { var valid69 = true; } if (valid69) { if (data.step !== undefined) { - let data121 = data.step; - const _errs330 = errors; + let data122 = data.step; + const _errs332 = errors; if ( - typeof data121 !== 'string' + typeof data122 !== 'string' ) { validate14.errors = [ { @@ -15013,7 +15097,7 @@ function validate14( } if ( 'runWpInstallationWizard' !== - data121 + data122 ) { validate14.errors = [ { @@ -15034,7 +15118,7 @@ function validate14( return false; } var valid69 = - _errs330 === errors; + _errs332 === errors; } else { var valid69 = true; } @@ -15042,21 +15126,21 @@ function validate14( if ( data.options !== undefined ) { - let data122 = data.options; - const _errs332 = errors; - const _errs333 = errors; - if (errors === _errs333) { + let data123 = data.options; + const _errs334 = errors; + const _errs335 = errors; + if (errors === _errs335) { if ( - data122 && - typeof data122 == + data123 && + typeof data123 == 'object' && !Array.isArray( - data122 + data123 ) ) { - const _errs335 = + const _errs337 = errors; - for (const key45 in data122) { + for (const key45 in data123) { if ( !( key45 === @@ -15088,17 +15172,17 @@ function validate14( } } if ( - _errs335 === + _errs337 === errors ) { if ( - data122.adminUsername !== + data123.adminUsername !== undefined ) { - const _errs336 = + const _errs338 = errors; if ( - typeof data122.adminUsername !== + typeof data123.adminUsername !== 'string' ) { validate14.errors = @@ -15121,20 +15205,20 @@ function validate14( return false; } var valid72 = - _errs336 === + _errs338 === errors; } else { var valid72 = true; } if (valid72) { if ( - data122.adminPassword !== + data123.adminPassword !== undefined ) { - const _errs338 = + const _errs340 = errors; if ( - typeof data122.adminPassword !== + typeof data123.adminPassword !== 'string' ) { validate14.errors = @@ -15157,7 +15241,7 @@ function validate14( return false; } var valid72 = - _errs338 === + _errs340 === errors; } else { var valid72 = true; @@ -15186,7 +15270,7 @@ function validate14( } } var valid69 = - _errs332 === errors; + _errs334 === errors; } else { var valid69 = true; } @@ -15208,8 +15292,8 @@ function validate14( } } } else if (tag0 === 'runSql') { - const _errs340 = errors; - if (errors === _errs340) { + const _errs342 = errors; + if (errors === _errs342) { if ( data && typeof data == 'object' && @@ -15238,7 +15322,7 @@ function validate14( ]; return false; } else { - const _errs342 = errors; + const _errs344 = errors; for (const key46 in data) { if ( !( @@ -15266,19 +15350,19 @@ function validate14( break; } } - if (_errs342 === errors) { + if (_errs344 === errors) { if (data.progress !== undefined) { - let data125 = data.progress; - const _errs343 = errors; - if (errors === _errs343) { + let data126 = data.progress; + const _errs345 = errors; + if (errors === _errs345) { if ( - data125 && - typeof data125 == + data126 && + typeof data126 == 'object' && - !Array.isArray(data125) + !Array.isArray(data126) ) { - const _errs345 = errors; - for (const key47 in data125) { + const _errs347 = errors; + for (const key47 in data126) { if ( !( key47 === @@ -15309,21 +15393,21 @@ function validate14( break; } } - if (_errs345 === errors) { + if (_errs347 === errors) { if ( - data125.weight !== + data126.weight !== undefined ) { - let data126 = - data125.weight; - const _errs346 = + let data127 = + data126.weight; + const _errs348 = errors; if ( !( - typeof data126 == + typeof data127 == 'number' && isFinite( - data126 + data127 ) ) ) { @@ -15347,20 +15431,20 @@ function validate14( return false; } var valid75 = - _errs346 === + _errs348 === errors; } else { var valid75 = true; } if (valid75) { if ( - data125.caption !== + data126.caption !== undefined ) { - const _errs348 = + const _errs350 = errors; if ( - typeof data125.caption !== + typeof data126.caption !== 'string' ) { validate14.errors = @@ -15383,7 +15467,7 @@ function validate14( return false; } var valid75 = - _errs348 === + _errs350 === errors; } else { var valid75 = true; @@ -15409,16 +15493,16 @@ function validate14( return false; } } - var valid74 = _errs343 === errors; + var valid74 = _errs345 === errors; } else { var valid74 = true; } if (valid74) { if (data.step !== undefined) { - let data128 = data.step; - const _errs350 = errors; + let data129 = data.step; + const _errs352 = errors; if ( - typeof data128 !== 'string' + typeof data129 !== 'string' ) { validate14.errors = [ { @@ -15437,7 +15521,7 @@ function validate14( ]; return false; } - if ('runSql' !== data128) { + if ('runSql' !== data129) { validate14.errors = [ { instancePath: @@ -15457,13 +15541,13 @@ function validate14( return false; } var valid74 = - _errs350 === errors; + _errs352 === errors; } else { var valid74 = true; } if (valid74) { if (data.sql !== undefined) { - const _errs352 = errors; + const _errs354 = errors; if ( !validate12(data.sql, { instancePath: @@ -15484,7 +15568,7 @@ function validate14( errors = vErrors.length; } var valid74 = - _errs352 === errors; + _errs354 === errors; } else { var valid74 = true; } @@ -15506,8 +15590,8 @@ function validate14( } } } else if (tag0 === 'setSiteOptions') { - const _errs353 = errors; - if (errors === _errs353) { + const _errs355 = errors; + if (errors === _errs355) { if ( data && typeof data == 'object' && @@ -15536,7 +15620,7 @@ function validate14( ]; return false; } else { - const _errs355 = errors; + const _errs357 = errors; for (const key48 in data) { if ( !( @@ -15564,19 +15648,19 @@ function validate14( break; } } - if (_errs355 === errors) { + if (_errs357 === errors) { if (data.progress !== undefined) { - let data130 = data.progress; - const _errs356 = errors; - if (errors === _errs356) { + let data131 = data.progress; + const _errs358 = errors; + if (errors === _errs358) { if ( - data130 && - typeof data130 == + data131 && + typeof data131 == 'object' && - !Array.isArray(data130) + !Array.isArray(data131) ) { - const _errs358 = errors; - for (const key49 in data130) { + const _errs360 = errors; + for (const key49 in data131) { if ( !( key49 === @@ -15607,21 +15691,21 @@ function validate14( break; } } - if (_errs358 === errors) { + if (_errs360 === errors) { if ( - data130.weight !== + data131.weight !== undefined ) { - let data131 = - data130.weight; - const _errs359 = + let data132 = + data131.weight; + const _errs361 = errors; if ( !( - typeof data131 == + typeof data132 == 'number' && isFinite( - data131 + data132 ) ) ) { @@ -15645,20 +15729,20 @@ function validate14( return false; } var valid78 = - _errs359 === + _errs361 === errors; } else { var valid78 = true; } if (valid78) { if ( - data130.caption !== + data131.caption !== undefined ) { - const _errs361 = + const _errs363 = errors; if ( - typeof data130.caption !== + typeof data131.caption !== 'string' ) { validate14.errors = @@ -15681,7 +15765,7 @@ function validate14( return false; } var valid78 = - _errs361 === + _errs363 === errors; } else { var valid78 = true; @@ -15707,16 +15791,16 @@ function validate14( return false; } } - var valid77 = _errs356 === errors; + var valid77 = _errs358 === errors; } else { var valid77 = true; } if (valid77) { if (data.step !== undefined) { - let data133 = data.step; - const _errs363 = errors; + let data134 = data.step; + const _errs365 = errors; if ( - typeof data133 !== 'string' + typeof data134 !== 'string' ) { validate14.errors = [ { @@ -15736,7 +15820,7 @@ function validate14( return false; } if ( - 'setSiteOptions' !== data133 + 'setSiteOptions' !== data134 ) { validate14.errors = [ { @@ -15757,7 +15841,7 @@ function validate14( return false; } var valid77 = - _errs363 === errors; + _errs365 === errors; } else { var valid77 = true; } @@ -15765,22 +15849,22 @@ function validate14( if ( data.options !== undefined ) { - let data134 = data.options; - const _errs365 = errors; - if (errors === _errs365) { + let data135 = data.options; + const _errs367 = errors; + if (errors === _errs367) { if ( - data134 && - typeof data134 == + data135 && + typeof data135 == 'object' && !Array.isArray( - data134 + data135 ) ) { - for (const key50 in data134) { - const _errs368 = + for (const key50 in data135) { + const _errs370 = errors; var valid79 = - _errs368 === + _errs370 === errors; if (!valid79) { break; @@ -15808,7 +15892,7 @@ function validate14( } } var valid77 = - _errs365 === errors; + _errs367 === errors; } else { var valid77 = true; } @@ -15830,8 +15914,8 @@ function validate14( } } } else if (tag0 === 'unzip') { - const _errs369 = errors; - if (errors === _errs369) { + const _errs371 = errors; + if (errors === _errs371) { if ( data && typeof data == 'object' && @@ -15860,7 +15944,7 @@ function validate14( ]; return false; } else { - const _errs371 = errors; + const _errs373 = errors; for (const key51 in data) { if ( !( @@ -15890,19 +15974,19 @@ function validate14( break; } } - if (_errs371 === errors) { + if (_errs373 === errors) { if (data.progress !== undefined) { - let data136 = data.progress; - const _errs372 = errors; - if (errors === _errs372) { + let data137 = data.progress; + const _errs374 = errors; + if (errors === _errs374) { if ( - data136 && - typeof data136 == + data137 && + typeof data137 == 'object' && - !Array.isArray(data136) + !Array.isArray(data137) ) { - const _errs374 = errors; - for (const key52 in data136) { + const _errs376 = errors; + for (const key52 in data137) { if ( !( key52 === @@ -15933,21 +16017,21 @@ function validate14( break; } } - if (_errs374 === errors) { + if (_errs376 === errors) { if ( - data136.weight !== + data137.weight !== undefined ) { - let data137 = - data136.weight; - const _errs375 = + let data138 = + data137.weight; + const _errs377 = errors; if ( !( - typeof data137 == + typeof data138 == 'number' && isFinite( - data137 + data138 ) ) ) { @@ -15971,20 +16055,20 @@ function validate14( return false; } var valid82 = - _errs375 === + _errs377 === errors; } else { var valid82 = true; } if (valid82) { if ( - data136.caption !== + data137.caption !== undefined ) { - const _errs377 = + const _errs379 = errors; if ( - typeof data136.caption !== + typeof data137.caption !== 'string' ) { validate14.errors = @@ -16007,7 +16091,7 @@ function validate14( return false; } var valid82 = - _errs377 === + _errs379 === errors; } else { var valid82 = true; @@ -16033,16 +16117,16 @@ function validate14( return false; } } - var valid81 = _errs372 === errors; + var valid81 = _errs374 === errors; } else { var valid81 = true; } if (valid81) { if (data.step !== undefined) { - let data139 = data.step; - const _errs379 = errors; + let data140 = data.step; + const _errs381 = errors; if ( - typeof data139 !== 'string' + typeof data140 !== 'string' ) { validate14.errors = [ { @@ -16061,7 +16145,7 @@ function validate14( ]; return false; } - if ('unzip' !== data139) { + if ('unzip' !== data140) { validate14.errors = [ { instancePath: @@ -16081,7 +16165,7 @@ function validate14( return false; } var valid81 = - _errs379 === errors; + _errs381 === errors; } else { var valid81 = true; } @@ -16089,7 +16173,7 @@ function validate14( if ( data.zipFile !== undefined ) { - const _errs381 = errors; + const _errs383 = errors; if ( !validate12( data.zipFile, @@ -16114,7 +16198,7 @@ function validate14( errors = vErrors.length; } var valid81 = - _errs381 === errors; + _errs383 === errors; } else { var valid81 = true; } @@ -16123,7 +16207,7 @@ function validate14( data.zipPath !== undefined ) { - const _errs382 = errors; + const _errs384 = errors; if ( typeof data.zipPath !== 'string' @@ -16148,7 +16232,7 @@ function validate14( return false; } var valid81 = - _errs382 === errors; + _errs384 === errors; } else { var valid81 = true; } @@ -16157,7 +16241,7 @@ function validate14( data.extractToPath !== undefined ) { - const _errs384 = + const _errs386 = errors; if ( typeof data.extractToPath !== @@ -16183,7 +16267,7 @@ function validate14( return false; } var valid81 = - _errs384 === + _errs386 === errors; } else { var valid81 = true; @@ -16208,8 +16292,8 @@ function validate14( } } } else if (tag0 === 'updateUserMeta') { - const _errs386 = errors; - if (errors === _errs386) { + const _errs388 = errors; + if (errors === _errs388) { if ( data && typeof data == 'object' && @@ -16240,7 +16324,7 @@ function validate14( ]; return false; } else { - const _errs388 = errors; + const _errs390 = errors; for (const key53 in data) { if ( !( @@ -16269,19 +16353,19 @@ function validate14( break; } } - if (_errs388 === errors) { + if (_errs390 === errors) { if (data.progress !== undefined) { - let data143 = data.progress; - const _errs389 = errors; - if (errors === _errs389) { + let data144 = data.progress; + const _errs391 = errors; + if (errors === _errs391) { if ( - data143 && - typeof data143 == + data144 && + typeof data144 == 'object' && - !Array.isArray(data143) + !Array.isArray(data144) ) { - const _errs391 = errors; - for (const key54 in data143) { + const _errs393 = errors; + for (const key54 in data144) { if ( !( key54 === @@ -16312,21 +16396,21 @@ function validate14( break; } } - if (_errs391 === errors) { + if (_errs393 === errors) { if ( - data143.weight !== + data144.weight !== undefined ) { - let data144 = - data143.weight; - const _errs392 = + let data145 = + data144.weight; + const _errs394 = errors; if ( !( - typeof data144 == + typeof data145 == 'number' && isFinite( - data144 + data145 ) ) ) { @@ -16350,20 +16434,20 @@ function validate14( return false; } var valid85 = - _errs392 === + _errs394 === errors; } else { var valid85 = true; } if (valid85) { if ( - data143.caption !== + data144.caption !== undefined ) { - const _errs394 = + const _errs396 = errors; if ( - typeof data143.caption !== + typeof data144.caption !== 'string' ) { validate14.errors = @@ -16386,7 +16470,7 @@ function validate14( return false; } var valid85 = - _errs394 === + _errs396 === errors; } else { var valid85 = true; @@ -16412,16 +16496,16 @@ function validate14( return false; } } - var valid84 = _errs389 === errors; + var valid84 = _errs391 === errors; } else { var valid84 = true; } if (valid84) { if (data.step !== undefined) { - let data146 = data.step; - const _errs396 = errors; + let data147 = data.step; + const _errs398 = errors; if ( - typeof data146 !== 'string' + typeof data147 !== 'string' ) { validate14.errors = [ { @@ -16441,7 +16525,7 @@ function validate14( return false; } if ( - 'updateUserMeta' !== data146 + 'updateUserMeta' !== data147 ) { validate14.errors = [ { @@ -16462,28 +16546,28 @@ function validate14( return false; } var valid84 = - _errs396 === errors; + _errs398 === errors; } else { var valid84 = true; } if (valid84) { if (data.meta !== undefined) { - let data147 = data.meta; - const _errs398 = errors; - if (errors === _errs398) { + let data148 = data.meta; + const _errs400 = errors; + if (errors === _errs400) { if ( - data147 && - typeof data147 == + data148 && + typeof data148 == 'object' && !Array.isArray( - data147 + data148 ) ) { - for (const key55 in data147) { - const _errs401 = + for (const key55 in data148) { + const _errs403 = errors; var valid86 = - _errs401 === + _errs403 === errors; if (!valid86) { break; @@ -16511,7 +16595,7 @@ function validate14( } } var valid84 = - _errs398 === errors; + _errs400 === errors; } else { var valid84 = true; } @@ -16520,15 +16604,15 @@ function validate14( data.userId !== undefined ) { - let data149 = + let data150 = data.userId; - const _errs402 = errors; + const _errs404 = errors; if ( !( - typeof data149 == + typeof data150 == 'number' && isFinite( - data149 + data150 ) ) ) { @@ -16552,7 +16636,7 @@ function validate14( return false; } var valid84 = - _errs402 === errors; + _errs404 === errors; } else { var valid84 = true; } @@ -16575,8 +16659,8 @@ function validate14( } } } else if (tag0 === 'writeFile') { - const _errs404 = errors; - if (errors === _errs404) { + const _errs406 = errors; + if (errors === _errs406) { if ( data && typeof data == 'object' && @@ -16607,7 +16691,7 @@ function validate14( ]; return false; } else { - const _errs406 = errors; + const _errs408 = errors; for (const key56 in data) { if ( !( @@ -16636,19 +16720,19 @@ function validate14( break; } } - if (_errs406 === errors) { + if (_errs408 === errors) { if (data.progress !== undefined) { - let data150 = data.progress; - const _errs407 = errors; - if (errors === _errs407) { + let data151 = data.progress; + const _errs409 = errors; + if (errors === _errs409) { if ( - data150 && - typeof data150 == + data151 && + typeof data151 == 'object' && - !Array.isArray(data150) + !Array.isArray(data151) ) { - const _errs409 = errors; - for (const key57 in data150) { + const _errs411 = errors; + for (const key57 in data151) { if ( !( key57 === @@ -16679,21 +16763,21 @@ function validate14( break; } } - if (_errs409 === errors) { + if (_errs411 === errors) { if ( - data150.weight !== + data151.weight !== undefined ) { - let data151 = - data150.weight; - const _errs410 = + let data152 = + data151.weight; + const _errs412 = errors; if ( !( - typeof data151 == + typeof data152 == 'number' && isFinite( - data151 + data152 ) ) ) { @@ -16717,20 +16801,20 @@ function validate14( return false; } var valid89 = - _errs410 === + _errs412 === errors; } else { var valid89 = true; } if (valid89) { if ( - data150.caption !== + data151.caption !== undefined ) { - const _errs412 = + const _errs414 = errors; if ( - typeof data150.caption !== + typeof data151.caption !== 'string' ) { validate14.errors = @@ -16753,7 +16837,7 @@ function validate14( return false; } var valid89 = - _errs412 === + _errs414 === errors; } else { var valid89 = true; @@ -16779,16 +16863,16 @@ function validate14( return false; } } - var valid88 = _errs407 === errors; + var valid88 = _errs409 === errors; } else { var valid88 = true; } if (valid88) { if (data.step !== undefined) { - let data153 = data.step; - const _errs414 = errors; + let data154 = data.step; + const _errs416 = errors; if ( - typeof data153 !== 'string' + typeof data154 !== 'string' ) { validate14.errors = [ { @@ -16807,7 +16891,7 @@ function validate14( ]; return false; } - if ('writeFile' !== data153) { + if ('writeFile' !== data154) { validate14.errors = [ { instancePath: @@ -16827,13 +16911,13 @@ function validate14( return false; } var valid88 = - _errs414 === errors; + _errs416 === errors; } else { var valid88 = true; } if (valid88) { if (data.path !== undefined) { - const _errs416 = errors; + const _errs418 = errors; if ( typeof data.path !== 'string' @@ -16856,7 +16940,7 @@ function validate14( return false; } var valid88 = - _errs416 === errors; + _errs418 === errors; } else { var valid88 = true; } @@ -16864,14 +16948,14 @@ function validate14( if ( data.data !== undefined ) { - let data155 = data.data; - const _errs418 = errors; - const _errs419 = errors; - let valid90 = false; + let data156 = data.data; const _errs420 = errors; + const _errs421 = errors; + let valid90 = false; + const _errs422 = errors; if ( !validate12( - data155, + data156, { instancePath: instancePath + @@ -16894,14 +16978,14 @@ function validate14( vErrors.length; } var _valid2 = - _errs420 === errors; + _errs422 === errors; valid90 = valid90 || _valid2; if (!valid90) { - const _errs421 = + const _errs423 = errors; if ( - typeof data155 !== + typeof data156 !== 'string' ) { const err2 = { @@ -16933,45 +17017,45 @@ function validate14( errors++; } var _valid2 = - _errs421 === + _errs423 === errors; valid90 = valid90 || _valid2; if (!valid90) { - const _errs423 = + const _errs425 = errors; if ( errors === - _errs423 + _errs425 ) { if ( - data155 && - typeof data155 == + data156 && + typeof data156 == 'object' && !Array.isArray( - data155 + data156 ) ) { let missing27; if ( - (data155.BYTES_PER_ELEMENT === + (data156.BYTES_PER_ELEMENT === undefined && (missing27 = 'BYTES_PER_ELEMENT')) || - (data155.buffer === + (data156.buffer === undefined && (missing27 = 'buffer')) || - (data155.byteLength === + (data156.byteLength === undefined && (missing27 = 'byteLength')) || - (data155.byteOffset === + (data156.byteOffset === undefined && (missing27 = 'byteOffset')) || - (data155.length === + (data156.length === undefined && (missing27 = 'length')) @@ -17009,9 +17093,9 @@ function validate14( } errors++; } else { - const _errs425 = + const _errs427 = errors; - for (const key58 in data155) { + for (const key58 in data156) { if ( !( key58 === @@ -17026,18 +17110,18 @@ function validate14( 'length' ) ) { - let data156 = - data155[ + let data157 = + data156[ key58 ]; - const _errs426 = + const _errs428 = errors; if ( !( - typeof data156 == + typeof data157 == 'number' && isFinite( - data156 + data157 ) ) ) { @@ -17081,7 +17165,7 @@ function validate14( errors++; } var valid91 = - _errs426 === + _errs428 === errors; if ( !valid91 @@ -17091,23 +17175,23 @@ function validate14( } } if ( - _errs425 === + _errs427 === errors ) { if ( - data155.BYTES_PER_ELEMENT !== + data156.BYTES_PER_ELEMENT !== undefined ) { - let data157 = - data155.BYTES_PER_ELEMENT; - const _errs428 = + let data158 = + data156.BYTES_PER_ELEMENT; + const _errs430 = errors; if ( !( - typeof data157 == + typeof data158 == 'number' && isFinite( - data157 + data158 ) ) ) { @@ -17142,7 +17226,7 @@ function validate14( errors++; } var valid92 = - _errs428 === + _errs430 === errors; } else { var valid92 = true; @@ -17151,28 +17235,28 @@ function validate14( valid92 ) { if ( - data155.buffer !== + data156.buffer !== undefined ) { - let data158 = - data155.buffer; - const _errs430 = + let data159 = + data156.buffer; + const _errs432 = errors; if ( errors === - _errs430 + _errs432 ) { if ( - data158 && - typeof data158 == + data159 && + typeof data159 == 'object' && !Array.isArray( - data158 + data159 ) ) { let missing28; if ( - data158.byteLength === + data159.byteLength === undefined && (missing28 = 'byteLength') @@ -17210,9 +17294,9 @@ function validate14( } errors++; } else { - const _errs432 = + const _errs434 = errors; - for (const key59 in data158) { + for (const key59 in data159) { if ( !( key59 === @@ -17253,21 +17337,21 @@ function validate14( } } if ( - _errs432 === + _errs434 === errors ) { if ( - data158.byteLength !== + data159.byteLength !== undefined ) { - let data159 = - data158.byteLength; + let data160 = + data159.byteLength; if ( !( - typeof data159 == + typeof data160 == 'number' && isFinite( - data159 + data160 ) ) ) { @@ -17337,7 +17421,7 @@ function validate14( } } var valid92 = - _errs430 === + _errs432 === errors; } else { var valid92 = true; @@ -17346,19 +17430,19 @@ function validate14( valid92 ) { if ( - data155.byteLength !== + data156.byteLength !== undefined ) { - let data160 = - data155.byteLength; - const _errs435 = + let data161 = + data156.byteLength; + const _errs437 = errors; if ( !( - typeof data160 == + typeof data161 == 'number' && isFinite( - data160 + data161 ) ) ) { @@ -17393,7 +17477,7 @@ function validate14( errors++; } var valid92 = - _errs435 === + _errs437 === errors; } else { var valid92 = true; @@ -17402,19 +17486,19 @@ function validate14( valid92 ) { if ( - data155.byteOffset !== + data156.byteOffset !== undefined ) { - let data161 = - data155.byteOffset; - const _errs437 = + let data162 = + data156.byteOffset; + const _errs439 = errors; if ( !( - typeof data161 == + typeof data162 == 'number' && isFinite( - data161 + data162 ) ) ) { @@ -17449,7 +17533,7 @@ function validate14( errors++; } var valid92 = - _errs437 === + _errs439 === errors; } else { var valid92 = true; @@ -17458,19 +17542,19 @@ function validate14( valid92 ) { if ( - data155.length !== + data156.length !== undefined ) { - let data162 = - data155.length; - const _errs439 = + let data163 = + data156.length; + const _errs441 = errors; if ( !( - typeof data162 == + typeof data163 == 'number' && isFinite( - data162 + data163 ) ) ) { @@ -17505,7 +17589,7 @@ function validate14( errors++; } var valid92 = - _errs439 === + _errs441 === errors; } else { var valid92 = true; @@ -17549,7 +17633,7 @@ function validate14( } } var _valid2 = - _errs423 === + _errs425 === errors; valid90 = valid90 || @@ -17585,13 +17669,13 @@ function validate14( vErrors; return false; } else { - errors = _errs419; + errors = _errs421; if ( vErrors !== null ) { - if (_errs419) { + if (_errs421) { vErrors.length = - _errs419; + _errs421; } else { vErrors = null; @@ -17599,7 +17683,7 @@ function validate14( } } var valid88 = - _errs418 === errors; + _errs420 === errors; } else { var valid88 = true; } @@ -17622,8 +17706,8 @@ function validate14( } } } else if (tag0 === 'writeFiles') { - const _errs441 = errors; - if (errors === _errs441) { + const _errs443 = errors; + if (errors === _errs443) { if ( data && typeof data == 'object' && @@ -17654,7 +17738,7 @@ function validate14( ]; return false; } else { - const _errs443 = errors; + const _errs445 = errors; for (const key60 in data) { if ( !( @@ -17683,19 +17767,19 @@ function validate14( break; } } - if (_errs443 === errors) { + if (_errs445 === errors) { if (data.progress !== undefined) { - let data163 = data.progress; - const _errs444 = errors; - if (errors === _errs444) { + let data164 = data.progress; + const _errs446 = errors; + if (errors === _errs446) { if ( - data163 && - typeof data163 == + data164 && + typeof data164 == 'object' && - !Array.isArray(data163) + !Array.isArray(data164) ) { - const _errs446 = errors; - for (const key61 in data163) { + const _errs448 = errors; + for (const key61 in data164) { if ( !( key61 === @@ -17726,21 +17810,21 @@ function validate14( break; } } - if (_errs446 === errors) { + if (_errs448 === errors) { if ( - data163.weight !== + data164.weight !== undefined ) { - let data164 = - data163.weight; - const _errs447 = + let data165 = + data164.weight; + const _errs449 = errors; if ( !( - typeof data164 == + typeof data165 == 'number' && isFinite( - data164 + data165 ) ) ) { @@ -17764,20 +17848,20 @@ function validate14( return false; } var valid96 = - _errs447 === + _errs449 === errors; } else { var valid96 = true; } if (valid96) { if ( - data163.caption !== + data164.caption !== undefined ) { - const _errs449 = + const _errs451 = errors; if ( - typeof data163.caption !== + typeof data164.caption !== 'string' ) { validate14.errors = @@ -17800,7 +17884,7 @@ function validate14( return false; } var valid96 = - _errs449 === + _errs451 === errors; } else { var valid96 = true; @@ -17826,16 +17910,16 @@ function validate14( return false; } } - var valid95 = _errs444 === errors; + var valid95 = _errs446 === errors; } else { var valid95 = true; } if (valid95) { if (data.step !== undefined) { - let data166 = data.step; - const _errs451 = errors; + let data167 = data.step; + const _errs453 = errors; if ( - typeof data166 !== 'string' + typeof data167 !== 'string' ) { validate14.errors = [ { @@ -17854,7 +17938,7 @@ function validate14( ]; return false; } - if ('writeFiles' !== data166) { + if ('writeFiles' !== data167) { validate14.errors = [ { instancePath: @@ -17874,7 +17958,7 @@ function validate14( return false; } var valid95 = - _errs451 === errors; + _errs453 === errors; } else { var valid95 = true; } @@ -17883,7 +17967,7 @@ function validate14( data.writeToPath !== undefined ) { - const _errs453 = errors; + const _errs455 = errors; if ( typeof data.writeToPath !== 'string' @@ -17906,7 +17990,7 @@ function validate14( return false; } var valid95 = - _errs453 === errors; + _errs455 === errors; } else { var valid95 = true; } @@ -17915,7 +17999,7 @@ function validate14( data.filesTree !== undefined ) { - const _errs455 = errors; + const _errs457 = errors; if ( !validate18( data.filesTree, @@ -17941,7 +18025,7 @@ function validate14( vErrors.length; } var valid95 = - _errs455 === errors; + _errs457 === errors; } else { var valid95 = true; } @@ -17964,8 +18048,8 @@ function validate14( } } } else if (tag0 === 'wp-cli') { - const _errs456 = errors; - if (errors === _errs456) { + const _errs458 = errors; + if (errors === _errs458) { if ( data && typeof data == 'object' && @@ -17994,7 +18078,7 @@ function validate14( ]; return false; } else { - const _errs458 = errors; + const _errs460 = errors; for (const key62 in data) { if ( !( @@ -18023,19 +18107,19 @@ function validate14( break; } } - if (_errs458 === errors) { + if (_errs460 === errors) { if (data.progress !== undefined) { - let data169 = data.progress; - const _errs459 = errors; - if (errors === _errs459) { + let data170 = data.progress; + const _errs461 = errors; + if (errors === _errs461) { if ( - data169 && - typeof data169 == + data170 && + typeof data170 == 'object' && - !Array.isArray(data169) + !Array.isArray(data170) ) { - const _errs461 = errors; - for (const key63 in data169) { + const _errs463 = errors; + for (const key63 in data170) { if ( !( key63 === @@ -18066,21 +18150,21 @@ function validate14( break; } } - if (_errs461 === errors) { + if (_errs463 === errors) { if ( - data169.weight !== + data170.weight !== undefined ) { - let data170 = - data169.weight; - const _errs462 = + let data171 = + data170.weight; + const _errs464 = errors; if ( !( - typeof data170 == + typeof data171 == 'number' && isFinite( - data170 + data171 ) ) ) { @@ -18104,20 +18188,20 @@ function validate14( return false; } var valid99 = - _errs462 === + _errs464 === errors; } else { var valid99 = true; } if (valid99) { if ( - data169.caption !== + data170.caption !== undefined ) { - const _errs464 = + const _errs466 = errors; if ( - typeof data169.caption !== + typeof data170.caption !== 'string' ) { validate14.errors = @@ -18140,7 +18224,7 @@ function validate14( return false; } var valid99 = - _errs464 === + _errs466 === errors; } else { var valid99 = true; @@ -18166,16 +18250,16 @@ function validate14( return false; } } - var valid98 = _errs459 === errors; + var valid98 = _errs461 === errors; } else { var valid98 = true; } if (valid98) { if (data.step !== undefined) { - let data172 = data.step; - const _errs466 = errors; + let data173 = data.step; + const _errs468 = errors; if ( - typeof data172 !== 'string' + typeof data173 !== 'string' ) { validate14.errors = [ { @@ -18194,7 +18278,7 @@ function validate14( ]; return false; } - if ('wp-cli' !== data172) { + if ('wp-cli' !== data173) { validate14.errors = [ { instancePath: @@ -18214,7 +18298,7 @@ function validate14( return false; } var valid98 = - _errs466 === errors; + _errs468 === errors; } else { var valid98 = true; } @@ -18222,13 +18306,13 @@ function validate14( if ( data.command !== undefined ) { - let data173 = data.command; - const _errs468 = errors; - const _errs469 = errors; - let valid100 = false; + let data174 = data.command; const _errs470 = errors; + const _errs471 = errors; + let valid100 = false; + const _errs472 = errors; if ( - typeof data173 !== + typeof data174 !== 'string' ) { const err15 = { @@ -18252,31 +18336,31 @@ function validate14( errors++; } var _valid3 = - _errs470 === errors; + _errs472 === errors; valid100 = valid100 || _valid3; if (!valid100) { - const _errs472 = errors; + const _errs474 = errors; if ( - errors === _errs472 + errors === _errs474 ) { if ( Array.isArray( - data173 + data174 ) ) { var valid101 = true; const len0 = - data173.length; + data174.length; for ( let i0 = 0; i0 < len0; i0++ ) { - const _errs474 = + const _errs476 = errors; if ( - typeof data173[ + typeof data174[ i0 ] !== 'string' @@ -18313,7 +18397,7 @@ function validate14( errors++; } var valid101 = - _errs474 === + _errs476 === errors; if ( !valid101 @@ -18352,7 +18436,7 @@ function validate14( } } var _valid3 = - _errs472 === errors; + _errs474 === errors; valid100 = valid100 || _valid3; } @@ -18378,18 +18462,18 @@ function validate14( vErrors; return false; } else { - errors = _errs469; + errors = _errs471; if (vErrors !== null) { - if (_errs469) { + if (_errs471) { vErrors.length = - _errs469; + _errs471; } else { vErrors = null; } } } var valid98 = - _errs468 === errors; + _errs470 === errors; } else { var valid98 = true; } @@ -18398,7 +18482,7 @@ function validate14( data.wpCliPath !== undefined ) { - const _errs476 = errors; + const _errs478 = errors; if ( typeof data.wpCliPath !== 'string' @@ -18423,7 +18507,7 @@ function validate14( return false; } var valid98 = - _errs476 === errors; + _errs478 === errors; } else { var valid98 = true; } @@ -18446,8 +18530,8 @@ function validate14( } } } else if (tag0 === 'setSiteLanguage') { - const _errs478 = errors; - if (errors === _errs478) { + const _errs480 = errors; + if (errors === _errs480) { if ( data && typeof data == 'object' && @@ -18476,7 +18560,7 @@ function validate14( ]; return false; } else { - const _errs480 = errors; + const _errs482 = errors; for (const key64 in data) { if ( !( @@ -18504,19 +18588,19 @@ function validate14( break; } } - if (_errs480 === errors) { + if (_errs482 === errors) { if (data.progress !== undefined) { - let data176 = data.progress; - const _errs481 = errors; - if (errors === _errs481) { + let data177 = data.progress; + const _errs483 = errors; + if (errors === _errs483) { if ( - data176 && - typeof data176 == + data177 && + typeof data177 == 'object' && - !Array.isArray(data176) + !Array.isArray(data177) ) { - const _errs483 = errors; - for (const key65 in data176) { + const _errs485 = errors; + for (const key65 in data177) { if ( !( key65 === @@ -18547,21 +18631,21 @@ function validate14( break; } } - if (_errs483 === errors) { + if (_errs485 === errors) { if ( - data176.weight !== + data177.weight !== undefined ) { - let data177 = - data176.weight; - const _errs484 = + let data178 = + data177.weight; + const _errs486 = errors; if ( !( - typeof data177 == + typeof data178 == 'number' && isFinite( - data177 + data178 ) ) ) { @@ -18585,20 +18669,20 @@ function validate14( return false; } var valid104 = - _errs484 === + _errs486 === errors; } else { var valid104 = true; } if (valid104) { if ( - data176.caption !== + data177.caption !== undefined ) { - const _errs486 = + const _errs488 = errors; if ( - typeof data176.caption !== + typeof data177.caption !== 'string' ) { validate14.errors = @@ -18621,7 +18705,7 @@ function validate14( return false; } var valid104 = - _errs486 === + _errs488 === errors; } else { var valid104 = true; @@ -18647,16 +18731,16 @@ function validate14( return false; } } - var valid103 = _errs481 === errors; + var valid103 = _errs483 === errors; } else { var valid103 = true; } if (valid103) { if (data.step !== undefined) { - let data179 = data.step; - const _errs488 = errors; + let data180 = data.step; + const _errs490 = errors; if ( - typeof data179 !== 'string' + typeof data180 !== 'string' ) { validate14.errors = [ { @@ -18677,7 +18761,7 @@ function validate14( } if ( 'setSiteLanguage' !== - data179 + data180 ) { validate14.errors = [ { @@ -18698,7 +18782,7 @@ function validate14( return false; } var valid103 = - _errs488 === errors; + _errs490 === errors; } else { var valid103 = true; } @@ -18706,7 +18790,7 @@ function validate14( if ( data.language !== undefined ) { - const _errs490 = errors; + const _errs492 = errors; if ( typeof data.language !== 'string' @@ -18729,7 +18813,7 @@ function validate14( return false; } var valid103 = - _errs490 === errors; + _errs492 === errors; } else { var valid103 = true; } diff --git a/packages/playground/blueprints/public/blueprint-schema.json b/packages/playground/blueprints/public/blueprint-schema.json index 0a8f058dca..912a376071 100644 --- a/packages/playground/blueprints/public/blueprint-schema.json +++ b/packages/playground/blueprints/public/blueprint-schema.json @@ -549,6 +549,12 @@ "file": { "$ref": "#/definitions/FileReference", "description": "The file to import" + }, + "importer": { + "type": "string", + "enum": ["data-liberation", "default"], + "description": "The importer to use. Possible values:\n\n- `default`: The importer from https://github.com/humanmade/WordPress-Importer\n- `data-liberation`: The experimental Data Liberation WXR importer developed at https://github.com/WordPress/wordpress-playground/issues/1894\n\nThis option is deprecated. The syntax will not be removed, but once the Data Liberation importer matures, it will become the only supported importer and the `importer` option will be ignored.", + "deprecated": true } }, "required": ["file", "step"] diff --git a/packages/playground/blueprints/src/lib/compile.ts b/packages/playground/blueprints/src/lib/compile.ts index 30c31a9cbd..932522e448 100644 --- a/packages/playground/blueprints/src/lib/compile.ts +++ b/packages/playground/blueprints/src/lib/compile.ts @@ -7,10 +7,13 @@ import { UniversalPHP, } from '@php-wasm/universal'; import { FileReference, isResourceReference, Resource } from './resources'; -import { Step, StepDefinition, WriteFileStep } from './steps'; +import { ImportWxrStep, Step, StepDefinition, WriteFileStep } from './steps'; import * as allStepHandlers from './steps/handlers'; import { Blueprint, ExtraLibrary } from './blueprint'; import { logger } from '@php-wasm/logger'; +/* @ts-ignore */ +// eslint-disable-next-line +import dataLiberationCoreUrl from '../../../data-liberation/dist/data-liberation-core.phar.gz?url'; // @TODO: Configure this in the `wp-cli` step, not here. const { wpCLI, ...otherStepHandlers } = allStepHandlers; @@ -228,14 +231,29 @@ export function compileBlueprint( (step) => typeof step === 'object' && step?.step === 'importWxr' ); if (importWxrStepIndex !== undefined && importWxrStepIndex > -1) { - blueprint.steps?.splice(importWxrStepIndex, 0, { - step: 'installPlugin', - pluginData: { - resource: 'url', - url: 'https://playground.wordpress.net/wordpress-importer.zip', - caption: 'Downloading the WordPress Importer plugin', - }, - }); + const importWxrStep = blueprint.steps![ + importWxrStepIndex + ]! as ImportWxrStep; + if (importWxrStep.importer === 'data-liberation') { + blueprint.steps?.splice(importWxrStepIndex, 0, { + step: 'writeFile', + path: '/internal/shared/data-liberation-core.phar', + data: { + resource: 'url', + url: dataLiberationCoreUrl, + caption: 'Downloading the Data Liberation WXR importer', + }, + }); + } else { + blueprint.steps?.splice(importWxrStepIndex, 0, { + step: 'installPlugin', + pluginData: { + resource: 'url', + url: 'https://playground.wordpress.net/wordpress-importer.zip', + caption: 'Downloading the WordPress Importer plugin', + }, + }); + } } const { valid, errors } = validateBlueprint(blueprint); diff --git a/packages/playground/blueprints/src/lib/resources.ts b/packages/playground/blueprints/src/lib/resources.ts index e9524aa21a..377dc7127f 100644 --- a/packages/playground/blueprints/src/lib/resources.ts +++ b/packages/playground/blueprints/src/lib/resources.ts @@ -459,7 +459,7 @@ export class GitDirectoryResource extends Resource { async resolve() { const repoUrl = this.options?.corsProxy - ? `${this.options.corsProxy}?${this.reference.url}` + ? `${this.options.corsProxy}${this.reference.url}` : this.reference.url; const ref = ['', 'HEAD'].includes(this.reference.ref) ? 'HEAD' diff --git a/packages/playground/blueprints/src/lib/steps/import-wxr.ts b/packages/playground/blueprints/src/lib/steps/import-wxr.ts index 1169d133d3..dd41de58ed 100644 --- a/packages/playground/blueprints/src/lib/steps/import-wxr.ts +++ b/packages/playground/blueprints/src/lib/steps/import-wxr.ts @@ -1,6 +1,7 @@ -import { StepHandler } from '.'; +import { StepHandler, StepProgress } from '.'; import { writeFile } from './write-file'; import { phpVar } from '@php-wasm/util'; +import { UniversalPHP } from '@php-wasm/universal'; /** * @inheritDoc importWxr @@ -20,6 +21,20 @@ export interface ImportWxrStep { step: 'importWxr'; /** The file to import */ file: ResourceType; + /** + * The importer to use. Possible values: + * + * - `default`: The importer from https://github.com/humanmade/WordPress-Importer + * - `data-liberation`: The experimental Data Liberation WXR importer developed at + * https://github.com/WordPress/wordpress-playground/issues/1894 + * + * This option is deprecated. The syntax will not be removed, but once the + * Data Liberation importer matures, it will become the only supported + * importer and the `importer` option will be ignored. + * + * @deprecated + */ + importer?: 'data-liberation' | 'default'; } /** @@ -30,9 +45,21 @@ export interface ImportWxrStep { */ export const importWxr: StepHandler> = async ( playground, - { file }, + { file, importer = 'default' }, progress? ) => { + if (importer === 'data-liberation') { + await importWithDataLiberationImporter(playground, file, progress); + } else { + await importWithDefaultImporter(playground, file, progress); + } +}; + +async function importWithDefaultImporter( + playground: UniversalPHP, + file: File, + progress?: StepProgress | undefined +) { progress?.tracker?.setCaption('Importing content'); await writeFile(playground, { path: '/tmp/import.wxr', @@ -41,44 +68,172 @@ export const importWxr: StepHandler> = async ( const docroot = await playground.documentRoot; await playground.run({ code: ` 'Administrator') )[0]->ID; + wp_set_current_user( $admin_id ); + $importer = new WXR_Importer( array( + 'fetch_attachments' => true, + 'default_author' => $admin_id + ) ); + $logger = new WP_Importer_Logger_CLI(); + $importer->set_logger( $logger ); + // Slashes from the imported content are lost if we don't call wp_slash here. + add_action( 'wp_insert_post_data', function( $data ) { + return wp_slash($data); + }); - kses_remove_filters(); - $admin_id = get_users(array('role' => 'Administrator') )[0]->ID; - wp_set_current_user( $admin_id ); - $importer = new WXR_Importer( array( - 'fetch_attachments' => true, - 'default_author' => $admin_id - ) ); - $logger = new WP_Importer_Logger_CLI(); - $importer->set_logger( $logger ); + // Ensure that Site Editor templates are associated with the correct taxonomy. + add_filter( 'wp_import_post_terms', function ( $terms, $post_id ) { + foreach ( $terms as $post_term ) { + if ( 'wp_theme' !== $term['taxonomy'] ) continue; + $post_term = get_term_by('slug', $term['slug'], $term['taxonomy'] ); + if ( ! $post_term ) { + $post_term = wp_insert_term( + $term['slug'], + $term['taxonomy'] + ); + $term_id = $post_term['term_id']; + } else { + $term_id = $post_term->term_id; + } + wp_set_object_terms( $post_id, $term_id, $term['taxonomy']) ; + } + return $terms; + }, 10, 2 ); + $result = $importer->import( '/tmp/import.wxr' ); + `, + }); +} - // Slashes from the imported content are lost if we don't call wp_slash here. - add_action( 'wp_insert_post_data', function( $data ) { - return wp_slash($data); - }); +async function importWithDataLiberationImporter( + playground: UniversalPHP, + file: File, + progress?: StepProgress | undefined +) { + progress?.tracker?.setCaption('Preparing content import'); + await writeFile(playground, { + path: '/tmp/import.wxr', + data: file, + }); + const docroot = await playground.documentRoot; + /** + * Surface the import progress information in the Blueprint progress bar. + * This temporary message handler is cleared at the end of this step. + */ + const clearProgressListener = await playground.onMessage( + (messageString) => { + const message = JSON.parse(messageString) as any; + if (message?.type === 'import-wxr-progress') { + progress?.tracker?.setCaption(message.progress); + } + } + ); + try { + await playground.run({ + code: ` 'Administrator') )[0]->ID; + wp_set_current_user( $admin_id ); + + $new_site_url = get_site_url(); + $importer = WP_Stream_Importer::create_for_wxr_file( + '/tmp/import.wxr', + array( + 'new_site_url' => $new_site_url, + ) + ); + $session = WP_Import_Session::create( + array( + 'data_source' => 'wxr_file', + 'file_name' => '/tmp/import.wxr', + ) + ); + while ( true ) { + if ( true === $importer->next_step() ) { + /** + * We're ignoring any importing errors. + * This script is a part of Blueprints and is expected to finish + * without stopping. We won't be gathering additional user input + * along the way. Instead, we'll just decide not to ignore the + * errors. + * + * @TODO: Consider extracting this code into a CLI script and + * using it here instead of this custom script. Note it's + * about a simple CLI script, not a WP-CLI command, as the + * latter would require downloading 5MB of WP-CLI code. + */ + switch ( $importer->get_stage() ) { + case WP_Stream_Importer::STAGE_INITIAL: + $message = 'Preparing content import'; + break; + + case WP_Stream_Importer::STAGE_INDEX_ENTITIES: + // Bump the total number of entities to import. + $indexed = $session->count_all_total_entities(); + $message = 'Content import 1/4: Indexing records (' . $indexed . ' so far)'; + $session->create_frontloading_placeholders( $importer->get_indexed_assets_urls() ); + $session->bump_total_number_of_entities( + $importer->get_indexed_entities_counts() + ); + break; + + case WP_Stream_Importer::STAGE_TOPOLOGICAL_SORT: + $message = 'Content import 2/4: Indexing data'; + break; + + case WP_Stream_Importer::STAGE_FRONTLOAD_ASSETS: + $session->bump_frontloading_progress( + $importer->get_frontloading_progress(), + $importer->get_frontloading_events() + ); + $nb_media = $session->count_awaiting_frontloading_placeholders(); + $message = 'Content import 3/4: Downloading media (' . $nb_media . ' remaining)'; + break; + + case WP_Stream_Importer::STAGE_IMPORT_ENTITIES: + $session->bump_imported_entities_counts( + $importer->get_imported_entities_counts() ); - $term_id = $post_term['term_id']; - } else { - $term_id = $post_term->term_id; - } - wp_set_object_terms( $post_id, $term_id, $term['taxonomy']) ; + $nb_remaining_entities = $session->count_remaining_entities(); + $message = 'Content import 4/4: Inserting data (' . $nb_remaining_entities . ' remaining)'; + break; + + default: + $message = 'Importing content'; + break; } - return $terms; - }, 10, 2 ); - $result = $importer->import( '/tmp/import.wxr' ); - `, - }); -}; + // Report progress to the UI + post_message_to_js(json_encode([ + 'type' => 'import-wxr-progress', + 'progress' => $message, + ])); + continue; + } + if ( $importer->advance_to_next_stage() ) { + continue; + } + // Import finished + break; + } + `, + }); + } finally { + await clearProgressListener(); + } +} diff --git a/packages/playground/client/src/index.ts b/packages/playground/client/src/index.ts index a23fd279a9..8741bb7db9 100644 --- a/packages/playground/client/src/index.ts +++ b/packages/playground/client/src/index.ts @@ -152,6 +152,7 @@ export async function startPlaygroundWeb({ phpVersion: compiled.versions.php, wpVersion: compiled.versions.wp, withNetworking: compiled.features.networking, + corsProxyUrl: corsProxy, }); await playground.isReady(); downloadPHPandWP.finish(); diff --git a/packages/playground/common/src/index.ts b/packages/playground/common/src/index.ts index 3c9b6c4d95..6a504e8b10 100644 --- a/packages/playground/common/src/index.ts +++ b/packages/playground/common/src/index.ts @@ -62,7 +62,7 @@ export const unzipFile = async ( $zip->close(); chmod($extractTo, 0777); } else { - throw new Exception("Could not unzip file"); + throw new Exception("Could not unzip file: " . $zip->getStatusString()); } } unzip(${js.zipPath}, ${js.extractToPath}, ${js.overwriteFiles}); diff --git a/packages/playground/data-liberation/README.md b/packages/playground/data-liberation/README.md index a6216429aa..9b4651a69f 100644 --- a/packages/playground/data-liberation/README.md +++ b/packages/playground/data-liberation/README.md @@ -4,6 +4,26 @@ This project aims to help the Data Liberation project and unlock powerful new use cases for WordPress. See [the rationale](RATIONALE.md) and [the plan](PLAN.md) for more details. -### Getting started +### Development -No instructions yet. TBD. +Every time you change the core libraries, you need to run one of the build +commands below before it will have effect in Playground. If you have an idea +how to automate this, please propose a PR! + +### Building + +#### Core libraries (data-liberation-core.phar) + +The `nx build:phar playground-data-liberation` command builds the standalone +dist/core-data-liberation.phar.gz file meant for use in the importWxr Blueprint +step. It is built with Box and brings in all the classes required to import the +WXR files, including WP_Stream_Importer, AsyncHttp\Client, and WP_XML_Processor. + +The phar is optimized for size and doesn't include the markdown importer and its +dependencies. + +#### Full plugin (data-liberation-plugin.zip) + +The `nx build:plugin playground-data-liberation` command builds the +dist/data-liberation-plugin.zip file, which contains all the plugin libraries, the +wp-admin page, JavaScript files, etc. diff --git a/packages/playground/data-liberation/bin/build/DataLiberationBoxCompactor.php b/packages/playground/data-liberation/bin/build/DataLiberationBoxCompactor.php new file mode 100644 index 0000000000..836504b640 --- /dev/null +++ b/packages/playground/data-liberation/bin/build/DataLiberationBoxCompactor.php @@ -0,0 +1,32 @@ + __DIR__ . '/uploads', + 'new_site_url' => 'https://smoke-test.org' +]); + +WP_URL::parse('https://example.com'); + +echo 'Stream importer created!'; + diff --git a/packages/playground/data-liberation/bin/build/truncate-composer-checks.php b/packages/playground/data-liberation/bin/build/truncate-composer-checks.php new file mode 100644 index 0000000000..1e48a8a86c --- /dev/null +++ b/packages/playground/data-liberation/bin/build/truncate-composer-checks.php @@ -0,0 +1,15 @@ +startBuffering(); +$phar['vendor/composer/platform_check.php'] = ''; // Set to empty string to truncate +$phar->stopBuffering(); + diff --git a/packages/playground/data-liberation/blueprints-library b/packages/playground/data-liberation/blueprints-library index 32b937d775..c7ed61ba6b 160000 --- a/packages/playground/data-liberation/blueprints-library +++ b/packages/playground/data-liberation/blueprints-library @@ -1 +1 @@ -Subproject commit 32b937d775b3df72997393b81efa068370ec81ca +Subproject commit c7ed61ba6bcd4be8defa1614d5cf479c98594656 diff --git a/packages/playground/data-liberation/bootstrap.php b/packages/playground/data-liberation/bootstrap.php index 8e4e5f0177..8b5910c217 100644 --- a/packages/playground/data-liberation/bootstrap.php +++ b/packages/playground/data-liberation/bootstrap.php @@ -17,7 +17,10 @@ require_once __DIR__ . '/src/byte-readers/WP_Remote_File_Reader.php'; require_once __DIR__ . '/src/byte-readers/WP_Remote_File_Ranged_Reader.php'; -if ( ! class_exists( 'WP_HTML_Tag_Processor' ) ) { +if ( + ! class_exists( 'WP_HTML_Tag_Processor' ) && + file_exists( __DIR__ . '/src/wordpress-core-html-api/class-wp-html-token.php' ) +) { require_once __DIR__ . '/src/wordpress-core-html-api/class-wp-html-token.php'; require_once __DIR__ . '/src/wordpress-core-html-api/class-wp-html-span.php'; require_once __DIR__ . '/src/wordpress-core-html-api/class-wp-html-text-replacement.php'; @@ -34,7 +37,10 @@ require_once __DIR__ . '/src/wordpress-core-html-api/class-wp-html-unsupported-exception.php'; require_once __DIR__ . '/src/wordpress-core-html-api/class-wp-html-processor.php'; } -if ( ! isset( $html5_named_character_references ) ) { +if ( + ! isset( $html5_named_character_references ) && + file_exists( __DIR__ . '/src/wordpress-core-html-api/html5-named-character-references.php' ) +) { require_once __DIR__ . '/src/wordpress-core-html-api/html5-named-character-references.php'; } @@ -55,15 +61,38 @@ require_once __DIR__ . '/src/import/WP_Attachment_Downloader_Event.php'; require_once __DIR__ . '/src/import/WP_Import_Session.php'; require_once __DIR__ . '/src/import/WP_Stream_Importer.php'; -require_once __DIR__ . '/src/import/WP_Markdown_Importer.php'; require_once __DIR__ . '/src/import/WP_Entity_Iterator_Chain.php'; require_once __DIR__ . '/src/import/WP_Retry_Frontloading_Iterator.php'; +require_once __DIR__ . '/src/import/WP_Markdown_Importer.php'; require_once __DIR__ . '/src/utf8_decoder.php'; -require_once __DIR__ . '/src/markdown-api/WP_Markdown_To_Blocks.php'; -require_once __DIR__ . '/src/markdown-api/WP_Markdown_Directory_Tree_Reader.php'; -require_once __DIR__ . '/src/markdown-api/WP_Markdown_HTML_Processor.php'; +/** + * Require conditionally – these files are missing from the data-liberation-core.phar + * to reduce the bundle size (we'd need to include a large markdown parser and its + * dependencies, too). + * + * @TODO: Build a separate "data-liberation-markdown" phar file plugin with the Markdown + * importing functionality. + */ +if ( file_exists( __DIR__ . '/src/markdown-api/WP_Markdown_To_Blocks.php' ) ) { + require_once __DIR__ . '/src/markdown-api/WP_Markdown_To_Blocks.php'; + require_once __DIR__ . '/src/markdown-api/WP_Markdown_Directory_Tree_Reader.php'; + require_once __DIR__ . '/src/markdown-api/WP_Markdown_HTML_Processor.php'; +} + +// When running in Playground, the composer autoloader script sees CLI SAPI and +// tries to use the STDERR, STDIN, and STDOUT constants. +// @TODO: Don't use the "cli" SAPI string and don't allow composer to run platform checks. +if ( ! defined( 'STDERR' ) ) { + define( 'STDERR', fopen( 'php://stderr', 'w' ) ); +} +if ( ! defined( 'STDIN' ) ) { + define( 'STDIN', fopen( 'php://stdin', 'r' ) ); +} +if ( ! defined( 'STDOUT' ) ) { + define( 'STDOUT', fopen( 'php://stdout', 'w' ) ); +} require_once __DIR__ . '/vendor/autoload.php'; // Polyfill WordPress core functions diff --git a/packages/playground/data-liberation/composer.json b/packages/playground/data-liberation/composer.json index 2b1445cc72..071ddd4a89 100644 --- a/packages/playground/data-liberation/composer.json +++ b/packages/playground/data-liberation/composer.json @@ -12,7 +12,8 @@ "yoast/phpunit-polyfills": "2.0.0", "squizlabs/php_codesniffer": "3.*", "wp-coding-standards/wpcs": "3.1.0", - "phpcompatibility/php-compatibility": "*" + "phpcompatibility/php-compatibility": "*", + "rector/rector": "^1.2" }, "config": { "optimize-autoloader": true, @@ -23,7 +24,8 @@ }, "autoload": { "classmap": [ - "src/" + "src/", + "vendor-patched/" ], "psr-4": { "WordPress\\DataLiberation\\": "src/WordPress" diff --git a/packages/playground/data-liberation/composer.lock b/packages/playground/data-liberation/composer.lock index 0e8f2c4eaf..f402995401 100644 --- a/packages/playground/data-liberation/composer.lock +++ b/packages/playground/data-liberation/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "df494240bd18495f76468d51d96729dd", + "content-hash": "ed6b4c737972083fd2fc9339f6604490", "packages": [ { "name": "brick/math", @@ -1752,6 +1752,64 @@ ], "time": "2024-05-20T13:34:27+00:00" }, + { + "name": "phpstan/phpstan", + "version": "1.12.12", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", + "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-11-28T22:13:23+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "10.1.16", @@ -2174,6 +2232,65 @@ ], "time": "2024-10-08T15:36:51+00:00" }, + { + "name": "rector/rector", + "version": "1.2.10", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "40f9cf38c05296bd32f444121336a521a293fa61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61", + "reference": "40f9cf38c05296bd32f444121336a521a293fa61", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0", + "phpstan/phpstan": "^1.12.5" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/1.2.10" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2024-11-08T13:59:10+00:00" + }, { "name": "sebastian/cli-parser", "version": "2.0.1", diff --git a/packages/playground/data-liberation/dist/.gitkeep b/packages/playground/data-liberation/dist/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/playground/data-liberation/dist/data-liberation-core.phar.gz b/packages/playground/data-liberation/dist/data-liberation-core.phar.gz new file mode 100755 index 0000000000..1aa65bf232 Binary files /dev/null and b/packages/playground/data-liberation/dist/data-liberation-core.phar.gz differ diff --git a/packages/playground/data-liberation/phar-core-box.json b/packages/playground/data-liberation/phar-core-box.json new file mode 100644 index 0000000000..6622473e8e --- /dev/null +++ b/packages/playground/data-liberation/phar-core-box.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://raw.githubusercontent.com/box-project/box/refs/heads/main/res/schema.json", + "main": "bootstrap.php", + "output": "dist/data-liberation-core.phar", + "force-autodiscovery": false, + "compactors": [ + "KevinGH\\Box\\Compactor\\Php", + "DataLiberationBoxCompactor" + ], + "annotations": false, + "files": ["src/functions.php", "src/utf8_decoder.php"], + "directories": [ + "src/block-markup", + "src/byte-readers", + "src/wxr", + "src/wordpress-core-html-api", + "src/xml-api", + "src/import", + "blueprints-library/src/WordPress/AsyncHttp", + "blueprints-library/src/WordPress/Streams", + "blueprints-library/src/WordPress/Util", + "vendor/brick", + "vendor/composer", + "vendor-patched/psr", + "vendor-patched/rowbot", + "vendor-patched/symfony/polyfill-intl-normalizer" + ] +} diff --git a/packages/playground/data-liberation/phar-core-build.sh b/packages/playground/data-liberation/phar-core-build.sh new file mode 100644 index 0000000000..ecfa386b82 --- /dev/null +++ b/packages/playground/data-liberation/phar-core-build.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Builds the standalone dist/core-data-liberation.phar.gz file meant for +# use in the importWxr Blueprint step. +# +# This is a temporary measure until we have a canonical way of distributing, +# versioning, and using the Data Liberation modules and their dependencies. +# Possible solutions might include composer packages, WordPress plugins, or +# tree-shaken zip files with each module and its composer deps. + +set -e +echo "Building data liberation plugin" +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +DATA_LIBERATION_DIR=$SCRIPT_DIR +BUILD_DIR=$DATA_LIBERATION_DIR/bin/build +DIST_DIR=$DATA_LIBERATION_DIR/dist + +rm $DIST_DIR/data-liberation-core.* > /dev/null 2>&1 || true +export BOX_BASE_PATH=$(type -a box | grep -v 'alias' | awk '{print $3}') +php $BUILD_DIR/box.php compile -d $DATA_LIBERATION_DIR -c $DATA_LIBERATION_DIR/phar-core-box.json +php -d 'phar.readonly=0' $BUILD_DIR/truncate-composer-checks.php $DIST_DIR/data-liberation-core.phar +cd $DIST_DIR +php $BUILD_DIR/smoke-test.php +PHP=7.2 bun $DATA_LIBERATION_DIR/../../php-wasm/cli/src/main.ts $BUILD_DIR/smoke-test.php +gzip data-liberation-core.phar +ls -sgh $DIST_DIR diff --git a/packages/playground/data-liberation/phpcs.xml b/packages/playground/data-liberation/phpcs.xml index b1522bc358..8e33364a13 100644 --- a/packages/playground/data-liberation/phpcs.xml +++ b/packages/playground/data-liberation/phpcs.xml @@ -2,6 +2,7 @@ PHP 7.0 compatibility. src/wxr/WXR_Importer.php + vendor-patched/* diff --git a/packages/playground/data-liberation/plugin-build.sh b/packages/playground/data-liberation/plugin-build.sh new file mode 100644 index 0000000000..e5c082918f --- /dev/null +++ b/packages/playground/data-liberation/plugin-build.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +echo "Running the full build of the data liberation plugin" +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Only include the subset of blueprints-library that we need +# for the importers to work. Don't include the PHP Blueprint +# library yet. +zip -r ./dist/data-liberation-plugin.zip ./*.php ./src \ + ./blueprints-library/src/WordPress/AsyncHttp \ + ./blueprints-library/src/WordPress/Zip \ + ./blueprints-library/src/WordPress/Util \ + ./blueprints-library/src/WordPress/Streams \ + ./vendor + +echo "Done" diff --git a/packages/playground/data-liberation/plugin.php b/packages/playground/data-liberation/plugin.php index 94c2b1b3ba..f17704ebcc 100644 --- a/packages/playground/data-liberation/plugin.php +++ b/packages/playground/data-liberation/plugin.php @@ -494,8 +494,8 @@ function ( $post ) { 'date' => $post->post_date, 'dataSource' => $import_session->get_data_source(), 'timeTaken' => human_time_diff( $import_session->get_started_at(), $import_session->is_finished() ? $import_session->get_finished_at() : time() ), - 'entitiesImported' => array_sum( array_column( $import_session->count_imported_entities(), 'imported' ) ), - 'totalEntities' => array_sum( array_column( $import_session->count_imported_entities(), 'total' ) ), + 'entitiesImported' => $import_session->count_all_imported_entities(), + 'totalEntities' => $import_session->count_all_total_entities(), 'status' => $import_session->get_stage(), ); }, diff --git a/packages/playground/data-liberation/project.json b/packages/playground/data-liberation/project.json index 8b8bc80e1b..f2712fef71 100644 --- a/packages/playground/data-liberation/project.json +++ b/packages/playground/data-liberation/project.json @@ -12,6 +12,22 @@ "parallel": false } }, + "build:phar": { + "executor": "nx:run-commands", + "options": { + "cwd": "packages/playground/data-liberation", + "commands": ["bash ./phar-core-build.sh"], + "parallel": false + } + }, + "build:plugin": { + "executor": "nx:run-commands", + "options": { + "cwd": "packages/playground/data-liberation", + "commands": ["bash ./plugin-build.sh"], + "parallel": false + } + }, "lint:php": { "executor": "nx:run-commands", "options": { diff --git a/packages/playground/data-liberation/rector.php b/packages/playground/data-liberation/rector.php new file mode 100644 index 0000000000..41a40e466a --- /dev/null +++ b/packages/playground/data-liberation/rector.php @@ -0,0 +1,22 @@ +withPaths( + array( + __DIR__ . '/vendor/rowbot/url', + ) + ) + ->withDowngradeSets( + false, + false, + false, + false, + false, + true + ) + ->withTypeCoverageLevel( 0 ); diff --git a/packages/playground/data-liberation/src/README.md b/packages/playground/data-liberation/src/README.md index fd8f31a3b8..f3f16107d3 100644 --- a/packages/playground/data-liberation/src/README.md +++ b/packages/playground/data-liberation/src/README.md @@ -55,7 +55,7 @@ v1 to ship and stress-test in Playground. – Test the speed of WP_Migration_URL_In_Text_Processor. Consider switching to the detector/rewriter explored by @dmsnell. Later on this might also require using his UTF-8 decoder. - – Make this PHP code compatible with PHP 7.2 – 8.3 + – Make this PHP code compatible with PHP 7.2 – 8.4 – Add extensibility so that specific plugins may, e.g., base64_decode() their block attributes before migrating the URLs. – Consider dropping the code from `src/wordpress-core`. Downside: Having a subset of diff --git a/packages/playground/data-liberation/src/block-markup/WP_URL.php b/packages/playground/data-liberation/src/block-markup/WP_URL.php index 4a0ffb6abd..84f6c1e745 100644 --- a/packages/playground/data-liberation/src/block-markup/WP_URL.php +++ b/packages/playground/data-liberation/src/block-markup/WP_URL.php @@ -9,7 +9,12 @@ class WP_URL { public static function parse( $url, $base = null ) { - return URL::parse( $url, $base ) ?? false; + if ( is_string( $url ) ) { + return URL::parse( $url, $base ) ?? false; + } elseif ( is_a( $url, 'Rowbot\URL\URL' ) ) { + return $url; + } + return false; } public static function can_parse( $url, $base = null ) { diff --git a/packages/playground/data-liberation/src/byte-readers/WP_Byte_Reader.php b/packages/playground/data-liberation/src/byte-readers/WP_Byte_Reader.php index 6d8bd247db..4671eca2cb 100644 --- a/packages/playground/data-liberation/src/byte-readers/WP_Byte_Reader.php +++ b/packages/playground/data-liberation/src/byte-readers/WP_Byte_Reader.php @@ -5,6 +5,6 @@ public function tell(): int; public function seek( int $offset ): bool; public function is_finished(): bool; public function next_bytes(): bool; - public function get_bytes(): string|null; - public function get_last_error(): string|null; + public function get_bytes(): ?string; + public function get_last_error(): ?string; } diff --git a/packages/playground/data-liberation/src/byte-readers/WP_File_Reader.php b/packages/playground/data-liberation/src/byte-readers/WP_File_Reader.php index f432c3ba7a..9100ed7f86 100644 --- a/packages/playground/data-liberation/src/byte-readers/WP_File_Reader.php +++ b/packages/playground/data-liberation/src/byte-readers/WP_File_Reader.php @@ -48,7 +48,7 @@ public function get_bytes(): string { return $this->output_bytes; } - public function get_last_error(): string|null { + public function get_last_error(): ?string { return $this->last_error; } diff --git a/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Ranged_Reader.php b/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Ranged_Reader.php index 50b68019b9..34ac703b18 100644 --- a/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Ranged_Reader.php +++ b/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Ranged_Reader.php @@ -139,7 +139,11 @@ public function next_chunk() { switch ( $this->client->get_event() ) { case WordPress\AsyncHttp\Client::EVENT_GOT_HEADERS: - $response = $this->client->get_request()?->response; + $request = $this->client->get_request(); + if ( ! $request ) { + return false; + } + $response = $request->response; if ( false === $response ) { return false; } diff --git a/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Reader.php b/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Reader.php index 819480d8fe..d55846f7b8 100644 --- a/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Reader.php +++ b/packages/playground/data-liberation/src/byte-readers/WP_Remote_File_Reader.php @@ -95,11 +95,11 @@ private function after_chunk() { $this->current_chunk = null; } - public function get_last_error(): string|null { + public function get_last_error(): ?string { return $this->last_error; } - public function get_bytes(): string|null { + public function get_bytes(): ?string { return $this->current_chunk; } diff --git a/packages/playground/data-liberation/src/functions.php b/packages/playground/data-liberation/src/functions.php index 05baf08aa0..44166b0f2a 100644 --- a/packages/playground/data-liberation/src/functions.php +++ b/packages/playground/data-liberation/src/functions.php @@ -42,7 +42,7 @@ function wp_rewrite_urls( $options ) { while ( $p->next_url() ) { $parsed_url = $p->get_parsed_url(); foreach ( $url_mapping as $mapping ) { - if ( url_matches( $parsed_url, $mapping['from_url'] ) ) { + if ( is_child_url_of( $parsed_url, $mapping['from_url'] ) ) { $p->replace_base_url( $mapping['to_url'] ); break; } @@ -54,25 +54,26 @@ function wp_rewrite_urls( $options ) { /** * Check if a given URL matches the current site URL. * - * @param URL $subject The URL to check. - * @param string $from_url_no_trailing_slash The current site URL to compare against. + * @param URL $parent The URL to check. + * @param string $child The current site URL to compare against. * @return bool Whether the URL matches the current site URL. */ -function url_matches( URL $subject, $from_url ) { - $parsed_from_url = is_string( $from_url ) ? WP_URL::parse( $from_url ) : $from_url; - $current_pathname_no_trailing_slash = rtrim( urldecode( $parsed_from_url->pathname ), '/' ); +function is_child_url_of( $child, $parent_url ) { + $parent_url = is_string( $parent_url ) ? WP_URL::parse( $parent_url ) : $parent_url; + $child = is_string( $child ) ? WP_URL::parse( $child ) : $child; + $child_pathname_no_trailing_slash = rtrim( urldecode( $child->pathname ), '/' ); - if ( $subject->hostname !== $parsed_from_url->hostname ) { + if ( $parent_url->hostname !== $child->hostname ) { return false; } - $matched_pathname_decoded = urldecode( $subject->pathname ); + $parent_pathname = urldecode( $parent_url->pathname ); return ( // Direct match - $matched_pathname_decoded === $current_pathname_no_trailing_slash || - $matched_pathname_decoded === $current_pathname_no_trailing_slash . '/' || + $parent_pathname === $child_pathname_no_trailing_slash || + $parent_pathname === $child_pathname_no_trailing_slash . '/' || // Path prefix - str_starts_with( $matched_pathname_decoded, $current_pathname_no_trailing_slash . '/' ) + str_starts_with( $child_pathname_no_trailing_slash . '/', $parent_pathname ) ); } @@ -234,3 +235,57 @@ function ( $value ) { get_post_meta( $post_id ) ); } + +/** + * Polyfill the mb_str_split function used by Rowbot\URL\URL. + * + * Source: https://www.php.net/manual/en/function.mb-str-split.php#125429 + */ +if ( ! function_exists( 'mb_str_split' ) ) { + function mb_str_split( $input, $split_length = 1, $encoding = null ) { + if ( null !== $input && ! \is_scalar( $input ) && ! ( \is_object( $input ) && \method_exists( $input, '__toString' ) ) ) { + trigger_error( 'mb_str_split(): expects parameter 1 to be string, ' . \gettype( $input ) . ' given', E_USER_WARNING ); + return null; + } + if ( null !== $split_length && ! \is_bool( $split_length ) && ! \is_numeric( $split_length ) ) { + trigger_error( 'mb_str_split(): expects parameter 2 to be int, ' . \gettype( $split_length ) . ' given', E_USER_WARNING ); + return null; + } + $split_length = (int) $split_length; + if ( 1 > $split_length ) { + trigger_error( 'mb_str_split(): The length of each segment must be greater than zero', E_USER_WARNING ); + return false; + } + if ( null === $encoding ) { + $encoding = mb_internal_encoding(); + } else { + $encoding = (string) $encoding; + } + $encoding = strtoupper( $encoding ); + if ( ! in_array( $encoding, mb_list_encodings(), true ) ) { + static $aliases; + if ( $aliases === null ) { + $aliases = array(); + foreach ( mb_list_encodings() as $encoding ) { + $encoding_aliases = mb_encoding_aliases( $encoding ); + if ( $encoding_aliases ) { + foreach ( $encoding_aliases as $alias ) { + $aliases[] = $alias; + } + } + } + } + if ( ! in_array( $encoding, $aliases, true ) ) { + trigger_error( 'mb_str_split(): Unknown encoding "' . $encoding . '"', E_USER_WARNING ); + return null; + } + } + + $result = array(); + $length = mb_strlen( $input, $encoding ); + for ( $i = 0; $i < $length; $i += $split_length ) { + $result[] = mb_substr( $input, $i, $split_length, $encoding ); + } + return $result; + } +} diff --git a/packages/playground/data-liberation/src/import/WP_Attachment_Downloader.php b/packages/playground/data-liberation/src/import/WP_Attachment_Downloader.php index 7dad1f328f..f16306275c 100644 --- a/packages/playground/data-liberation/src/import/WP_Attachment_Downloader.php +++ b/packages/playground/data-liberation/src/import/WP_Attachment_Downloader.php @@ -37,8 +37,7 @@ public function has_pending_requests() { } public function enqueue_if_not_exists( $url, $output_relative_path ) { - $this->enqueued_url = $url; - + $this->enqueued_url = $url; $output_relative_path = $this->output_root . '/' . ltrim( $output_relative_path, '/' ); if ( file_exists( $output_relative_path ) ) { $this->pending_events[] = new WP_Attachment_Downloader_Event( @@ -47,6 +46,13 @@ public function enqueue_if_not_exists( $url, $output_relative_path ) { ); return true; } + if ( file_exists( $output_relative_path . '.partial' ) ) { + $this->pending_events[] = new WP_Attachment_Downloader_Event( + $this->enqueued_url, + WP_Attachment_Downloader_Event::IN_PROGRESS + ); + return true; + } $output_dir = dirname( $output_relative_path ); if ( ! file_exists( $output_dir ) ) { @@ -83,6 +89,8 @@ public function enqueue_if_not_exists( $url, $output_relative_path ) { return true; case 'http': case 'https': + // Create a placeholder file to indicate that the download is in progress. + touch( $output_relative_path . '.partial' ); $request = new Request( $url ); $this->output_paths[ $request->id ] = $output_relative_path; $this->progress[ $this->enqueued_url ] = array( @@ -128,6 +136,11 @@ public function poll() { $original_url = $request->original_request()->url; $original_request_id = $request->original_request()->id; + /** + * @TODO: Whenever we get a redirect to a URL we've already processed, + * stop and emit a success event. + */ + switch ( $event ) { case Client::EVENT_GOT_HEADERS: if ( ! $request->is_redirected() ) { diff --git a/packages/playground/data-liberation/src/import/WP_Attachment_Downloader_Event.php b/packages/playground/data-liberation/src/import/WP_Attachment_Downloader_Event.php index 0502107782..7a8f5cbfc9 100644 --- a/packages/playground/data-liberation/src/import/WP_Attachment_Downloader_Event.php +++ b/packages/playground/data-liberation/src/import/WP_Attachment_Downloader_Event.php @@ -5,6 +5,7 @@ class WP_Attachment_Downloader_Event { const SUCCESS = '#success'; const FAILURE = '#failure'; const ALREADY_EXISTS = '#already_exists'; + const IN_PROGRESS = '#in_progress'; public $type; public $resource_id; diff --git a/packages/playground/data-liberation/src/import/WP_Entity_Importer.php b/packages/playground/data-liberation/src/import/WP_Entity_Importer.php index 0daebac7b4..95ff593f6f 100644 --- a/packages/playground/data-liberation/src/import/WP_Entity_Importer.php +++ b/packages/playground/data-liberation/src/import/WP_Entity_Importer.php @@ -1,5 +1,7 @@ entities_iterator = $iterator; } - public function current(): mixed { + #[\ReturnTypeWillChange] + public function current() { $iterator = $this->get_iterator(); return $iterator ? $iterator->current() : null; } - public function key(): mixed { + #[\ReturnTypeWillChange] + public function key() { $iterator = $this->get_iterator(); return $iterator ? $iterator->key() : null; } @@ -53,7 +55,8 @@ private function get_iterator(): ?Iterator { return null; } - public function get_reentrancy_cursor(): mixed { + #[\ReturnTypeWillChange] + public function get_reentrancy_cursor() { if ( ! $this->entities_iterator ) { return null; } diff --git a/packages/playground/data-liberation/src/import/WP_Import_Session.php b/packages/playground/data-liberation/src/import/WP_Import_Session.php index 2f56040563..a731f3f9fc 100644 --- a/packages/playground/data-liberation/src/import/WP_Import_Session.php +++ b/packages/playground/data-liberation/src/import/WP_Import_Session.php @@ -227,6 +227,22 @@ public function count_imported_entities() { } return $progress; } + + public function count_all_imported_entities() { + $counts = $this->count_imported_entities(); + return array_sum( array_column( $counts, 'imported' ) ); + } + + public function count_all_total_entities() { + $counts = $this->count_imported_entities(); + return array_sum( array_column( $counts, 'total' ) ); + } + + public function count_remaining_entities() { + $counts = $this->count_imported_entities(); + return array_sum( array_column( $counts, 'total' ) ) - array_sum( array_column( $counts, 'imported' ) ); + } + /** * Cache of imported entity counts to avoid repeated database queries * @var array @@ -276,6 +292,20 @@ public function bump_imported_entities_counts( $newly_imported_entities ) { } } + public function count_awaiting_frontloading_placeholders() { + global $wpdb; + return (int) $wpdb->get_var( + $wpdb->prepare( + "SELECT COUNT(*) FROM $wpdb->posts + WHERE post_type = 'frontloading_placeholder' + AND post_parent = %d + AND post_status = %s", + $this->post_id, + self::FRONTLOAD_STATUS_AWAITING_DOWNLOAD + ) + ); + } + public function count_unfinished_frontloading_placeholders() { global $wpdb; return (int) $wpdb->get_var( diff --git a/packages/playground/data-liberation/src/import/WP_Stream_Importer.php b/packages/playground/data-liberation/src/import/WP_Stream_Importer.php index 05b85ff05b..41f08a4e7c 100644 --- a/packages/playground/data-liberation/src/import/WP_Stream_Importer.php +++ b/packages/playground/data-liberation/src/import/WP_Stream_Importer.php @@ -36,6 +36,37 @@ class WP_Stream_Importer { * Populated from the WXR file's tag. */ private $source_site_url; + /** + * A list of [original_url, migrated_url] pairs for rewriting the URLs + * in the imported content. + */ + private $site_url_mapping = array(); + /** + * A list of candidate base URLs that have been spotted in the WXR file. + * + * For example, the theme unit test data refers to a site with a base + * URL https://wpthemetestdata.wordpress.com/, but it contains attachments + * from https://wpthemetestdata.files.wordpress.com/. + * + * Every time the importer encounters a previously unseen attachment domain, + * it needs more information to map it. We can't just guess. Assuming we're + * importing into https://example.com/, we could guess that + * https://wpthemetestdata.files.wordpress.com/ maps to: + * + * * https://example.com/ + * * https://example.com/wp-content/ + * * https://example.com/wp-content/uploads/ + * * ...a completely different path. + * + * There's no reliable way to guess the correct mapping. Instead of trying, + * we're exposing the external domain to the API consumer, who can then + * gather additional information from the user and decide whether to map + * it and how. + * + * Once the API consumer decides on the mapping, it can call + * add_site_url_mapping() to tell the importer what to map that domain to. + */ + private $site_url_mapping_candidates = array(); private $entity_iterator_factory; /** * @param array|string|null $query { @@ -138,12 +169,56 @@ private function initialize_from_cursor( $cursor ) { $this->next_stage = $cursor['next_stage']; $this->resume_at_entity = $cursor['resume_at_entity']; if ( ! empty( $cursor['source_site_url'] ) ) { - $this->source_site_url = $cursor['source_site_url']; + $this->set_source_site_url( $cursor['source_site_url'] ); + } + if ( ! empty( $cursor['site_url_mapping'] ) ) { + foreach ( $cursor['site_url_mapping'] as $pair ) { + $this->add_site_url_mapping( $pair[0], $pair[1] ); + } + } + if ( ! empty( $cursor['site_url_mapping_candidates'] ) ) { + $this->site_url_mapping_candidates = $cursor['site_url_mapping_candidates']; } return true; } + private function set_source_site_url( $source_site_url ) { + $this->source_site_url = $source_site_url; + // -1 is a well-known index for the source site URL. + // Every subsequent call to set_source_site_url() will + // override that mapping. + $this->site_url_mapping[-1] = array( + WP_URL::parse( $source_site_url ), + WP_URL::parse( $this->options['new_site_url'] ), + ); + } + + public function get_site_url_mapping_candidates() { + // Only return the candidates that have been spotted in the last index_entities() call. + if ( self::STAGE_INDEX_ENTITIES !== $this->stage ) { + return array(); + } + $new_candidates = array(); + foreach ( $this->site_url_mapping_candidates as $base_url => $status ) { + if ( false === $status ) { + $new_candidates[] = $base_url; + } + } + return $new_candidates; + } + + public function add_site_url_mapping( $from, $to ) { + $this->site_url_mapping[] = array( WP_URL::parse( $from ), WP_URL::parse( $to ) ); + } + public function get_reentrancy_cursor() { + $serialized_site_url_mapping = array(); + foreach ( $this->site_url_mapping as $pair ) { + $serialized_site_url_mapping[] = array( + (string) $pair[0], + (string) $pair[1], + ); + } return json_encode( array( 'stage' => $this->stage, @@ -154,6 +229,8 @@ public function get_reentrancy_cursor() { 'next_stage' => $this->next_stage, 'resume_at_entity' => $this->resume_at_entity, 'source_site_url' => $this->source_site_url, + 'site_url_mapping' => $serialized_site_url_mapping, + 'site_url_mapping_candidates' => $this->site_url_mapping_candidates, ) ); } @@ -185,7 +262,7 @@ private function __construct( $this->entity_iterator_factory = $entity_iterator_factory; $this->options = $options; if ( isset( $options['default_source_site_url'] ) ) { - $this->source_site_url = $options['default_source_site_url']; + $this->set_source_site_url( $options['default_source_site_url'] ); } } @@ -263,6 +340,11 @@ private function index_next_entities( $count = 10000 ) { $this->entity_iterator = $this->create_entity_iterator(); } + // Mark all mapping candidates as seen. + foreach ( $this->site_url_mapping_candidates as $base_url => $status ) { + $this->site_url_mapping_candidates[ $base_url ] = true; + } + // Reset the counts and URLs found in the previous pass. $this->indexed_entities_counts = array(); $this->indexed_assets_urls = array(); @@ -312,11 +394,26 @@ private function index_next_entities( $count = 10000 ) { switch ( $type ) { case 'site_option': if ( $data['option_name'] === 'home' ) { - $this->source_site_url = $data['option_value']; + $this->set_source_site_url( $data['option_value'] ); } break; case 'post': if ( isset( $data['post_type'] ) && $data['post_type'] === 'attachment' ) { + /** + * Keep track of alternative domains used to reference attachments, + * e.g. Theme Unit Test Data site lives at https://wpthemetestdata.wordpress.com/ + * but many attachments are served from https://wpthemetestdata.files.wordpress.com/ + */ + $parsed_url = WP_URL::parse( $data['attachment_url'] ); + if ( $parsed_url ) { + $parsed_url->pathname = ''; + $parsed_url->search = ''; + $parsed_url->hash = ''; + $base_url = $parsed_url->toString(); + if ( ! array_key_exists( $base_url, $this->site_url_mapping_candidates ) ) { + $this->site_url_mapping_candidates[ $base_url ] = false; + } + } // @TODO: Consider using sha1 hashes to prevent huge URLs from blowing up the memory. $this->indexed_assets_urls[ $data['attachment_url'] ] = true; } elseif ( isset( $data['post_content'] ) ) { @@ -339,6 +436,16 @@ private function index_next_entities( $count = 10000 ) { return true; } + public function get_new_site_url_mapping_candidates() { + $candidates = array(); + foreach ( $this->site_url_mapping_candidates as $base_url => $status ) { + if ( false === $status ) { + $candidates[] = $base_url; + } + } + return $candidates; + } + public function get_indexed_entities_counts() { return $this->indexed_entities_counts; } @@ -374,6 +481,7 @@ private function frontloading_advance_reentrancy_cursor() { switch ( $event->type ) { case WP_Attachment_Downloader_Event::FAILURE: case WP_Attachment_Downloader_Event::SUCCESS: + case WP_Attachment_Downloader_Event::IN_PROGRESS: case WP_Attachment_Downloader_Event::ALREADY_EXISTS: $this->frontloading_events[] = $event; foreach ( array_keys( $this->active_downloads ) as $entity_cursor ) { @@ -539,11 +647,22 @@ private function import_next_entity() { } $p = new WP_Block_Markup_Url_Processor( $data[ $key ], $this->source_site_url ); while ( $p->next_url() ) { - if ( $this->url_processor_matched_asset_url( $p ) ) { - $filename = $this->new_asset_filename( $p->get_raw_url() ); - $new_asset_url = $this->options['uploads_url'] . '/' . $filename; - $p->replace_base_url( WP_URL::parse( $new_asset_url ) ); - $attachments[] = $new_asset_url; + // Relative URLs are okay at this stage. + if ( ! $p->get_raw_url() ) { + continue; + } + + /** + * Any URL that has a corresponding frontloaded file is an asset URL. + */ + $asset_filename = $this->new_asset_filename( + $p->get_raw_url(), + $data['source_path'] ?? $data['slug'] ?? null + ); + if ( file_exists( $this->options['uploads_path'] . '/' . $asset_filename ) ) { + $p->set_raw_url( + $this->options['uploads_url'] . '/' . $asset_filename + ); /** * @TODO: How would we know a specific image block refers to a specific * attachment? We need to cross-correlate that to rewrite the URL. @@ -553,13 +672,18 @@ private function import_next_entity() { * A few ideas: GUID, block attributes, fuzzy matching. Maybe a configurable * strategy? And the API consumer would make the decision? */ - } elseif ( $this->source_site_url && - $p->get_parsed_url() && - url_matches( $p->get_parsed_url(), $this->source_site_url ) - ) { - $p->replace_base_url( WP_URL::parse( $this->options['new_site_url'] ) ); - } else { - // Ignore other URLs. + continue; + } + + // Absolute URLs are required at this stage. + if ( ! $p->get_parsed_url() ) { + continue; + } + + $target_base_url = $this->get_url_mapping_target( $p->get_parsed_url() ); + if ( false !== $target_base_url ) { + $p->replace_base_url( $target_base_url ); + continue; } } $data[ $key ] = $p->get_updated_html(); @@ -605,15 +729,15 @@ public function get_imported_entities_counts() { } private function enqueue_attachment_download( string $raw_url, $options = array() ) { - $context_path = $options['context_path'] ?? null; - $original_url = $options['original_url'] ?? $raw_url; - $url = $this->rewrite_attachment_url( $raw_url, $context_path ); - $asset_filename = $this->new_asset_filename( $original_url ); - $output_filename = ltrim( $asset_filename, '/' ); + $output_filename = $this->new_asset_filename( + $options['original_url'] ?? $raw_url, + $options['context_path'] ?? null + ); - $enqueued = $this->downloader->enqueue_if_not_exists( $url, $output_filename ); + $download_url = $this->rewrite_attachment_url( $raw_url, $options['context_path'] ?? null ); + $enqueued = $this->downloader->enqueue_if_not_exists( $download_url, $output_filename ); if ( false === $enqueued ) { - _doing_it_wrong( __METHOD__, sprintf( 'Failed to enqueue attachment download: %s', $url ), '1.0' ); + _doing_it_wrong( __METHOD__, sprintf( 'Failed to enqueue attachment download: %s', $raw_url ), '1.0' ); return false; } @@ -652,7 +776,12 @@ private function enqueue_attachment_download( string $raw_url, $options = array( * different permissions. Just because Bob deletes his copy, doesn't * mean we should delete Alice's copy. */ - private function new_asset_filename( string $raw_asset_url ) { + private function new_asset_filename( string $raw_asset_url, $context_path = null ) { + $raw_asset_url = $this->rewrite_attachment_url( + $raw_asset_url, + $context_path + ); + $filename = md5( $raw_asset_url ); $parsed_url = WP_URL::parse( $raw_asset_url ); if ( false !== $parsed_url ) { @@ -696,10 +825,25 @@ private function url_processor_matched_asset_url( WP_Block_Markup_Url_Processor return ( $p->get_tag() === 'IMG' && $p->get_inspected_attribute_name() === 'src' && - ( ! $this->source_site_url || url_matches( $p->get_parsed_url(), $this->source_site_url ) ) + $this->is_child_of_a_mapped_url( $p->get_parsed_url() ) ); } + private function is_child_of_a_mapped_url( $url ) { + return $this->get_url_mapping_target( $url ) !== false; + } + + private function get_url_mapping_target( $source_url ) { + $url = WP_URL::parse( $source_url ); + foreach ( $this->site_url_mapping as $pair ) { + $parsed_base_url = $pair[0]; + if ( is_child_url_of( $parsed_base_url, $url ) ) { + return $pair[1]; + } + } + return false; + } + private $first_iterator = true; private function create_entity_iterator() { $factory = $this->entity_iterator_factory; diff --git a/packages/playground/data-liberation/src/wxr/WXR_Importer.php b/packages/playground/data-liberation/src/wxr/WXR_Importer.php deleted file mode 100644 index 1f67973efb..0000000000 --- a/packages/playground/data-liberation/src/wxr/WXR_Importer.php +++ /dev/null @@ -1,2374 +0,0 @@ -` tag at the start of the file. - * - * @var string - */ - protected $version = '1.0'; - - // information to import from WXR file - protected $categories = array(); - protected $tags = array(); - protected $base_url = ''; - - // TODO: REMOVE THESE - protected $processed_terms = array(); - protected $processed_posts = array(); - protected $processed_menu_items = array(); - protected $menu_item_orphans = array(); - protected $missing_menu_items = array(); - - // NEW STYLE - protected $mapping = array(); - protected $requires_remapping = array(); - protected $exists = array(); - protected $user_slug_override = array(); - - protected $url_remap = array(); - protected $featured_images = array(); - - /** - * Logger instance. - * - * @var WP_Importer_Logger - */ - protected $logger; - - /** - * Constructor - * - * @param array $options { - * @var bool $prefill_existing_posts Should we prefill `post_exists` calls? (True prefills and uses more memory, false checks once per imported post and takes longer. Default is true.) - * @var bool $prefill_existing_comments Should we prefill `comment_exists` calls? (True prefills and uses more memory, false checks once per imported comment and takes longer. Default is true.) - * @var bool $prefill_existing_terms Should we prefill `term_exists` calls? (True prefills and uses more memory, false checks once per imported term and takes longer. Default is true.) - * @var bool $update_attachment_guids Should attachment GUIDs be updated to the new URL? (True updates the GUID, which keeps compatibility with v1, false doesn't update, and allows deduplication and reimporting. Default is false.) - * @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.) - * @var bool $aggressive_url_search Should we search/replace for URLs aggressively? (True searches all posts' content for old URLs and replaces, false checks for `` only. Default is false.) - * @var int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.) - * } - */ - public function __construct( $options = array() ) { - // Initialize some important variables - $empty_types = array( - 'post' => array(), - 'comment' => array(), - 'term' => array(), - 'user' => array(), - ); - - $this->mapping = $empty_types; - $this->mapping['user_slug'] = array(); - $this->mapping['term_id'] = array(); - $this->requires_remapping = $empty_types; - $this->exists = $empty_types; - - $this->options = wp_parse_args( - $options, - array( - 'prefill_existing_posts' => true, - 'prefill_existing_comments' => true, - 'prefill_existing_terms' => true, - 'update_attachment_guids' => false, - 'fetch_attachments' => false, - 'aggressive_url_search' => false, - 'default_author' => null, - ) - ); - } - - public function set_logger( $logger ) { - $this->logger = $logger; - } - - /** - * Get a stream reader for the file. - * - * @param string $file Path to the XML file. - * @return XMLReader|WP_Error Reader instance on success, error otherwise. - */ - protected function get_reader( $file ) { - // Avoid loading external entities for security - $old_value = null; - if ( function_exists( 'libxml_disable_entity_loader' ) ) { - // $old_value = libxml_disable_entity_loader( true ); - } - - $reader = new XMLReader(); - $status = $reader->open( $file ); - - if ( ! is_null( $old_value ) ) { - // libxml_disable_entity_loader( $old_value ); - } - - if ( ! $status ) { - return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'wordpress-importer' ) ); - } - - return $reader; - } - - /** - * The main controller for the actual import stage. - * - * @param string $file Path to the WXR file for importing - */ - public function get_preliminary_information( $file ) { - // Let's run the actual importer now, woot - $reader = $this->get_reader( $file ); - if ( is_wp_error( $reader ) ) { - return $reader; - } - - // Set the version to compatibility mode first - $this->version = '1.0'; - - // Start parsing! - $data = new WXR_Import_Info(); - while ( $reader->read() ) { - // Only deal with element opens - if ( $reader->nodeType !== XMLReader::ELEMENT ) { - continue; - } - - switch ( $reader->name ) { - case 'wp:wxr_version': - // Upgrade to the correct version - $this->version = $reader->readString(); - - if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) { - $this->logger->warning( - sprintf( - __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ), - $this->version, - self::MAX_WXR_VERSION - ) - ); - } - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'generator': - $data->generator = $reader->readString(); - $reader->next(); - break; - - case 'title': - $data->title = $reader->readString(); - $reader->next(); - break; - - case 'wp:base_site_url': - $data->siteurl = $reader->readString(); - $reader->next(); - break; - - case 'wp:base_blog_url': - $data->home = $reader->readString(); - $reader->next(); - break; - - case 'wp:author': - $node = $reader->expand(); - - $parsed = $this->parse_author_node( $node ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - $data->users[] = $parsed; - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'item': - $node = $reader->expand(); - $parsed = $this->parse_post_node( $node ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - if ( $parsed['data']['post_type'] === 'attachment' ) { - ++$data->media_count; - } else { - ++$data->post_count; - } - $data->comment_count += count( $parsed['comments'] ); - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'wp:category': - case 'wp:tag': - case 'wp:term': - ++$data->term_count; - - // Handled everything in this node, move on to the next - $reader->next(); - break; - } - } - - $data->version = $this->version; - - return $data; - } - - /** - * The main controller for the actual import stage. - * - * @param string $file Path to the WXR file for importing - */ - public function parse_authors( $file ) { - // Let's run the actual importer now, woot - $reader = $this->get_reader( $file ); - if ( is_wp_error( $reader ) ) { - return $reader; - } - - // Set the version to compatibility mode first - $this->version = '1.0'; - - // Start parsing! - $authors = array(); - while ( $reader->read() ) { - // Only deal with element opens - if ( $reader->nodeType !== XMLReader::ELEMENT ) { - continue; - } - - switch ( $reader->name ) { - case 'wp:wxr_version': - // Upgrade to the correct version - $this->version = $reader->readString(); - - if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) { - $this->logger->warning( - sprintf( - __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ), - $this->version, - self::MAX_WXR_VERSION - ) - ); - } - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'wp:author': - $node = $reader->expand(); - - $parsed = $this->parse_author_node( $node ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - $authors[] = $parsed; - - // Handled everything in this node, move on to the next - $reader->next(); - break; - } - } - - return $authors; - } - - /** - * The main controller for the actual import stage. - * - * @param string $file Path to the WXR file for importing - */ - public function import( $file ) { - add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) ); - add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) ); - - $result = $this->import_start( $file ); - if ( is_wp_error( $result ) ) { - return $result; - } - - // Let's run the actual importer now, woot - $reader = $this->get_reader( $file ); - if ( is_wp_error( $reader ) ) { - return $reader; - } - - // Set the version to compatibility mode first - $this->version = '1.0'; - - // Reset other variables - $this->base_url = ''; - - // Start parsing! - while ( $reader->read() ) { - // Only deal with element opens - if ( $reader->nodeType !== XMLReader::ELEMENT ) { - continue; - } - - switch ( $reader->name ) { - case 'wp:wxr_version': - // Upgrade to the correct version - $this->version = $reader->readString(); - - if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) { - $this->logger->warning( - sprintf( - __( 'This WXR file (version %1$s) is newer than the importer (version %2$s) and may not be supported. Please consider updating.', 'wordpress-importer' ), - $this->version, - self::MAX_WXR_VERSION - ) - ); - } - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'wp:base_site_url': - $this->base_url = $reader->readString(); - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'item': - $node = $reader->expand(); - $parsed = $this->parse_post_node( $node ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] ); - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'wp:author': - $node = $reader->expand(); - - $parsed = $this->parse_author_node( $node ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - $status = $this->process_author( $parsed['data'], $parsed['meta'] ); - if ( is_wp_error( $status ) ) { - $this->log_error( $status ); - } - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'wp:category': - $node = $reader->expand(); - - $parsed = $this->parse_term_node( $node, 'category' ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - $status = $this->process_term( $parsed['data'], $parsed['meta'] ); - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'wp:tag': - $node = $reader->expand(); - - $parsed = $this->parse_term_node( $node, 'tag' ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - $status = $this->process_term( $parsed['data'], $parsed['meta'] ); - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - case 'wp:term': - $node = $reader->expand(); - - $parsed = $this->parse_term_node( $node ); - if ( is_wp_error( $parsed ) ) { - $this->log_error( $parsed ); - - // Skip the rest of this post - $reader->next(); - break; - } - - $status = $this->process_term( $parsed['data'], $parsed['meta'] ); - - // Handled everything in this node, move on to the next - $reader->next(); - break; - - default: - // Skip this node, probably handled by something already - break; - } - } - - // Now that we've done the main processing, do any required - // post-processing and remapping. - $this->post_process(); - - if ( $this->options['aggressive_url_search'] ) { - $this->replace_attachment_urls_in_content(); - } - // $this->remap_featured_images(); - - $this->import_end(); - } - - /** - * Log an error instance to the logger. - * - * @param WP_Error $error Error instance to log. - */ - protected function log_error( WP_Error $error ) { - $this->logger->warning( $error->get_error_message() ); - - // Log the data as debug info too - $data = $error->get_error_data(); - if ( ! empty( $data ) ) { - $this->logger->debug( var_export( $data, true ) ); - } - } - - /** - * Parses the WXR file and prepares us for the task of processing parsed data - * - * @param string $file Path to the WXR file for importing - */ - protected function import_start( $file ) { - if ( ! is_file( $file ) ) { - return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'wordpress-importer' ) ); - } - - // Suspend bunches of stuff in WP core - wp_defer_term_counting( true ); - wp_defer_comment_counting( true ); - wp_suspend_cache_invalidation( true ); - - // Prefill exists calls if told to - if ( $this->options['prefill_existing_posts'] ) { - $this->prefill_existing_posts(); - } - if ( $this->options['prefill_existing_comments'] ) { - $this->prefill_existing_comments(); - } - if ( $this->options['prefill_existing_terms'] ) { - $this->prefill_existing_terms(); - } - - /** - * Begin the import. - * - * Fires before the import process has begun. If you need to suspend - * caching or heavy processing on hooks, do so here. - */ - do_action( 'import_start' ); - } - - /** - * Performs post-import cleanup of files and the cache - */ - protected function import_end() { - // Re-enable stuff in core - wp_suspend_cache_invalidation( false ); - wp_cache_flush(); - foreach ( get_taxonomies() as $tax ) { - delete_option( "{$tax}_children" ); - _get_term_hierarchy( $tax ); - } - - wp_defer_term_counting( false ); - wp_defer_comment_counting( false ); - - /** - * Complete the import. - * - * Fires after the import process has finished. If you need to update - * your cache or re-enable processing, do so here. - */ - do_action( 'import_end' ); - } - - /** - * Set the user mapping. - * - * @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`) - */ - public function set_user_mapping( $mapping ) { - foreach ( $mapping as $map ) { - if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) { - $this->logger->warning( __( 'Invalid author mapping', 'wordpress-importer' ) ); - $this->logger->debug( var_export( $map, true ) ); - continue; - } - - $old_slug = $map['old_slug']; - $old_id = $map['old_id']; - $new_id = $map['new_id']; - - $this->mapping['user'][ $old_id ] = $new_id; - $this->mapping['user_slug'][ $old_slug ] = $new_id; - } - } - - /** - * Set the user slug overrides. - * - * Allows overriding the slug in the import with a custom/renamed version. - * - * @param string[] $overrides Map of old slug to new slug. - */ - public function set_user_slug_overrides( $overrides ) { - foreach ( $overrides as $original => $renamed ) { - $this->user_slug_override[ $original ] = $renamed; - } - } - - /** - * Parse a post node into post data. - * - * @param DOMElement $node Parent node of post data (typically `item`). - * @return array|WP_Error Post data array on success, error otherwise. - */ - protected function parse_post_node( $node ) { - $data = array(); - $meta = array(); - $comments = array(); - $terms = array(); - - foreach ( $node->childNodes as $child ) { - // We only care about child elements - if ( $child->nodeType !== XML_ELEMENT_NODE ) { - continue; - } - - switch ( $child->tagName ) { - case 'wp:post_type': - $data['post_type'] = $child->textContent; - break; - - case 'title': - $data['post_title'] = $child->textContent; - break; - - case 'guid': - $data['guid'] = $child->textContent; - break; - - case 'dc:creator': - $data['post_author'] = $child->textContent; - break; - - case 'content:encoded': - $data['post_content'] = $child->textContent; - break; - - case 'excerpt:encoded': - $data['post_excerpt'] = $child->textContent; - break; - - case 'wp:post_id': - $data['post_id'] = $child->textContent; - break; - - case 'wp:post_date': - $data['post_date'] = $child->textContent; - break; - - case 'wp:post_date_gmt': - $data['post_date_gmt'] = $child->textContent; - break; - - case 'wp:comment_status': - $data['comment_status'] = $child->textContent; - break; - - case 'wp:ping_status': - $data['ping_status'] = $child->textContent; - break; - - case 'wp:post_name': - $data['post_name'] = $child->textContent; - break; - - case 'wp:status': - $data['post_status'] = $child->textContent; - - if ( $data['post_status'] === 'auto-draft' ) { - // Bail now - return new WP_Error( - 'wxr_importer.post.cannot_import_draft', - __( 'Cannot import auto-draft posts' ), - $data - ); - } - break; - - case 'wp:post_parent': - $data['post_parent'] = $child->textContent; - break; - - case 'wp:menu_order': - $data['menu_order'] = $child->textContent; - break; - - case 'wp:post_password': - $data['post_password'] = $child->textContent; - break; - - case 'wp:is_sticky': - $data['is_sticky'] = $child->textContent; - break; - - case 'wp:attachment_url': - $data['attachment_url'] = $child->textContent; - break; - - case 'wp:postmeta': - $meta_item = $this->parse_meta_node( $child ); - if ( ! empty( $meta_item ) ) { - $meta[] = $meta_item; - } - break; - - case 'wp:comment': - $comment_item = $this->parse_comment_node( $child ); - if ( ! empty( $comment_item ) ) { - $comments[] = $comment_item; - } - break; - - case 'category': - $term_item = $this->parse_category_node( $child ); - if ( ! empty( $term_item ) ) { - $terms[] = $term_item; - } - break; - } - } - - return compact( 'data', 'meta', 'comments', 'terms' ); - } - - /** - * Create new posts based on import information - * - * Posts marked as having a parent which doesn't exist will become top level items. - * Doesn't create a new post if: the post type doesn't exist, the given post ID - * is already noted as imported or a post with the same title and date already exists. - * Note that new/updated terms, comments and meta are imported for the last of the above. - */ - protected function process_post( $data, $meta, $comments, $terms ) { - /** - * Pre-process post data. - * - * @param array $data Post data. (Return empty to skip.) - * @param array $meta Meta data. - * @param array $comments Comments on the post. - * @param array $terms Terms on the post. - */ - $data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms ); - if ( empty( $data ) ) { - return false; - } - - $original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0; - $parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0; - $author_id = isset( $data['post_author'] ) ? (int) $data['post_author'] : 0; - - // Have we already processed this? - if ( isset( $this->mapping['post'][ $original_id ] ) ) { - return; - } - - $post_type_object = get_post_type_object( $data['post_type'] ); - - // Is this type even valid? - if ( ! $post_type_object ) { - $this->logger->warning( - sprintf( - __( 'Failed to import "%1$s": Invalid post type %2$s', 'wordpress-importer' ), - $data['post_title'], - $data['post_type'] - ) - ); - return false; - } - - $post_exists = $this->post_exists( $data ); - if ( $post_exists ) { - $this->logger->info( - sprintf( - __( '%1$s "%2$s" already exists.', 'wordpress-importer' ), - $post_type_object->labels->singular_name, - $data['post_title'] - ) - ); - - /** - * Post processing already imported. - * - * @param array $data Raw data imported for the post. - */ - do_action( 'wxr_importer.process_already_imported.post', $data ); - - // Even though this post already exists, new comments might need importing - $this->process_comments( $comments, $original_id, $data, $post_exists ); - - return false; - } - - // Map the parent post, or mark it as one we need to fix - $requires_remapping = false; - if ( $parent_id ) { - if ( isset( $this->mapping['post'][ $parent_id ] ) ) { - $data['post_parent'] = $this->mapping['post'][ $parent_id ]; - } else { - $meta[] = array( - 'key' => '_wxr_import_parent', - 'value' => $parent_id, - ); - $requires_remapping = true; - - $data['post_parent'] = 0; - } - } - - // Map the author, or mark it as one we need to fix - $author = sanitize_user( $data['post_author'], true ); - if ( empty( $author ) ) { - // Missing or invalid author, use default if available. - $data['post_author'] = $this->options['default_author']; - } elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) { - $data['post_author'] = $this->mapping['user_slug'][ $author ]; - } else { - $meta[] = array( - 'key' => '_wxr_import_user_slug', - 'value' => $author, - ); - $requires_remapping = true; - - $data['post_author'] = (int) get_current_user_id(); - } - - // Does the post look like it contains attachment images? - if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) { - $meta[] = array( - 'key' => '_wxr_import_has_attachment_refs', - 'value' => true, - ); - $requires_remapping = true; - } - - // Whitelist to just the keys we allow - $postdata = array( - 'import_id' => $data['post_id'], - ); - $allowed = array( - 'post_author' => true, - 'post_date' => true, - 'post_date_gmt' => true, - 'post_content' => true, - 'post_excerpt' => true, - 'post_title' => true, - 'post_status' => true, - 'post_name' => true, - 'comment_status' => true, - 'ping_status' => true, - 'guid' => true, - 'post_parent' => true, - 'menu_order' => true, - 'post_type' => true, - 'post_password' => true, - ); - foreach ( $data as $key => $value ) { - if ( ! isset( $allowed[ $key ] ) ) { - continue; - } - - $postdata[ $key ] = $data[ $key ]; - } - - $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $data ); - - if ( 'attachment' === $postdata['post_type'] ) { - if ( ! $this->options['fetch_attachments'] ) { - $this->logger->notice( - sprintf( - __( 'Skipping attachment "%s", fetching attachments disabled' ), - $data['post_title'] - ) - ); - /** - * Post processing skipped. - * - * @param array $data Raw data imported for the post. - * @param array $meta Raw meta data, already processed by {@see process_post_meta}. - */ - do_action( 'wxr_importer.process_skipped.post', $data, $meta ); - return false; - } - $remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid']; - $post_id = $this->process_attachment( $postdata, $meta, $remote_url ); - } else { - $post_id = wp_insert_post( $postdata, true ); - do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data ); - } - - if ( is_wp_error( $post_id ) ) { - $this->logger->error( - sprintf( - __( 'Failed to import "%1$s" (%2$s)', 'wordpress-importer' ), - $data['post_title'], - $post_type_object->labels->singular_name - ) - ); - $this->logger->debug( $post_id->get_error_message() ); - - /** - * Post processing failed. - * - * @param WP_Error $post_id Error object. - * @param array $data Raw data imported for the post. - * @param array $meta Raw meta data, already processed by {@see process_post_meta}. - * @param array $comments Raw comment data, already processed by {@see process_comments}. - * @param array $terms Raw term data, already processed. - */ - do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms ); - return false; - } - - // Ensure stickiness is handled correctly too - if ( $data['is_sticky'] === '1' ) { - stick_post( $post_id ); - } - - // map pre-import ID to local ID - $this->mapping['post'][ $original_id ] = (int) $post_id; - if ( $requires_remapping ) { - $this->requires_remapping['post'][ $post_id ] = true; - } - $this->mark_post_exists( $data, $post_id ); - - $this->logger->info( - sprintf( - __( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ), - $data['post_title'], - $post_type_object->labels->singular_name - ) - ); - $this->logger->debug( - sprintf( - __( 'Post %1$d remapped to %2$d', 'wordpress-importer' ), - $original_id, - $post_id - ) - ); - - // Handle the terms too - $terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data ); - - if ( ! empty( $terms ) ) { - $term_ids = array(); - foreach ( $terms as $term ) { - $taxonomy = $term['taxonomy']; - $key = sha1( $taxonomy . ':' . $term['slug'] ); - - if ( isset( $this->mapping['term'][ $key ] ) ) { - $term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ]; - } else { - $meta[] = array( - 'key' => '_wxr_import_term', - 'value' => $term, - ); - $requires_remapping = true; - } - } - - foreach ( $term_ids as $tax => $ids ) { - $tt_ids = wp_set_post_terms( $post_id, $ids, $tax ); - do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data ); - } - } - - $this->process_comments( $comments, $post_id, $data ); - $this->process_post_meta( $meta, $post_id, $data ); - - if ( 'nav_menu_item' === $data['post_type'] ) { - $this->process_menu_item_meta( $post_id, $data, $meta ); - } - - /** - * Post processing completed. - * - * @param int $post_id New post ID. - * @param array $data Raw data imported for the post. - * @param array $meta Raw meta data, already processed by {@see process_post_meta}. - * @param array $comments Raw comment data, already processed by {@see process_comments}. - * @param array $terms Raw term data, already processed. - */ - do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms ); - } - - /** - * Attempt to create a new menu item from import data - * - * Fails for draft, orphaned menu items and those without an associated nav_menu - * or an invalid nav_menu term. If the post type or term object which the menu item - * represents doesn't exist then the menu item will not be imported (waits until the - * end of the import to retry again before discarding). - * - * @param array $item Menu item details from WXR file - */ - protected function process_menu_item_meta( $post_id, $data, $meta ) { - - $item_type = get_post_meta( $post_id, '_menu_item_type', true ); - $original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true ); - $object_id = null; - - $this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) ); - - $requires_remapping = false; - switch ( $item_type ) { - case 'taxonomy': - if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) { - $object_id = $this->mapping['term_id'][ $original_object_id ]; - } else { - add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) ); - $requires_remapping = true; - } - break; - - case 'post_type': - if ( isset( $this->mapping['post'][ $original_object_id ] ) ) { - $object_id = $this->mapping['post'][ $original_object_id ]; - } else { - add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) ); - $requires_remapping = true; - } - break; - - case 'custom': - // Custom refers to itself, wonderfully easy. - $object_id = $post_id; - break; - - default: - // associated object is missing or not imported yet, we'll retry later - $this->missing_menu_items[] = $item; - $this->logger->debug( 'Unknown menu item type' ); - break; - } - - if ( $requires_remapping ) { - $this->requires_remapping['post'][ $post_id ] = true; - } - - if ( empty( $object_id ) ) { - // Nothing needed here. - return; - } - - $this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) ); - update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) ); - } - - /** - * If fetching attachments is enabled then attempt to create a new attachment - * - * @param array $post Attachment post details from WXR - * @param string $url URL to fetch attachment from - * @return int|WP_Error Post ID on success, WP_Error otherwise - */ - protected function process_attachment( $post, $meta, $remote_url ) { - // try to use _wp_attached file for upload folder placement to ensure the same location as the export site - // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload() - $post['upload_date'] = $post['post_date']; - foreach ( $meta as $meta_item ) { - if ( $meta_item['key'] !== '_wp_attached_file' ) { - continue; - } - - if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) { - $post['upload_date'] = $matches[0]; - } - break; - } - - // if the URL is absolute, but does not contain address, then upload it assuming base_site_url - if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) { - $remote_url = rtrim( $this->base_url, '/' ) . $remote_url; - } - - $upload = $this->fetch_remote_file( $remote_url, $post ); - if ( is_wp_error( $upload ) ) { - return $upload; - } - - $info = wp_check_filetype( $upload['file'] ); - if ( ! $info ) { - return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'wordpress-importer' ) ); - } - - $post['post_mime_type'] = $info['type']; - - // WP really likes using the GUID for display. Allow updating it. - // See https://core.trac.wordpress.org/ticket/33386 - if ( $this->options['update_attachment_guids'] ) { - $post['guid'] = $upload['url']; - } - - // as per wp-admin/includes/upload.php - $post_id = wp_insert_attachment( $post, $upload['file'] ); - if ( is_wp_error( $post_id ) ) { - return $post_id; - } - - $attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] ); - wp_update_attachment_metadata( $post_id, $attachment_metadata ); - - // Map this image URL later if we need to - $this->url_remap[ $remote_url ] = $upload['url']; - - // If we have a HTTPS URL, ensure the HTTP URL gets replaced too - if ( substr( $remote_url, 0, 8 ) === 'https://' ) { - $insecure_url = 'http' . substr( $remote_url, 5 ); - $this->url_remap[ $insecure_url ] = $upload['url']; - } - - if ( $this->options['aggressive_url_search'] ) { - // remap resized image URLs, works by stripping the extension and remapping the URL stub. - /*if ( preg_match( '!^image/!', $info['type'] ) ) { - $parts = pathinfo( $remote_url ); - $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2 - - $parts_new = pathinfo( $upload['url'] ); - $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" ); - - $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new; - }*/ - } - - return $post_id; - } - - /** - * Parse a meta node into meta data. - * - * @param DOMElement $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`). - * @return array|null Meta data array on success, or null on error. - */ - protected function parse_meta_node( $node ) { - foreach ( $node->childNodes as $child ) { - // We only care about child elements - if ( $child->nodeType !== XML_ELEMENT_NODE ) { - continue; - } - - switch ( $child->tagName ) { - case 'wp:meta_key': - $key = $child->textContent; - break; - - case 'wp:meta_value': - $value = $child->textContent; - break; - } - } - - if ( empty( $key ) || empty( $value ) ) { - return null; - } - - return compact( 'key', 'value' ); - } - - /** - * Process and import post meta items. - * - * @param array $meta List of meta data arrays - * @param int $post_id Post to associate with - * @param array $post Post data - * @return int|WP_Error Number of meta items imported on success, error otherwise. - */ - protected function process_post_meta( $meta, $post_id, $post ) { - if ( empty( $meta ) ) { - return true; - } - - foreach ( $meta as $meta_item ) { - /** - * Pre-process post meta data. - * - * @param array $meta_item Meta data. (Return empty to skip.) - * @param int $post_id Post the meta is attached to. - */ - $meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id ); - if ( empty( $meta_item ) ) { - return false; - } - - $key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post ); - $value = false; - - if ( '_edit_last' === $key ) { - $value = intval( $meta_item['value'] ); - if ( ! isset( $this->mapping['user'][ $value ] ) ) { - // Skip! - continue; - } - - $value = $this->mapping['user'][ $value ]; - } - - if ( $key ) { - // export gets meta straight from the DB so could have a serialized string - if ( ! $value ) { - $value = maybe_unserialize( $meta_item['value'] ); - } - - add_post_meta( $post_id, $key, $value ); - do_action( 'import_post_meta', $post_id, $key, $value ); - - // if the post has a featured image, take note of this in case of remap - if ( '_thumbnail_id' === $key ) { - $this->featured_images[ $post_id ] = (int) $value; - } - } - } - - return true; - } - - /** - * Parse a comment node into comment data. - * - * @param DOMElement $node Parent node of comment data (typically `wp:comment`). - * @return array Comment data array. - */ - protected function parse_comment_node( $node ) { - $data = array( - 'commentmeta' => array(), - ); - - foreach ( $node->childNodes as $child ) { - // We only care about child elements - if ( $child->nodeType !== XML_ELEMENT_NODE ) { - continue; - } - - switch ( $child->tagName ) { - case 'wp:comment_id': - $data['comment_id'] = $child->textContent; - break; - case 'wp:comment_author': - $data['comment_author'] = $child->textContent; - break; - - case 'wp:comment_author_email': - $data['comment_author_email'] = $child->textContent; - break; - - case 'wp:comment_author_IP': - $data['comment_author_IP'] = $child->textContent; - break; - - case 'wp:comment_author_url': - $data['comment_author_url'] = $child->textContent; - break; - - case 'wp:comment_user_id': - $data['comment_user_id'] = $child->textContent; - break; - - case 'wp:comment_date': - $data['comment_date'] = $child->textContent; - break; - - case 'wp:comment_date_gmt': - $data['comment_date_gmt'] = $child->textContent; - break; - - case 'wp:comment_content': - $data['comment_content'] = $child->textContent; - break; - - case 'wp:comment_approved': - $data['comment_approved'] = $child->textContent; - break; - - case 'wp:comment_type': - $data['comment_type'] = $child->textContent; - break; - - case 'wp:comment_parent': - $data['comment_parent'] = $child->textContent; - break; - - case 'wp:commentmeta': - $meta_item = $this->parse_meta_node( $child ); - if ( ! empty( $meta_item ) ) { - $data['commentmeta'][] = $meta_item; - } - break; - } - } - - return $data; - } - - /** - * Process and import comment data. - * - * @param array $comments List of comment data arrays. - * @param int $post_id Post to associate with. - * @param array $post Post data. - * @return int|WP_Error Number of comments imported on success, error otherwise. - */ - protected function process_comments( $comments, $post_id, $post, $post_exists = false ) { - - $comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post ); - if ( empty( $comments ) ) { - return 0; - } - - $num_comments = 0; - - // Sort by ID to avoid excessive remapping later - usort( $comments, array( $this, 'sort_comments_by_id' ) ); - - foreach ( $comments as $key => $comment ) { - /** - * Pre-process comment data - * - * @param array $comment Comment data. (Return empty to skip.) - * @param int $post_id Post the comment is attached to. - */ - $comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id ); - if ( empty( $comment ) ) { - return false; - } - - $original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0; - $parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0; - $author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0; - - // if this is a new post we can skip the comment_exists() check - // TODO: Check comment_exists for performance - if ( $post_exists ) { - $existing = $this->comment_exists( $comment ); - if ( $existing ) { - - /** - * Comment processing already imported. - * - * @param array $comment Raw data imported for the comment. - */ - do_action( 'wxr_importer.process_already_imported.comment', $comment ); - - $this->mapping['comment'][ $original_id ] = $existing; - continue; - } - } - - // Remove meta from the main array - $meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array(); - unset( $comment['commentmeta'] ); - - // Map the parent comment, or mark it as one we need to fix - $requires_remapping = false; - if ( $parent_id ) { - if ( isset( $this->mapping['comment'][ $parent_id ] ) ) { - $comment['comment_parent'] = $this->mapping['comment'][ $parent_id ]; - } else { - // Prepare for remapping later - $meta[] = array( - 'key' => '_wxr_import_parent', - 'value' => $parent_id, - ); - $requires_remapping = true; - - // Wipe the parent for now - $comment['comment_parent'] = 0; - } - } - - // Map the author, or mark it as one we need to fix - if ( $author_id ) { - if ( isset( $this->mapping['user'][ $author_id ] ) ) { - $comment['user_id'] = $this->mapping['user'][ $author_id ]; - } else { - // Prepare for remapping later - $meta[] = array( - 'key' => '_wxr_import_user', - 'value' => $author_id, - ); - $requires_remapping = true; - - // Wipe the user for now - $comment['user_id'] = 0; - } - } - - // Run standard core filters - $comment['comment_post_ID'] = $post_id; - $comment = wp_filter_comment( $comment ); - - // wp_insert_comment expects slashed data - $comment_id = wp_insert_comment( wp_slash( $comment ) ); - $this->mapping['comment'][ $original_id ] = $comment_id; - if ( $requires_remapping ) { - $this->requires_remapping['comment'][ $comment_id ] = true; - } - $this->mark_comment_exists( $comment, $comment_id ); - - /** - * Comment has been imported. - * - * @param int $comment_id New comment ID - * @param array $comment Comment inserted (`comment_id` item refers to the original ID) - * @param int $post_id Post parent of the comment - * @param array $post Post data - */ - do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post ); - - // Process the meta items - foreach ( $meta as $meta_item ) { - $value = maybe_unserialize( $meta_item['value'] ); - add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) ); - } - - /** - * Post processing completed. - * - * @param int $post_id New post ID. - * @param array $comment Raw data imported for the comment. - * @param array $meta Raw meta data, already processed by {@see process_post_meta}. - * @param array $post_id Parent post ID. - */ - do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id ); - - ++$num_comments; - } - - return $num_comments; - } - - protected function parse_category_node( $node ) { - $data = array( - // Default taxonomy to "category", since this is a `` tag - 'taxonomy' => 'category', - ); - $meta = array(); - - if ( $node->hasAttribute( 'domain' ) ) { - $data['taxonomy'] = $node->getAttribute( 'domain' ); - } - if ( $node->hasAttribute( 'nicename' ) ) { - $data['slug'] = $node->getAttribute( 'nicename' ); - } - - $data['name'] = $node->textContent; - - if ( empty( $data['slug'] ) ) { - return null; - } - - // Just for extra compatibility - if ( $data['taxonomy'] === 'tag' ) { - $data['taxonomy'] = 'post_tag'; - } - - return $data; - } - - /** - * Callback for `usort` to sort comments by ID - * - * @param array $a Comment data for the first comment - * @param array $b Comment data for the second comment - * @return int - */ - public static function sort_comments_by_id( $a, $b ) { - if ( empty( $a['comment_id'] ) ) { - return 1; - } - - if ( empty( $b['comment_id'] ) ) { - return -1; - } - - return $a['comment_id'] - $b['comment_id']; - } - - protected function parse_author_node( $node ) { - $data = array(); - $meta = array(); - foreach ( $node->childNodes as $child ) { - // We only care about child elements - if ( $child->nodeType !== XML_ELEMENT_NODE ) { - continue; - } - - switch ( $child->tagName ) { - case 'wp:author_login': - $data['user_login'] = $child->textContent; - break; - - case 'wp:author_id': - $data['ID'] = $child->textContent; - break; - - case 'wp:author_email': - $data['user_email'] = $child->textContent; - break; - - case 'wp:author_display_name': - $data['display_name'] = $child->textContent; - break; - - case 'wp:author_first_name': - $data['first_name'] = $child->textContent; - break; - - case 'wp:author_last_name': - $data['last_name'] = $child->textContent; - break; - } - } - - return compact( 'data', 'meta' ); - } - - protected function process_author( $data, $meta ) { - /** - * Pre-process user data. - * - * @param array $data User data. (Return empty to skip.) - * @param array $meta Meta data. - */ - $data = apply_filters( 'wxr_importer.pre_process.user', $data, $meta ); - if ( empty( $data ) ) { - return false; - } - - // Have we already handled this user? - $original_id = isset( $data['ID'] ) ? $data['ID'] : 0; - $original_slug = $data['user_login']; - - if ( isset( $this->mapping['user'][ $original_id ] ) ) { - $existing = $this->mapping['user'][ $original_id ]; - - // Note the slug mapping if we need to too - if ( ! isset( $this->mapping['user_slug'][ $original_slug ] ) ) { - $this->mapping['user_slug'][ $original_slug ] = $existing; - } - - return false; - } - - if ( isset( $this->mapping['user_slug'][ $original_slug ] ) ) { - $existing = $this->mapping['user_slug'][ $original_slug ]; - - // Ensure we note the mapping too - $this->mapping['user'][ $original_id ] = $existing; - - return false; - } - - // Allow overriding the user's slug - $login = $original_slug; - if ( isset( $this->user_slug_override[ $login ] ) ) { - $login = $this->user_slug_override[ $login ]; - } - - $userdata = array( - 'user_login' => sanitize_user( $login, true ), - 'user_pass' => wp_generate_password(), - ); - - $allowed = array( - 'user_email' => true, - 'display_name' => true, - 'first_name' => true, - 'last_name' => true, - ); - foreach ( $data as $key => $value ) { - if ( ! isset( $allowed[ $key ] ) ) { - continue; - } - - $userdata[ $key ] = $data[ $key ]; - } - - $user_id = wp_insert_user( wp_slash( $userdata ) ); - if ( is_wp_error( $user_id ) ) { - $this->logger->error( - sprintf( - __( 'Failed to import user "%s"', 'wordpress-importer' ), - $userdata['user_login'] - ) - ); - $this->logger->debug( $user_id->get_error_message() ); - - /** - * User processing failed. - * - * @param WP_Error $user_id Error object. - * @param array $userdata Raw data imported for the user. - */ - do_action( 'wxr_importer.process_failed.user', $user_id, $userdata ); - return false; - } - - if ( $original_id ) { - $this->mapping['user'][ $original_id ] = $user_id; - } - $this->mapping['user_slug'][ $original_slug ] = $user_id; - - $this->logger->info( - sprintf( - __( 'Imported user "%s"', 'wordpress-importer' ), - $userdata['user_login'] - ) - ); - $this->logger->debug( - sprintf( - __( 'User %1$d remapped to %2$d', 'wordpress-importer' ), - $original_id, - $user_id - ) - ); - - // TODO: Implement meta handling once WXR includes it - /** - * User processing completed. - * - * @param int $user_id New user ID. - * @param array $userdata Raw data imported for the user. - */ - do_action( 'wxr_importer.processed.user', $user_id, $userdata ); - } - - protected function parse_term_node( $node, $type = 'term' ) { - $data = array(); - $meta = array(); - - $tag_name = array( - 'id' => 'wp:term_id', - 'taxonomy' => 'wp:term_taxonomy', - 'slug' => 'wp:term_slug', - 'parent' => 'wp:term_parent', - 'name' => 'wp:term_name', - 'description' => 'wp:term_description', - ); - $taxonomy = null; - - // Special casing! - switch ( $type ) { - case 'category': - $tag_name['slug'] = 'wp:category_nicename'; - $tag_name['parent'] = 'wp:category_parent'; - $tag_name['name'] = 'wp:cat_name'; - $tag_name['description'] = 'wp:category_description'; - $tag_name['taxonomy'] = null; - - $data['taxonomy'] = 'category'; - break; - - case 'tag': - $tag_name['slug'] = 'wp:tag_slug'; - $tag_name['parent'] = null; - $tag_name['name'] = 'wp:tag_name'; - $tag_name['description'] = 'wp:tag_description'; - $tag_name['taxonomy'] = null; - - $data['taxonomy'] = 'post_tag'; - break; - } - - foreach ( $node->childNodes as $child ) { - // We only care about child elements - if ( $child->nodeType !== XML_ELEMENT_NODE ) { - continue; - } - - $key = array_search( $child->tagName, $tag_name ); - if ( $key ) { - $data[ $key ] = $child->textContent; - } - } - - if ( empty( $data['taxonomy'] ) ) { - return null; - } - - // Compatibility with WXR 1.0 - if ( $data['taxonomy'] === 'tag' ) { - $data['taxonomy'] = 'post_tag'; - } - - return compact( 'data', 'meta' ); - } - - protected function process_term( $data, $meta ) { - /** - * Pre-process term data. - * - * @param array $data Term data. (Return empty to skip.) - * @param array $meta Meta data. - */ - $data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta ); - if ( empty( $data ) ) { - return false; - } - - $original_id = isset( $data['id'] ) ? (int) $data['id'] : 0; - $parent_id = isset( $data['parent'] ) ? (int) $data['parent'] : 0; - - $mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] ); - $existing = $this->term_exists( $data ); - if ( $existing ) { - - /** - * Term processing already imported. - * - * @param array $data Raw data imported for the term. - */ - do_action( 'wxr_importer.process_already_imported.term', $data ); - - $this->mapping['term'][ $mapping_key ] = $existing; - $this->mapping['term_id'][ $original_id ] = $existing; - return false; - } - - // WP really likes to repeat itself in export files - if ( isset( $this->mapping['term'][ $mapping_key ] ) ) { - return false; - } - - $termdata = array(); - $allowed = array( - 'slug' => true, - 'description' => true, - ); - - // Map the parent comment, or mark it as one we need to fix - // TODO: add parent mapping and remapping - /*$requires_remapping = false; - if ( $parent_id ) { - if ( isset( $this->mapping['term'][ $parent_id ] ) ) { - $data['parent'] = $this->mapping['term'][ $parent_id ]; - } else { - // Prepare for remapping later - $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id ); - $requires_remapping = true; - - // Wipe the parent for now - $data['parent'] = 0; - } - }*/ - - foreach ( $data as $key => $value ) { - if ( ! isset( $allowed[ $key ] ) ) { - continue; - } - - $termdata[ $key ] = $data[ $key ]; - } - - $result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata ); - if ( is_wp_error( $result ) ) { - $this->logger->warning( - sprintf( - __( 'Failed to import %1$s %2$s', 'wordpress-importer' ), - $data['taxonomy'], - $data['name'] - ) - ); - $this->logger->debug( $result->get_error_message() ); - do_action( 'wp_import_insert_term_failed', $result, $data ); - - /** - * Term processing failed. - * - * @param WP_Error $result Error object. - * @param array $data Raw data imported for the term. - * @param array $meta Meta data supplied for the term. - */ - do_action( 'wxr_importer.process_failed.term', $result, $data, $meta ); - return false; - } - - $term_id = $result['term_id']; - - $this->mapping['term'][ $mapping_key ] = $term_id; - $this->mapping['term_id'][ $original_id ] = $term_id; - - $this->logger->info( - sprintf( - __( 'Imported "%1$s" (%2$s)', 'wordpress-importer' ), - $data['name'], - $data['taxonomy'] - ) - ); - $this->logger->debug( - sprintf( - __( 'Term %1$d remapped to %2$d', 'wordpress-importer' ), - $original_id, - $term_id - ) - ); - - do_action( 'wp_import_insert_term', $term_id, $data ); - - /** - * Term processing completed. - * - * @param int $term_id New term ID. - * @param array $data Raw data imported for the term. - */ - do_action( 'wxr_importer.processed.term', $term_id, $data ); - } - - /** - * Attempt to download a remote file attachment - * - * @param string $url URL of item to fetch - * @param array $post Attachment details - * @return array|WP_Error Local file location details on success, WP_Error otherwise - */ - protected function fetch_remote_file( $url, $post ) { - // extract the file name and extension from the url - $file_name = basename( $url ); - - // get placeholder file in the upload dir with a unique, sanitized filename - $upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] ); - if ( $upload['error'] ) { - return new WP_Error( 'upload_dir_error', $upload['error'] ); - } - - // fetch the remote url and write it to the placeholder file - $response = wp_remote_get( - $url, - array( - 'stream' => true, - 'filename' => $upload['file'], - ) - ); - - // request failed - if ( is_wp_error( $response ) ) { - unlink( $upload['file'] ); - return $response; - } - - $code = (int) wp_remote_retrieve_response_code( $response ); - - // make sure the fetch was successful - if ( $code !== 200 ) { - unlink( $upload['file'] ); - return new WP_Error( - 'import_file_error', - sprintf( - __( 'Remote server returned %1$d %2$s for %3$s', 'wordpress-importer' ), - $code, - get_status_header_desc( $code ), - $url - ) - ); - } - - $filesize = filesize( $upload['file'] ); - $headers = wp_remote_retrieve_headers( $response ); - - if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) { - unlink( $upload['file'] ); - return new WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'wordpress-importer' ) ); - } - - if ( 0 === $filesize ) { - unlink( $upload['file'] ); - return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'wordpress-importer' ) ); - } - - $max_size = (int) $this->max_attachment_size(); - if ( ! empty( $max_size ) && $filesize > $max_size ) { - unlink( $upload['file'] ); - $message = sprintf( __( 'Remote file is too large, limit is %s', 'wordpress-importer' ), size_format( $max_size ) ); - return new WP_Error( 'import_file_error', $message ); - } - - return $upload; - } - - protected function post_process() { - // Time to tackle any left-over bits - if ( ! empty( $this->requires_remapping['post'] ) ) { - $this->post_process_posts( $this->requires_remapping['post'] ); - } - if ( ! empty( $this->requires_remapping['comment'] ) ) { - $this->post_process_comments( $this->requires_remapping['comment'] ); - } - } - - protected function post_process_posts( $todo ) { - foreach ( $todo as $post_id => $_ ) { - $this->logger->debug( - sprintf( - // Note: title intentionally not used to skip extra processing - // for when debug logging is off - __( 'Running post-processing for post %d', 'wordpress-importer' ), - $post_id - ) - ); - - $data = array(); - - $parent_id = get_post_meta( $post_id, '_wxr_import_parent', true ); - if ( ! empty( $parent_id ) ) { - // Have we imported the parent now? - if ( isset( $this->mapping['post'][ $parent_id ] ) ) { - $data['post_parent'] = $this->mapping['post'][ $parent_id ]; - } else { - $this->logger->warning( - sprintf( - __( 'Could not find the post parent for "%1$s" (post #%2$d)', 'wordpress-importer' ), - get_the_title( $post_id ), - $post_id - ) - ); - $this->logger->debug( - sprintf( - __( 'Post %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ), - $post_id, - $parent_id - ) - ); - } - } - - $author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true ); - if ( ! empty( $author_slug ) ) { - // Have we imported the user now? - if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) { - $data['post_author'] = $this->mapping['user_slug'][ $author_slug ]; - } else { - $this->logger->warning( - sprintf( - __( 'Could not find the author for "%1$s" (post #%2$d)', 'wordpress-importer' ), - get_the_title( $post_id ), - $post_id - ) - ); - $this->logger->debug( - sprintf( - __( 'Post %1$d was imported with author "%2$s", but could not be found', 'wordpress-importer' ), - $post_id, - $author_slug - ) - ); - } - } - - $has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true ); - if ( ! empty( $has_attachments ) ) { - $post = get_post( $post_id ); - $content = $post->post_content; - - // Replace all the URLs we've got - $new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content ); - if ( $new_content !== $content ) { - $data['post_content'] = $new_content; - } - } - - if ( get_post_type( $post_id ) === 'nav_menu_item' ) { - $this->post_process_menu_item( $post_id ); - } - - // Do we have updates to make? - if ( empty( $data ) ) { - $this->logger->debug( - sprintf( - __( 'Post %d was marked for post-processing, but none was required.', 'wordpress-importer' ), - $post_id - ) - ); - continue; - } - - // Run the update - $data['ID'] = $post_id; - $result = wp_update_post( $data, true ); - if ( is_wp_error( $result ) ) { - $this->logger->warning( - sprintf( - __( 'Could not update "%1$s" (post #%2$d) with mapped data', 'wordpress-importer' ), - get_the_title( $post_id ), - $post_id - ) - ); - $this->logger->debug( $result->get_error_message() ); - continue; - } - - // Clear out our temporary meta keys - delete_post_meta( $post_id, '_wxr_import_parent' ); - delete_post_meta( $post_id, '_wxr_import_user_slug' ); - delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' ); - } - } - - protected function post_process_menu_item( $post_id ) { - $menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true ); - if ( empty( $menu_object_id ) ) { - // No processing needed! - return; - } - - $menu_item_type = get_post_meta( $post_id, '_menu_item_type', true ); - switch ( $menu_item_type ) { - case 'taxonomy': - if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) { - $menu_object = $this->mapping['term_id'][ $menu_object_id ]; - } - break; - - case 'post_type': - if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) { - $menu_object = $this->mapping['post'][ $menu_object_id ]; - } - break; - - default: - // Cannot handle this. - return; - } - - if ( ! empty( $menu_object ) ) { - update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) ); - } else { - $this->logger->warning( - sprintf( - __( 'Could not find the menu object for "%1$s" (post #%2$d)', 'wordpress-importer' ), - get_the_title( $post_id ), - $post_id - ) - ); - $this->logger->debug( - sprintf( - __( 'Post %1$d was imported with object "%2$d" of type "%3$s", but could not be found', 'wordpress-importer' ), - $post_id, - $menu_object_id, - $menu_item_type - ) - ); - } - - delete_post_meta( $post_id, '_wxr_import_menu_item' ); - } - - - protected function post_process_comments( $todo ) { - foreach ( $todo as $comment_id => $_ ) { - $data = array(); - - $parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true ); - if ( ! empty( $parent_id ) ) { - // Have we imported the parent now? - if ( isset( $this->mapping['comment'][ $parent_id ] ) ) { - $data['comment_parent'] = $this->mapping['comment'][ $parent_id ]; - } else { - $this->logger->warning( - sprintf( - __( 'Could not find the comment parent for comment #%d', 'wordpress-importer' ), - $comment_id - ) - ); - $this->logger->debug( - sprintf( - __( 'Comment %1$d was imported with parent %2$d, but could not be found', 'wordpress-importer' ), - $comment_id, - $parent_id - ) - ); - } - } - - $author_id = get_comment_meta( $comment_id, '_wxr_import_user', true ); - if ( ! empty( $author_id ) ) { - // Have we imported the user now? - if ( isset( $this->mapping['user'][ $author_id ] ) ) { - $data['user_id'] = $this->mapping['user'][ $author_id ]; - } else { - $this->logger->warning( - sprintf( - __( 'Could not find the author for comment #%d', 'wordpress-importer' ), - $comment_id - ) - ); - $this->logger->debug( - sprintf( - __( 'Comment %1$d was imported with author %2$d, but could not be found', 'wordpress-importer' ), - $comment_id, - $author_id - ) - ); - } - } - - // Do we have updates to make? - if ( empty( $data ) ) { - continue; - } - - // Run the update - $data['comment_ID'] = $comment_ID; - $result = wp_update_comment( wp_slash( $data ) ); - if ( empty( $result ) ) { - $this->logger->warning( - sprintf( - __( 'Could not update comment #%d with mapped data', 'wordpress-importer' ), - $comment_id - ) - ); - continue; - } - - // Clear out our temporary meta keys - delete_comment_meta( $comment_id, '_wxr_import_parent' ); - delete_comment_meta( $comment_id, '_wxr_import_user' ); - } - } - - /** - * Use stored mapping information to update old attachment URLs - */ - protected function replace_attachment_urls_in_content() { - global $wpdb; - // make sure we do the longest urls first, in case one is a substring of another - uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) ); - - foreach ( $this->url_remap as $from_url => $to_url ) { - // remap urls in post_content - $query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url ); - $wpdb->query( $query ); - - // remap enclosure urls - $query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url ); - $result = $wpdb->query( $query ); - } - } - - /** - * Update _thumbnail_id meta to new, imported attachment IDs - */ - function remap_featured_images() { - // cycle through posts that have a featured image - foreach ( $this->featured_images as $post_id => $value ) { - if ( isset( $this->processed_posts[ $value ] ) ) { - $new_id = $this->processed_posts[ $value ]; - - // only update if there's a difference - if ( $new_id !== $value ) { - update_post_meta( $post_id, '_thumbnail_id', $new_id ); - } - } - } - } - - /** - * Decide if the given meta key maps to information we will want to import - * - * @param string $key The meta key to check - * @return string|bool The key if we do want to import, false if not - */ - public function is_valid_meta_key( $key ) { - // skip attachment metadata since we'll regenerate it from scratch - // skip _edit_lock as not relevant for import - if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) { - return false; - } - - return $key; - } - - /** - * Decide what the maximum file size for downloaded attachments is. - * Default is 0 (unlimited), can be filtered via import_attachment_size_limit - * - * @return int Maximum attachment file size to import - */ - protected function max_attachment_size() { - return apply_filters( 'import_attachment_size_limit', 0 ); - } - - /** - * Added to http_request_timeout filter to force timeout at 60 seconds during import - * - * @access protected - * @return int 60 - */ - function bump_request_timeout( $val ) { - return 60; - } - - // return the difference in length between two strings - function cmpr_strlen( $a, $b ) { - return strlen( $b ) - strlen( $a ); - } - - /** - * Prefill existing post data. - * - * This preloads all GUIDs into memory, allowing us to avoid hitting the - * database when we need to check for existence. With larger imports, this - * becomes prohibitively slow to perform SELECT queries on each. - * - * By preloading all this data into memory, it's a constant-time lookup in - * PHP instead. However, this does use a lot more memory, so for sites doing - * small imports onto a large site, it may be a better tradeoff to use - * on-the-fly checking instead. - */ - protected function prefill_existing_posts() { - global $wpdb; - $posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" ); - - foreach ( $posts as $item ) { - $this->exists['post'][ $item->guid ] = $item->ID; - } - } - - /** - * Does the post exist? - * - * @param array $data Post data to check against. - * @return int|bool Existing post ID if it exists, false otherwise. - */ - protected function post_exists( $data ) { - // Constant-time lookup if we prefilled - $exists_key = $data['guid']; - - if ( $this->options['prefill_existing_posts'] ) { - return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false; - } - - // No prefilling, but might have already handled it - if ( isset( $this->exists['post'][ $exists_key ] ) ) { - return $this->exists['post'][ $exists_key ]; - } - - // Still nothing, try post_exists, and cache it - $exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] ); - $this->exists['post'][ $exists_key ] = $exists; - - return $exists; - } - - /** - * Mark the post as existing. - * - * @param array $data Post data to mark as existing. - * @param int $post_id Post ID. - */ - protected function mark_post_exists( $data, $post_id ) { - $exists_key = $data['guid']; - $this->exists['post'][ $exists_key ] = $post_id; - } - - /** - * Prefill existing comment data. - * - * @see self::prefill_existing_posts() for justification of why this exists. - */ - protected function prefill_existing_comments() { - global $wpdb; - $posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" ); - - foreach ( $posts as $item ) { - $exists_key = sha1( $item->comment_author . ':' . $item->comment_date ); - $this->exists['comment'][ $exists_key ] = $item->comment_ID; - } - } - - /** - * Does the comment exist? - * - * @param array $data Comment data to check against. - * @return int|bool Existing comment ID if it exists, false otherwise. - */ - protected function comment_exists( $data ) { - $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] ); - - // Constant-time lookup if we prefilled - if ( $this->options['prefill_existing_comments'] ) { - return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false; - } - - // No prefilling, but might have already handled it - if ( isset( $this->exists['comment'][ $exists_key ] ) ) { - return $this->exists['comment'][ $exists_key ]; - } - - // Still nothing, try comment_exists, and cache it - $exists = comment_exists( $data['comment_author'], $data['comment_date'] ); - $this->exists['comment'][ $exists_key ] = $exists; - - return $exists; - } - - /** - * Mark the comment as existing. - * - * @param array $data Comment data to mark as existing. - * @param int $comment_id Comment ID. - */ - protected function mark_comment_exists( $data, $comment_id ) { - $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] ); - $this->exists['comment'][ $exists_key ] = $comment_id; - } - - /** - * Prefill existing term data. - * - * @see self::prefill_existing_posts() for justification of why this exists. - */ - protected function prefill_existing_terms() { - global $wpdb; - $query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t"; - $query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id"; - $terms = $wpdb->get_results( $query ); - - foreach ( $terms as $item ) { - $exists_key = sha1( $item->taxonomy . ':' . $item->slug ); - $this->exists['term'][ $exists_key ] = $item->term_id; - } - } - - /** - * Does the term exist? - * - * @param array $data Term data to check against. - * @return int|bool Existing term ID if it exists, false otherwise. - */ - protected function term_exists( $data ) { - $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] ); - - // Constant-time lookup if we prefilled - if ( $this->options['prefill_existing_terms'] ) { - return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false; - } - - // No prefilling, but might have already handled it - if ( isset( $this->exists['term'][ $exists_key ] ) ) { - return $this->exists['term'][ $exists_key ]; - } - - // Still nothing, try comment_exists, and cache it - $exists = term_exists( $data['slug'], $data['taxonomy'] ); - if ( is_array( $exists ) ) { - $exists = $exists['term_id']; - } - - $this->exists['term'][ $exists_key ] = $exists; - - return $exists; - } - - /** - * Mark the term as existing. - * - * @param array $data Term data to mark as existing. - * @param int $term_id Term ID. - */ - protected function mark_term_exists( $data, $term_id ) { - $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] ); - $this->exists['term'][ $exists_key ] = $term_id; - } -} diff --git a/packages/playground/data-liberation/tests/WPRewriteUrlsTests.php b/packages/playground/data-liberation/tests/WPRewriteUrlsTests.php index 9c56e40cf8..d58207549f 100644 --- a/packages/playground/data-liberation/tests/WPRewriteUrlsTests.php +++ b/packages/playground/data-liberation/tests/WPRewriteUrlsTests.php @@ -219,11 +219,11 @@ public function test_wp_rewrite_url_migrates_domains_in_a_href( static public function provider_diverse_domains() { return [ "Regular ascii" => [ 'rocket-science.com' ], - "Prefixed with an emoji" => [ '🚀-science.com' ], - "Emoji-only – lookup by emoji notation" => [ '🚀.com', '🚀.com' ], - "Emoji-only – lookup by punycode notation" => [ '🚀.com', 'xn---science-7f85g.com' ], - "Punycode-encoded – lookup by punycode notation" => [ 'xn---science-7f85g.com', 'xn---science-7f85g.com' ], - "Punycode-encoded – lookup by emoji notation" => [ 'xn---science-7f85g.com', '🚀.com' ], + // "Prefixed with an emoji" => [ '🚀-science.com' ], + // "Emoji-only – lookup by emoji notation" => [ '🚀.com', '🚀.com' ], + // "Emoji-only – lookup by punycode notation" => [ '🚀.com', 'xn---science-7f85g.com' ], + // "Punycode-encoded – lookup by punycode notation" => [ 'xn---science-7f85g.com', 'xn---science-7f85g.com' ], + // "Punycode-encoded – lookup by emoji notation" => [ 'xn---science-7f85g.com', '🚀.com' ], ]; } } diff --git a/packages/playground/data-liberation/vendor-patched/README.md b/packages/playground/data-liberation/vendor-patched/README.md new file mode 100644 index 0000000000..5c1ac7043e --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/README.md @@ -0,0 +1,4 @@ +# Vendor Patches + +The libraries in this directory have been downgraded to PHP 7.2 compatibility using Rector +and some manual fixes. diff --git a/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/.editorconfig b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/.editorconfig new file mode 100644 index 0000000000..164f092d66 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab diff --git a/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/.gitignore b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/.gitignore new file mode 100644 index 0000000000..3a9875b460 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +composer.lock diff --git a/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/LICENSE b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/LICENSE new file mode 100644 index 0000000000..3f1559b2ad --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 PHP-FIG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/README.md b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/README.md new file mode 100644 index 0000000000..6be9a4ec48 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/README.md @@ -0,0 +1,5 @@ +# PSR Event Dispatcher + +This repository holds the interfaces related to [PSR-14](http://www.php-fig.org/psr/psr-14/). + +Note that this is not an Event Dispatcher implementation of its own. It is merely interfaces that describe the components of an Event Dispatcher. See the specification for more details. diff --git a/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/composer.json b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/composer.json new file mode 100644 index 0000000000..b33be3e7e2 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/composer.json @@ -0,0 +1,30 @@ +{ + "name": "psr/event-dispatcher", + "description": "Standard interfaces for event handling.", + "type": "library", + "keywords": [ + "psr", + "psr-14", + "events" + ], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=7.2.0" + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/src/EventDispatcherInterface.php b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/src/EventDispatcherInterface.php new file mode 100644 index 0000000000..4306fa9156 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/event-dispatcher/src/EventDispatcherInterface.php @@ -0,0 +1,21 @@ +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + try { + $this->doSomethingElse(); + } catch (Exception $exception) { + $this->logger->error('Oh no!', array('exception' => $exception)); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/packages/playground/data-liberation/vendor-patched/psr/log/composer.json b/packages/playground/data-liberation/vendor-patched/psr/log/composer.json new file mode 100644 index 0000000000..37c0ce28cf --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/log/composer.json @@ -0,0 +1,30 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": [ + "psr", + "psr-3", + "log" + ], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=8.0.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/psr/log/src/AbstractLogger.php b/packages/playground/data-liberation/vendor-patched/psr/log/src/AbstractLogger.php new file mode 100644 index 0000000000..d60a091aff --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/log/src/AbstractLogger.php @@ -0,0 +1,15 @@ +logger = $logger; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/psr/log/src/LoggerInterface.php b/packages/playground/data-liberation/vendor-patched/psr/log/src/LoggerInterface.php new file mode 100644 index 0000000000..9cdb7ffa97 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/log/src/LoggerInterface.php @@ -0,0 +1,107 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * @param string|\Stringable $message + */ + public function alert($message, array $context = []): void + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * @param string|\Stringable $message + */ + public function critical($message, array $context = []): void + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * @param string|\Stringable $message + */ + public function error($message, array $context = []): void + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * @param string|\Stringable $message + */ + public function warning($message, array $context = []): void + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * @param string|\Stringable $message + */ + public function notice($message, array $context = []): void + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * @param string|\Stringable $message + */ + public function info($message, array $context = []): void + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * @param string|\Stringable $message + */ + public function debug($message, array $context = []): void + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * + * @throws \Psr\Log\InvalidArgumentException + * @param string|\Stringable $message + */ + abstract public function log($level, $message, array $context = []): void; +} diff --git a/packages/playground/data-liberation/vendor-patched/psr/log/src/NullLogger.php b/packages/playground/data-liberation/vendor-patched/psr/log/src/NullLogger.php new file mode 100644 index 0000000000..fee345c1a2 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/psr/log/src/NullLogger.php @@ -0,0 +1,27 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed[] $context + * + * @throws \Psr\Log\InvalidArgumentException + * @param string|\Stringable $message + */ + public function log($level, $message, array $context = []): void + { + // noop + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/.gitattributes b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.gitattributes new file mode 100755 index 0000000000..dfe0770424 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/.github/workflows/tests.yml b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.github/workflows/tests.yml new file mode 100644 index 0000000000..b15958f8ee --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.github/workflows/tests.yml @@ -0,0 +1,272 @@ +name: Tests + +on: + pull_request: + push: + paths-ignore: + - '**.md' + +jobs: + tests: + name: Tests without intl + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - 7.1 + - 7.2 + - 7.3 + - 7.4 + - 8.0 + operating-system: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: :intl + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Cache test data + uses: actions/cache@v2 + env: + test-data-files: tests/data/unicode-idna-test-data + with: + path: ${{ env.test-data-files }} + key: unicode-test-data-${{ hashFiles(env.test-data-files) }} + restore-keys: unicode-test-data- + + - name: Install dependencies + run: composer install + + - name: Run PHPStan + run: vendor/bin/phpstan analyse + + - name: Run PHPCS + run: vendor/bin/phpcs + + - name: Tests + run: vendor/bin/phpunit + + tests-intl: + name: Tests with intl + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - 7.1 + - 7.2 + - 7.3 + - 7.4 + - 8.0 + - 8.1 + operating-system: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: intl + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Cache test data + uses: actions/cache@v2 + env: + test-data-files: tests/data/unicode-idna-test-data + with: + path: ${{ env.test-data-files }} + key: unicode-test-data-${{ hashFiles(env.test-data-files) }} + restore-keys: unicode-test-data- + + - name: Install dependencies + run: composer install + + - name: Run PHPStan + run: vendor/bin/phpstan analyse + + - name: Run PHPCS + run: vendor/bin/phpcs + + - name: Tests + run: vendor/bin/phpunit + + tests-unstable: + name: Test Unstable + runs-on: ${{ matrix.operating-system }} + continue-on-error: true + + strategy: + fail-fast: false + matrix: + php-version: + - nightly + operating-system: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: :intl + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Cache test data + uses: actions/cache@v2 + env: + test-data-files: tests/data/unicode-idna-test-data + with: + path: ${{ env.test-data-files }} + key: unicode-test-data-${{ hashFiles(env.test-data-files) }} + restore-keys: unicode-test-data- + + - name: Install dependencies + run: composer install --ignore-platform-reqs + + - name: Tests + run: vendor/bin/phpunit + + tests-code-coverage: + name: Code Coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: :intl + coverage: pcov + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Cache test data + uses: actions/cache@v2 + env: + test-data-files: tests/data/unicode-idna-test-data + with: + path: ${{ env.test-data-files }} + key: unicode-test-data-${{ hashFiles(env.test-data-files) }} + restore-keys: unicode-test-data- + + - name: Install dependencies + run: composer install + + - name: Tests with code coverage + run: | + mkdir -p ./build/coverage/ + vendor/bin/phpunit --coverage-clover ./build/coverage/clover.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./build/coverage/clover.xml + + tests-jit: + name: Test JIT + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - 8.0 + operating-system: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: none + extensions: :intl + ini-values: opcache.enable_cli=1, opcache.jit=1255, opcache.jit_buffer_size=64M + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Cache test data + uses: actions/cache@v2 + env: + test-data-files: tests/data/unicode-idna-test-data + with: + path: ${{ env.test-data-files }} + key: unicode-test-data-${{ hashFiles(env.test-data-files) }} + restore-keys: unicode-test-data- + + - name: Install dependencies + run: composer install + + - name: Tests + run: vendor/bin/phpunit diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/.gitignore b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.gitignore new file mode 100755 index 0000000000..da3851392f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.gitignore @@ -0,0 +1,7 @@ +composer.phar +composer.lock +/vendor/ +/.vscode/ +/build/ +/tests/data/ +.phpunit.result.cache diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/.phpcs.xml b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.phpcs.xml new file mode 100644 index 0000000000..8a8c9273e3 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/.phpcs.xml @@ -0,0 +1,14 @@ + + + src + */tests/* + */data/* + */Data/* + */build/* + src/Regex.php + + + + + + diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/CHANGELOG.md b/packages/playground/data-liberation/vendor-patched/rowbot/idna/CHANGELOG.md new file mode 100644 index 0000000000..3b671a4245 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog + +## [Unreleased] + +## [0.1.5] - 2022-01-10 + +### Added + +- Add `UNICODE_VERSION` constant to `Idna` class that returns a string with the Unicode version of the data files used + +### Changed + +- Update data files to Unicode 14 +- Update PHPStan to 1.0 + +## [0.1.4] - 2021-02-11 + +### Added + +- Test on PHP 8 release + +### Fixed + +- Handle the rare possibility that string nomalization can fail + +## [0.1.3] - 2020-08-03 + +### Fixed + +- Fix bug with domain names containing a URL delimiter + +## [0.1.2] - 2020-07-15 + +### Added + +- Performance improvements + +## [0.1.1] - 2020-07-10 + +### Added + +- Performance improvements +- Reduced memory footprint + +## [0.1.0] - 2020-06-10 + +- Initial release diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/LICENSE b/packages/playground/data-liberation/vendor-patched/rowbot/idna/LICENSE new file mode 100755 index 0000000000..06a800ebea --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Trevor Rowbotham + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/README.md b/packages/playground/data-liberation/vendor-patched/rowbot/idna/README.md new file mode 100644 index 0000000000..1d79b19b51 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/README.md @@ -0,0 +1,350 @@ +# IDNA + +[![License](https://img.shields.io/github/license/TRowbotham/idna?style=flat-square)](https://github.com/TRowbotham/URL-Parser/blob/master/LICENSE) +[![Build Status](https://img.shields.io/travis/com/TRowbotham/idna/master?style=flat-square)](https://travis-ci.com/github/TRowbotham/idna) +[![Code Coverage](https://img.shields.io/codecov/c/github/TRowbotham/idna/master?style=flat-square)](https://codecov.io/gh/TRowbotham/idna) +[![Version](https://img.shields.io/packagist/v/rowbot/idna?style=flat-square)](https://packagist.org/packages/rowbot/idna) +[![Downloads](https://img.shields.io/packagist/dt/rowbot/idna?style=flat-square)](https://packagist.org/packages/rowbot/idna) + +A fully compliant implementation of [UTS#46](https://www.unicode.org/reports/tr46/), otherwise known +as Unicode IDNA Compatibility Processing. You can read more about the differences between IDNA2003, +IDNA2008, and UTS#46 in [Section 7. IDNA Comparison](https://www.unicode.org/reports/tr46/#IDNAComparison) +of the specification. + +This library currently ships with Unicode 14.0.0 support and implements Version 14.0.0, Revision 27 of IDNA Compatibility +Processing. It has the ability to use Unicode 11.0.0 to Unicode 14.0.0. While this library likely supports versions of +Unicode less than 11.0.0, the format of the Unicode test files were changed beginning in 11.0.0 and as a result, versions +of Unicode less than 11.0.0 have not been tested. + +- [Requirements](#requirements) +- [Installation](#installation) +- [API](#api) +- [Error Codes](#error-codes) +- [The WTFs of Unicode Support in PHP](#the-wtfs-of-unicode-support-in-php) +- [FAQs](#faqs) +- [Internals](#internals) + +## Requirements + +- PHP 7.1+ +- `rowbot/punycode` +- `symfony/polyfill-intl-normalizer` + +## Installation + +```bash +composer require rowbot/idna +``` + +## API + +### Idna::UNICODE_VERSION + +The Unicode version of the data files used, as a string. + +### Idna::toAscii(string $domain, array $options = []): IdnaResult + +Converts a domain name to its ASCII form. Anytime an error is recorded while doing an ASCII +transformation, the transformation is considered to have failed and whatever domain name string is +returned is considered "garbage". What you do with that result is entirely up to you. + +#### toAscii Parameters + +- $domain - A domain name to convert to ASCII. +- $options - An array of options for customizing the behavior of the transformation. Possible + options include: + + - `"CheckBidi"` - Checks the domain name string for errors with bi-directional characters. + Defaults to true. + - `"CheckHyphens"` - Checks the domain name string for the positioning of hypens. Defaults to + true. + - `"CheckJoiners"` - Checks the domain name string for errors with joining code points. Defaults + to true. + - `"UseSTD3ASCIIRules"` - Disallows the use of ASCII characters other than `[a-zA-Z0-9-]`. + Defaults to true. + - `"Transitional_Processing"` - Whether transitional or non-transitional processing is used. When + enabled, processing behaves more like IDNA2003 and when disabled behaves like IDNA2008. Defaults + to false, which means that non-transitional processing is used by default. + - `"VerifyDnsLength"` - Validates the length of the domain name string and it's individual labels. + Defaults to true. + + **Note**: All options are case-sensitive. + + ```php + use Rowbot\Idna\Idna; + + $result = Idna::toAscii('x-.xn--nxa'); + + // You must not use an ASCII domain that has errors. + if ($result->hasErrors()) { + throw new \Exception(); + } + + echo $result->getDomain(); // x-.xn--nxa + ``` + +### Idna::toUnicode(string $domain, array $options = []): IdnaResult + +Converts the domain name to its Unicode form. Unlike the toAscii transformation, toUnicode does not +have a failure concept. This means that you can always use the returned string. However, deciding +what to do with the returned domain name string when an error is recorded is entirely up to you. + +- $domain - A domain name to convert to UTF-8. +- $options - An array of options for customizing the behavior of the transformation. Possible + options include: + + - `"CheckBidi"` - Checks the domain name string for errors with bi-directional characters. + Defaults to true. + - `"CheckHyphens"` - Checks the domain name string for the positioning of hypens. Defaults to + true. + - `"CheckJoiners"` - Checks the domain name string for errors with joining code points. Defaults + to true. + - `"UseSTD3ASCIIRules"` - Disallows the use of ASCII characters other than `[a-zA-Z0-9-]`. + Defaults to true. + - `"Transitional_Processing"` - Whether transitional or non-transitional processing is used. When + enabled, processing behaves more like IDNA2003 and when disabled behaves like IDNA2008. Defaults + to false, which means that non-transitional processing is used by default. + + **Note**: All options are case-sensitive. + + **Note**: `"VerifyDnsLength"` is not a valid option here. + + ```php + use Rowbot\Idna\Idna; + + $result = Idna::toUnicode('xn---with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n.com'); + echo $result->getDomain(); // 安室奈美恵-with-super-monkeys.com + ``` + +### IdnaResult object + +#### Members + +##### IdnaResult::getDomain(): string + +Returns the transformed domain name string. + +##### IdnaResult::getErrors(): int + +Returns a bitmask representing all errors that were recorded while processing the input domain name +string. + +##### IdnaResult::hasError(int $error): bool + +Returns whether or not a specific error was recorded. + +##### IdnaResult::hasErrors(): bool + +Returns whether or not an error was recorded while processing the input domain name string. + +##### IdnaResult::isTransitionalDifferent(): bool + +Returns `true` if the input domain name contains a code point that has a status of `"deviation"`. +This status indicates that the code points are handled differently in IDNA2003 than they are in +IDNA2008. At the time of writing, there are only 4 code points that have this status. They are +U+00DF, U+03C2, U+200C, and U+200D. + +## Error Codes + +- `Idna::ERROR_EMPTY_LABEL` + + The domain name or one of it's labels are an empty string. + +- `Idna::ERROR_LABEL_TOO_LONG` + + One of the domain's labels exceeds 63 bytes. + +- `Idna::ERROR_DOMAIN_NAME_TOO_LONG` + + The length of the domain name exceeds 253 bytes. + +- `Idna::ERROR_LEADING_HYPHEN` + + One of the domain name's labels starts with a hyphen-minus character (-). + +- `Idna::ERROR_TRAILING_HYPHEN` + + One of the domain name's labels ends with a hyphen-minus character (-). + +- `Idna::ERROR_HYPHEN_3_4` + + One of the domain name's labels contains a hyphen-minus character in the 3rd and 4th position. + +- `Idna::ERROR_LEADING_COMBINING_MARK` + + One of the domain name's labels starts with a combining mark. + +- `Idna::ERROR_DISALLOWED` + + The domain name contains characters that are disallowed. + +- `Idna::ERROR_PUNYCODE` + + One of the domain name's labels starts with "xn--", but is not valid punycode. + +- `Idna::ERROR_LABEL_HAS_DOT` + + One of the domain name's labels contains a full stop character (.). + +- `Idna::ERROR_INVALID_ACE_LABEL` + + One of the domain name's labels is an invalid ACE label. + +- `Idna::ERROR_BIDI` + + The domain name does not meet the BiDi requirements for IDNA. + +- `Idna::ERROR_CONTEXTJ` + + One of the domain name's labels does not meet the CONTEXTJ requirements for IDNA. + +## The WTFs of Unicode Support in PHP + +In any given version of PHP, there can be a multitude of different versions of Unicode in use. +So... WTF? + +- What does this mean? + + This means that if I ask the same question, each of the extensions listed below can give me a + different answer. This is compounded by the fact that the versions of Unicode used in the below + extensions can also be different given the same version of PHP. For example, the `intl` extension + being used by my installation of PHP 7.2 could be using Unicode version 11, but the `intl` + extension in your web hosts installation of PHP 7.2 could be using Unicode version 6. + +- How does this happen? + + - The `mbstring` extension uses its own version of Unicode. + - The `Onigurama` library, which is behind `mbstring`'s regular expression functions, uses its + own version of Unicode. + - The `PCRE` extension, which is the primary extension for working with regular extensions in + PHP, uses its own version of Unicode. + - The `intl` extension uses its own version of Unicode. + - Any other extensions that add their own versions of Unicode. + - Userland libraries use their own version of Unicode (including this library). + +- This library + + Being able to use `mbstring` or `intl` extensions would be helpful, but we cannot depend on them + being installed or them having a consistent version of Unicode when they are installed. + Additionally, extensions like `PCRE` could be compiled without Unicode support entirely, though we + do rely on `PCRE`'s `u` modifier. For this reason we have to include our own Unicode data. + +## FAQs + +- **I'm confused! Is this IDNA2003 or IDNA2008?** + + The answer to this is somewhat convoluted. TL;DR; It is neither. + + Here is what the spec says: + + > To satisfy user expectations for mapping, and provide maximal compatibility with IDNA2003, this + > document specifies a mapping for use with IDNA2008. In addition, to transition more smoothly to + > IDNA2008, this document provides a Unicode algorithm for a standardized processing that allows + > conformant implementations to minimize the security and interoperability problems caused by the + > differences between IDNA2003 and IDNA2008. This Unicode IDNA Compatibility Processing is + > structured according to IDNA2003 principles, but extends those principles to Unicode 5.2 and + > later. It also incorporates the repertoire extensions provided by IDNA2008. + + More information can be found in [Section 2. Unicode IDNA Compatibility Processing](https://www.unicode.org/reports/tr46/#Compatibility_Processing) + and [Section 7. IDNA Comparison](https://www.unicode.org/reports/tr46/#IDNAComparison). + +- **What are the recommended options?** + + The default options are the recommended options, which are also the strictest. + + ```php + // Default options. + [ + 'CheckHyphens' => true, + 'CheckBidi' => true, + 'CheckJoiners' => true, + 'UseSTD3ASCIIRules' => true, + 'Transitional_Processing' => false, + 'VerifyDnsLength' => true, // Only for Idna::toAscii() + ]; + ``` + +- **Do I have to provide all the options?** + + No. You only need to specifiy the options that you wish to change. Any option you specify will + overwrite the default options. + + ```php + use Rowbot\Idna\Idna; + + $result = Idna::toAscii('x-.xn--nxa', ['CheckHyphens' => true]); + $result->hasErrors(); // true + $result->hasError(Idna::ERROR_TRAILING_HYPHEN); // true + + $result = Idna::toAscii('x-.xn--nxa', ['CheckHyphens' => false]); + $result->hasErrors(); // false + $result->hasError(Idna::ERROR_TRAILING_HYPHEN); // false + ``` + +- **What is the difference between `Transitional` and `Non-transitional` processing?** + + `Transitional` processing is designed to mimic IDNA2003. It is highly recommended to use + `Non-transitional` processing, which tries to mimic IDNA2008. You can always check if a domain + name would be different between the two processing modes by checking + `IdnaResult::isTransitionalDifferent()`. + +- **Wouldn't it be neat if you also tested against the `idn_to_ascii()` and `idn_to_utf8()` + functions from the `intl` extension?** + + Yes. Yes, it would be neat if we could do an additional check for parity with the ICU + implementation, however, for the reasons outlined above in + [The WTFs of Unicode Support in PHP](#the-wtfs-of-unicode-support-in-php), testing against these + functions would be unreliable at best. + +- **Why does the `intl` extension show weird characters that look like diamonds with question marks + inside in invalid domains, but your implementation doesn't?** + + ```php + $input = '憡?Ⴔ.XN--1UG73GL146A'; + + idn_to_utf8($input, 0, IDNA_INTL_VARIANT_UTS46, $info); + echo $info['result']; // 憡��.xn--1ug73gl146a� + echo ($info['errors'] & IDNA_ERROR_DISALLOWED) !== 0; // true + + $result = \Rowbot\Idna\Idna::toUnicode($input); + echo $result->getDomain(); // 憡?Ⴔ.xn--1ug73gl146a + echo $result->hasError(\Rowbot\Idna\Idna::ERROR_DISALLOWED); // true + ``` + + From [Section 4. Processing](https://www.unicode.org/reports/tr46/#Processing): + + > Implementations may make further modifications to the resulting Unicode string when showing it to + > the user. For example, it is recommended that disallowed characters be replaced by a U+FFFD to + > make them visible to the user. Similarly, labels that fail processing during steps 4 or 5 may be + > marked by the insertion of a U+FFFD or other visual device. + + This implementation currently does not make these recommended modifications. + +## Internals + +### Building + +Unicode data files are fetched from https://www.unicode.org/Public. Currently, Unicode version +11.0.0-14.0.0 are supported. To change the version of Unicode that the library is built with, you +must first change the value of the `\Rowbot\Idna::UNICODE_VERSION` constant, like so: + +```diff +class Idna +{ +- public const UNICODE_VERSION = '13.0.0'; ++ public const UNICODE_VERSION = '14.0.0'; +``` + +Then to generate the necessary data files, you execute the following command: + +```bash +php bin/generateDataFiles.php +``` + +If no assertions or exceptions have occured, then you have successfully changed the Unicode version. +You should now execute the tests to make sure everything is good to go. The tests will automatically +fetch the version appropriate tests as the test files are not generated by the above command. + +```bash +vendor/bin/phpunit +``` diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/Builder.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/Builder.php new file mode 100644 index 0000000000..2de7da42be --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/Builder.php @@ -0,0 +1,90 @@ + + */ + protected static function parseCodePoints(string $codePoints): array + { + $range = explode('..', $codePoints); + $start = intval($range[0], 16); + $end = isset($range[1]) ? intval($range[1], 16) : $start; + + return [$start, $end]; + } + + /** + * @return array|string>> + */ + protected static function parseProperties(string $file): array + { + $handle = self::getUnicodeDataResource($file); + $retVal = []; + + while (($line = fgets($handle)) !== false) { + if ($line === "\n" || $line[0] === '#') { + continue; + } + + [$data] = explode('#', $line); + $data = array_map('trim', explode(';', $data)); + $data[0] = self::parseCodePoints($data[0]); + $retVal[] = $data; + } + + fclose($handle); + usort($retVal, static function (array $a, array $b): int { + return $a[0][0] <=> $b[0][0]; + }); + + return $retVal; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/IdnaDataBuilder.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/IdnaDataBuilder.php new file mode 100755 index 0000000000..a0fce8fd51 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/IdnaDataBuilder.php @@ -0,0 +1,129 @@ + [], + 'ignored' => [], + 'deviation' => [], + 'disallowed' => [], + 'disallowed_STD3_mapped' => [], + 'disallowed_STD3_valid' => [], + ]; + $rangeFallback = ''; + + while (($line = fgets($handle)) !== false) { + if ($line === "\n" || $line[0] === '#') { + continue; + } + + [$data] = explode('#', $line); + $data = array_map('trim', explode(';', $data)); + [$codePoints, $status] = $data; + $codePoints = self::parseCodePoints($codePoints); + $diff = $codePoints[1] - $codePoints[0] + 1; + + switch ($status) { + case 'valid': + // skip valid. + break; + + case 'mapped': + case 'deviation': + case 'disallowed_STD3_mapped': + if (preg_match_all('/[[:xdigit:]]+/', $data[2], $matches) === false) { + throw new RuntimeException(); + } + + $mapped = ''; + + foreach ($matches[0] as $codePoint) { + $mapped .= CodePoint::encode(intval($codePoint, 16)); + } + + for ($i = 0; $i < $diff; ++$i) { + $statuses[$status][$codePoints[0] + $i] = $mapped; + } + + break; + + case 'disallowed': + if ($diff > 30) { + if ($rangeFallback !== '') { + $rangeFallback .= "\n\n"; + } + + $rangeFallback .= <<= {$codePoints[0]} && \$codePoint <= {$codePoints[1]}) { + return true; + } +RANGE_FALLBACK; + + continue 2; + } + + for ($i = 0; $i < $diff; ++$i) { + $statuses[$status][$codePoints[0] + $i] = true; + } + + break; + + case 'ignored': + case 'disallowed_STD3_valid': + for ($i = 0; $i < $diff; ++$i) { + $statuses[$status][$codePoints[0] + $i] = true; + } + + break; + } + } + + fclose($handle); + file_put_contents($output . DS . 'mapped.php', "|string>> $data + */ + private static function buildCharacterClass(array $data): string + { + $out = ''; + + foreach ($data as $codePoints) { + assert(is_array($codePoints[0])); + + if ($codePoints[0][0] !== $codePoints[0][1]) { + $out .= sprintf('\x{%04X}-\x{%04X}', ...$codePoints[0]); + + continue; + } + + $out .= sprintf('\x{%04X}', $codePoints[0][0]); + } + + return $out; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/ViramaDataBuilder.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/ViramaDataBuilder.php new file mode 100644 index 0000000000..56e4429d62 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/bin/ViramaDataBuilder.php @@ -0,0 +1,47 @@ +=7.1", + "rowbot/punycode": "^1.0", + "symfony/polyfill-intl-normalizer": "^1.18" + }, + "require-dev": { + "guzzlehttp/guzzle": "^6.5 || ^7.0", + "phpstan/phpstan": "^1.2", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.5.1", + "symfony/cache": "^4.3 || ^5.0" + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/phpstan.neon b/packages/playground/data-liberation/vendor-patched/rowbot/idna/phpstan.neon new file mode 100644 index 0000000000..cf6152813a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/phpstan.neon @@ -0,0 +1,16 @@ +includes: + - vendor/phpstan/phpstan-deprecation-rules/rules.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon + +parameters: + level: max + paths: + - src + - bin + + ignoreErrors: + # This error can safely be ignored as we do these checks when building the data files. + - + message: "#Offset 'mapping' does not exist on array{status: string, mapping\\?: string}\\.#" + path: src/Idna.php + count: 2 diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/phpunit.xml b/packages/playground/data-liberation/vendor-patched/rowbot/idna/phpunit.xml new file mode 100644 index 0000000000..51f55c94cb --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/phpunit.xml @@ -0,0 +1,23 @@ + + + + tests + + + + + src + + src/Resources + + + + diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/DisallowedRanges.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/DisallowedRanges.php new file mode 100644 index 0000000000..5d64313c1b --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/DisallowedRanges.php @@ -0,0 +1,361 @@ += 128 && $codePoint <= 159) { + return true; + } + + if ($codePoint >= 3676 && $codePoint <= 3712) { + return true; + } + + if ($codePoint >= 3808 && $codePoint <= 3839) { + return true; + } + + if ($codePoint >= 4059 && $codePoint <= 4095) { + return true; + } + + if ($codePoint >= 4256 && $codePoint <= 4293) { + return true; + } + + if ($codePoint >= 6863 && $codePoint <= 6911) { + return true; + } + + if ($codePoint >= 11870 && $codePoint <= 11903) { + return true; + } + + if ($codePoint >= 55296 && $codePoint <= 57343) { + return true; + } + + if ($codePoint >= 57344 && $codePoint <= 63743) { + return true; + } + + if ($codePoint >= 64218 && $codePoint <= 64255) { + return true; + } + + if ($codePoint >= 64976 && $codePoint <= 65007) { + return true; + } + + if ($codePoint >= 65630 && $codePoint <= 65663) { + return true; + } + + if ($codePoint >= 65953 && $codePoint <= 65999) { + return true; + } + + if ($codePoint >= 66046 && $codePoint <= 66175) { + return true; + } + + if ($codePoint >= 66518 && $codePoint <= 66559) { + return true; + } + + if ($codePoint >= 67005 && $codePoint <= 67071) { + return true; + } + + if ($codePoint >= 67515 && $codePoint <= 67583) { + return true; + } + + if ($codePoint >= 67760 && $codePoint <= 67807) { + return true; + } + + if ($codePoint >= 67904 && $codePoint <= 67967) { + return true; + } + + if ($codePoint >= 68256 && $codePoint <= 68287) { + return true; + } + + if ($codePoint >= 68528 && $codePoint <= 68607) { + return true; + } + + if ($codePoint >= 68681 && $codePoint <= 68735) { + return true; + } + + if ($codePoint >= 68922 && $codePoint <= 69215) { + return true; + } + + if ($codePoint >= 69298 && $codePoint <= 69375) { + return true; + } + + if ($codePoint >= 69514 && $codePoint <= 69551) { + return true; + } + + if ($codePoint >= 70207 && $codePoint <= 70271) { + return true; + } + + if ($codePoint >= 70517 && $codePoint <= 70655) { + return true; + } + + if ($codePoint >= 70874 && $codePoint <= 71039) { + return true; + } + + if ($codePoint >= 71134 && $codePoint <= 71167) { + return true; + } + + if ($codePoint >= 71370 && $codePoint <= 71423) { + return true; + } + + if ($codePoint >= 71495 && $codePoint <= 71679) { + return true; + } + + if ($codePoint >= 71740 && $codePoint <= 71839) { + return true; + } + + if ($codePoint >= 72026 && $codePoint <= 72095) { + return true; + } + + if ($codePoint >= 72441 && $codePoint <= 72703) { + return true; + } + + if ($codePoint >= 72887 && $codePoint <= 72959) { + return true; + } + + if ($codePoint >= 73130 && $codePoint <= 73439) { + return true; + } + + if ($codePoint >= 73465 && $codePoint <= 73647) { + return true; + } + + if ($codePoint >= 74650 && $codePoint <= 74751) { + return true; + } + + if ($codePoint >= 75076 && $codePoint <= 77711) { + return true; + } + + if ($codePoint >= 78905 && $codePoint <= 82943) { + return true; + } + + if ($codePoint >= 83527 && $codePoint <= 92159) { + return true; + } + + if ($codePoint >= 93072 && $codePoint <= 93759) { + return true; + } + + if ($codePoint >= 93851 && $codePoint <= 93951) { + return true; + } + + if ($codePoint >= 94112 && $codePoint <= 94175) { + return true; + } + + if ($codePoint >= 101590 && $codePoint <= 101631) { + return true; + } + + if ($codePoint >= 101641 && $codePoint <= 110575) { + return true; + } + + if ($codePoint >= 110883 && $codePoint <= 110927) { + return true; + } + + if ($codePoint >= 111356 && $codePoint <= 113663) { + return true; + } + + if ($codePoint >= 113828 && $codePoint <= 118527) { + return true; + } + + if ($codePoint >= 118724 && $codePoint <= 118783) { + return true; + } + + if ($codePoint >= 119366 && $codePoint <= 119519) { + return true; + } + + if ($codePoint >= 119673 && $codePoint <= 119807) { + return true; + } + + if ($codePoint >= 121520 && $codePoint <= 122623) { + return true; + } + + if ($codePoint >= 122655 && $codePoint <= 122879) { + return true; + } + + if ($codePoint >= 122923 && $codePoint <= 123135) { + return true; + } + + if ($codePoint >= 123216 && $codePoint <= 123535) { + return true; + } + + if ($codePoint >= 123648 && $codePoint <= 124895) { + return true; + } + + if ($codePoint >= 125143 && $codePoint <= 125183) { + return true; + } + + if ($codePoint >= 125280 && $codePoint <= 126064) { + return true; + } + + if ($codePoint >= 126133 && $codePoint <= 126208) { + return true; + } + + if ($codePoint >= 126270 && $codePoint <= 126463) { + return true; + } + + if ($codePoint >= 126652 && $codePoint <= 126703) { + return true; + } + + if ($codePoint >= 126706 && $codePoint <= 126975) { + return true; + } + + if ($codePoint >= 127406 && $codePoint <= 127461) { + return true; + } + + if ($codePoint >= 127590 && $codePoint <= 127743) { + return true; + } + + if ($codePoint >= 129202 && $codePoint <= 129279) { + return true; + } + + if ($codePoint >= 129995 && $codePoint <= 130031) { + return true; + } + + if ($codePoint >= 130042 && $codePoint <= 131069) { + return true; + } + + if ($codePoint >= 173792 && $codePoint <= 173823) { + return true; + } + + if ($codePoint >= 191457 && $codePoint <= 194559) { + return true; + } + + if ($codePoint >= 195102 && $codePoint <= 196605) { + return true; + } + + if ($codePoint >= 201547 && $codePoint <= 262141) { + return true; + } + + if ($codePoint >= 262144 && $codePoint <= 327677) { + return true; + } + + if ($codePoint >= 327680 && $codePoint <= 393213) { + return true; + } + + if ($codePoint >= 393216 && $codePoint <= 458749) { + return true; + } + + if ($codePoint >= 458752 && $codePoint <= 524285) { + return true; + } + + if ($codePoint >= 524288 && $codePoint <= 589821) { + return true; + } + + if ($codePoint >= 589824 && $codePoint <= 655357) { + return true; + } + + if ($codePoint >= 655360 && $codePoint <= 720893) { + return true; + } + + if ($codePoint >= 720896 && $codePoint <= 786429) { + return true; + } + + if ($codePoint >= 786432 && $codePoint <= 851965) { + return true; + } + + if ($codePoint >= 851968 && $codePoint <= 917501) { + return true; + } + + if ($codePoint >= 917536 && $codePoint <= 917631) { + return true; + } + + if ($codePoint >= 917632 && $codePoint <= 917759) { + return true; + } + + if ($codePoint >= 918000 && $codePoint <= 983037) { + return true; + } + + if ($codePoint >= 983040 && $codePoint <= 1048573) { + return true; + } + + if ($codePoint >= 1048576 && $codePoint <= 1114109) { + return true; + } + + return false; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/Regex.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/Regex.php new file mode 100644 index 0000000000..bedfe6b20c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/Regex.php @@ -0,0 +1,32 @@ + 'ss', + 962 => 'σ', + 8204 => '', + 8205 => '', +); diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed.php new file mode 100644 index 0000000000..58bb2a2758 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed.php @@ -0,0 +1,2715 @@ + true, + 889 => true, + 896 => true, + 897 => true, + 898 => true, + 899 => true, + 907 => true, + 909 => true, + 930 => true, + 1216 => true, + 1328 => true, + 1367 => true, + 1368 => true, + 1419 => true, + 1420 => true, + 1424 => true, + 1480 => true, + 1481 => true, + 1482 => true, + 1483 => true, + 1484 => true, + 1485 => true, + 1486 => true, + 1487 => true, + 1515 => true, + 1516 => true, + 1517 => true, + 1518 => true, + 1525 => true, + 1526 => true, + 1527 => true, + 1528 => true, + 1529 => true, + 1530 => true, + 1531 => true, + 1532 => true, + 1533 => true, + 1534 => true, + 1535 => true, + 1536 => true, + 1537 => true, + 1538 => true, + 1539 => true, + 1540 => true, + 1541 => true, + 1564 => true, + 1757 => true, + 1806 => true, + 1807 => true, + 1867 => true, + 1868 => true, + 1970 => true, + 1971 => true, + 1972 => true, + 1973 => true, + 1974 => true, + 1975 => true, + 1976 => true, + 1977 => true, + 1978 => true, + 1979 => true, + 1980 => true, + 1981 => true, + 1982 => true, + 1983 => true, + 2043 => true, + 2044 => true, + 2094 => true, + 2095 => true, + 2111 => true, + 2140 => true, + 2141 => true, + 2143 => true, + 2155 => true, + 2156 => true, + 2157 => true, + 2158 => true, + 2159 => true, + 2191 => true, + 2192 => true, + 2193 => true, + 2194 => true, + 2195 => true, + 2196 => true, + 2197 => true, + 2198 => true, + 2199 => true, + 2274 => true, + 2436 => true, + 2445 => true, + 2446 => true, + 2449 => true, + 2450 => true, + 2473 => true, + 2481 => true, + 2483 => true, + 2484 => true, + 2485 => true, + 2490 => true, + 2491 => true, + 2501 => true, + 2502 => true, + 2505 => true, + 2506 => true, + 2511 => true, + 2512 => true, + 2513 => true, + 2514 => true, + 2515 => true, + 2516 => true, + 2517 => true, + 2518 => true, + 2520 => true, + 2521 => true, + 2522 => true, + 2523 => true, + 2526 => true, + 2532 => true, + 2533 => true, + 2559 => true, + 2560 => true, + 2564 => true, + 2571 => true, + 2572 => true, + 2573 => true, + 2574 => true, + 2577 => true, + 2578 => true, + 2601 => true, + 2609 => true, + 2612 => true, + 2615 => true, + 2618 => true, + 2619 => true, + 2621 => true, + 2627 => true, + 2628 => true, + 2629 => true, + 2630 => true, + 2633 => true, + 2634 => true, + 2638 => true, + 2639 => true, + 2640 => true, + 2642 => true, + 2643 => true, + 2644 => true, + 2645 => true, + 2646 => true, + 2647 => true, + 2648 => true, + 2653 => true, + 2655 => true, + 2656 => true, + 2657 => true, + 2658 => true, + 2659 => true, + 2660 => true, + 2661 => true, + 2679 => true, + 2680 => true, + 2681 => true, + 2682 => true, + 2683 => true, + 2684 => true, + 2685 => true, + 2686 => true, + 2687 => true, + 2688 => true, + 2692 => true, + 2702 => true, + 2706 => true, + 2729 => true, + 2737 => true, + 2740 => true, + 2746 => true, + 2747 => true, + 2758 => true, + 2762 => true, + 2766 => true, + 2767 => true, + 2769 => true, + 2770 => true, + 2771 => true, + 2772 => true, + 2773 => true, + 2774 => true, + 2775 => true, + 2776 => true, + 2777 => true, + 2778 => true, + 2779 => true, + 2780 => true, + 2781 => true, + 2782 => true, + 2783 => true, + 2788 => true, + 2789 => true, + 2802 => true, + 2803 => true, + 2804 => true, + 2805 => true, + 2806 => true, + 2807 => true, + 2808 => true, + 2816 => true, + 2820 => true, + 2829 => true, + 2830 => true, + 2833 => true, + 2834 => true, + 2857 => true, + 2865 => true, + 2868 => true, + 2874 => true, + 2875 => true, + 2885 => true, + 2886 => true, + 2889 => true, + 2890 => true, + 2894 => true, + 2895 => true, + 2896 => true, + 2897 => true, + 2898 => true, + 2899 => true, + 2900 => true, + 2904 => true, + 2905 => true, + 2906 => true, + 2907 => true, + 2910 => true, + 2916 => true, + 2917 => true, + 2936 => true, + 2937 => true, + 2938 => true, + 2939 => true, + 2940 => true, + 2941 => true, + 2942 => true, + 2943 => true, + 2944 => true, + 2945 => true, + 2948 => true, + 2955 => true, + 2956 => true, + 2957 => true, + 2961 => true, + 2966 => true, + 2967 => true, + 2968 => true, + 2971 => true, + 2973 => true, + 2976 => true, + 2977 => true, + 2978 => true, + 2981 => true, + 2982 => true, + 2983 => true, + 2987 => true, + 2988 => true, + 2989 => true, + 3002 => true, + 3003 => true, + 3004 => true, + 3005 => true, + 3011 => true, + 3012 => true, + 3013 => true, + 3017 => true, + 3022 => true, + 3023 => true, + 3025 => true, + 3026 => true, + 3027 => true, + 3028 => true, + 3029 => true, + 3030 => true, + 3032 => true, + 3033 => true, + 3034 => true, + 3035 => true, + 3036 => true, + 3037 => true, + 3038 => true, + 3039 => true, + 3040 => true, + 3041 => true, + 3042 => true, + 3043 => true, + 3044 => true, + 3045 => true, + 3067 => true, + 3068 => true, + 3069 => true, + 3070 => true, + 3071 => true, + 3085 => true, + 3089 => true, + 3113 => true, + 3130 => true, + 3131 => true, + 3141 => true, + 3145 => true, + 3150 => true, + 3151 => true, + 3152 => true, + 3153 => true, + 3154 => true, + 3155 => true, + 3156 => true, + 3159 => true, + 3163 => true, + 3164 => true, + 3166 => true, + 3167 => true, + 3172 => true, + 3173 => true, + 3184 => true, + 3185 => true, + 3186 => true, + 3187 => true, + 3188 => true, + 3189 => true, + 3190 => true, + 3213 => true, + 3217 => true, + 3241 => true, + 3252 => true, + 3258 => true, + 3259 => true, + 3269 => true, + 3273 => true, + 3278 => true, + 3279 => true, + 3280 => true, + 3281 => true, + 3282 => true, + 3283 => true, + 3284 => true, + 3287 => true, + 3288 => true, + 3289 => true, + 3290 => true, + 3291 => true, + 3292 => true, + 3295 => true, + 3300 => true, + 3301 => true, + 3312 => true, + 3315 => true, + 3316 => true, + 3317 => true, + 3318 => true, + 3319 => true, + 3320 => true, + 3321 => true, + 3322 => true, + 3323 => true, + 3324 => true, + 3325 => true, + 3326 => true, + 3327 => true, + 3341 => true, + 3345 => true, + 3397 => true, + 3401 => true, + 3408 => true, + 3409 => true, + 3410 => true, + 3411 => true, + 3428 => true, + 3429 => true, + 3456 => true, + 3460 => true, + 3479 => true, + 3480 => true, + 3481 => true, + 3506 => true, + 3516 => true, + 3518 => true, + 3519 => true, + 3527 => true, + 3528 => true, + 3529 => true, + 3531 => true, + 3532 => true, + 3533 => true, + 3534 => true, + 3541 => true, + 3543 => true, + 3552 => true, + 3553 => true, + 3554 => true, + 3555 => true, + 3556 => true, + 3557 => true, + 3568 => true, + 3569 => true, + 3573 => true, + 3574 => true, + 3575 => true, + 3576 => true, + 3577 => true, + 3578 => true, + 3579 => true, + 3580 => true, + 3581 => true, + 3582 => true, + 3583 => true, + 3584 => true, + 3643 => true, + 3644 => true, + 3645 => true, + 3646 => true, + 3715 => true, + 3717 => true, + 3723 => true, + 3748 => true, + 3750 => true, + 3774 => true, + 3775 => true, + 3781 => true, + 3783 => true, + 3790 => true, + 3791 => true, + 3802 => true, + 3803 => true, + 3912 => true, + 3949 => true, + 3950 => true, + 3951 => true, + 3952 => true, + 3992 => true, + 4029 => true, + 4045 => true, + 4294 => true, + 4296 => true, + 4297 => true, + 4298 => true, + 4299 => true, + 4300 => true, + 4302 => true, + 4303 => true, + 4447 => true, + 4448 => true, + 4681 => true, + 4686 => true, + 4687 => true, + 4695 => true, + 4697 => true, + 4702 => true, + 4703 => true, + 4745 => true, + 4750 => true, + 4751 => true, + 4785 => true, + 4790 => true, + 4791 => true, + 4799 => true, + 4801 => true, + 4806 => true, + 4807 => true, + 4823 => true, + 4881 => true, + 4886 => true, + 4887 => true, + 4955 => true, + 4956 => true, + 4989 => true, + 4990 => true, + 4991 => true, + 5018 => true, + 5019 => true, + 5020 => true, + 5021 => true, + 5022 => true, + 5023 => true, + 5110 => true, + 5111 => true, + 5118 => true, + 5119 => true, + 5760 => true, + 5789 => true, + 5790 => true, + 5791 => true, + 5881 => true, + 5882 => true, + 5883 => true, + 5884 => true, + 5885 => true, + 5886 => true, + 5887 => true, + 5910 => true, + 5911 => true, + 5912 => true, + 5913 => true, + 5914 => true, + 5915 => true, + 5916 => true, + 5917 => true, + 5918 => true, + 5943 => true, + 5944 => true, + 5945 => true, + 5946 => true, + 5947 => true, + 5948 => true, + 5949 => true, + 5950 => true, + 5951 => true, + 5972 => true, + 5973 => true, + 5974 => true, + 5975 => true, + 5976 => true, + 5977 => true, + 5978 => true, + 5979 => true, + 5980 => true, + 5981 => true, + 5982 => true, + 5983 => true, + 5997 => true, + 6001 => true, + 6004 => true, + 6005 => true, + 6006 => true, + 6007 => true, + 6008 => true, + 6009 => true, + 6010 => true, + 6011 => true, + 6012 => true, + 6013 => true, + 6014 => true, + 6015 => true, + 6068 => true, + 6069 => true, + 6110 => true, + 6111 => true, + 6122 => true, + 6123 => true, + 6124 => true, + 6125 => true, + 6126 => true, + 6127 => true, + 6138 => true, + 6139 => true, + 6140 => true, + 6141 => true, + 6142 => true, + 6143 => true, + 6150 => true, + 6158 => true, + 6170 => true, + 6171 => true, + 6172 => true, + 6173 => true, + 6174 => true, + 6175 => true, + 6265 => true, + 6266 => true, + 6267 => true, + 6268 => true, + 6269 => true, + 6270 => true, + 6271 => true, + 6315 => true, + 6316 => true, + 6317 => true, + 6318 => true, + 6319 => true, + 6390 => true, + 6391 => true, + 6392 => true, + 6393 => true, + 6394 => true, + 6395 => true, + 6396 => true, + 6397 => true, + 6398 => true, + 6399 => true, + 6431 => true, + 6444 => true, + 6445 => true, + 6446 => true, + 6447 => true, + 6460 => true, + 6461 => true, + 6462 => true, + 6463 => true, + 6465 => true, + 6466 => true, + 6467 => true, + 6510 => true, + 6511 => true, + 6517 => true, + 6518 => true, + 6519 => true, + 6520 => true, + 6521 => true, + 6522 => true, + 6523 => true, + 6524 => true, + 6525 => true, + 6526 => true, + 6527 => true, + 6572 => true, + 6573 => true, + 6574 => true, + 6575 => true, + 6602 => true, + 6603 => true, + 6604 => true, + 6605 => true, + 6606 => true, + 6607 => true, + 6619 => true, + 6620 => true, + 6621 => true, + 6684 => true, + 6685 => true, + 6751 => true, + 6781 => true, + 6782 => true, + 6794 => true, + 6795 => true, + 6796 => true, + 6797 => true, + 6798 => true, + 6799 => true, + 6810 => true, + 6811 => true, + 6812 => true, + 6813 => true, + 6814 => true, + 6815 => true, + 6830 => true, + 6831 => true, + 6989 => true, + 6990 => true, + 6991 => true, + 7039 => true, + 7156 => true, + 7157 => true, + 7158 => true, + 7159 => true, + 7160 => true, + 7161 => true, + 7162 => true, + 7163 => true, + 7224 => true, + 7225 => true, + 7226 => true, + 7242 => true, + 7243 => true, + 7244 => true, + 7305 => true, + 7306 => true, + 7307 => true, + 7308 => true, + 7309 => true, + 7310 => true, + 7311 => true, + 7355 => true, + 7356 => true, + 7368 => true, + 7369 => true, + 7370 => true, + 7371 => true, + 7372 => true, + 7373 => true, + 7374 => true, + 7375 => true, + 7419 => true, + 7420 => true, + 7421 => true, + 7422 => true, + 7423 => true, + 7958 => true, + 7959 => true, + 7966 => true, + 7967 => true, + 8006 => true, + 8007 => true, + 8014 => true, + 8015 => true, + 8024 => true, + 8026 => true, + 8028 => true, + 8030 => true, + 8062 => true, + 8063 => true, + 8117 => true, + 8133 => true, + 8148 => true, + 8149 => true, + 8156 => true, + 8176 => true, + 8177 => true, + 8181 => true, + 8191 => true, + 8206 => true, + 8207 => true, + 8228 => true, + 8229 => true, + 8230 => true, + 8232 => true, + 8233 => true, + 8234 => true, + 8235 => true, + 8236 => true, + 8237 => true, + 8238 => true, + 8289 => true, + 8290 => true, + 8291 => true, + 8293 => true, + 8294 => true, + 8295 => true, + 8296 => true, + 8297 => true, + 8298 => true, + 8299 => true, + 8300 => true, + 8301 => true, + 8302 => true, + 8303 => true, + 8306 => true, + 8307 => true, + 8335 => true, + 8349 => true, + 8350 => true, + 8351 => true, + 8385 => true, + 8386 => true, + 8387 => true, + 8388 => true, + 8389 => true, + 8390 => true, + 8391 => true, + 8392 => true, + 8393 => true, + 8394 => true, + 8395 => true, + 8396 => true, + 8397 => true, + 8398 => true, + 8399 => true, + 8433 => true, + 8434 => true, + 8435 => true, + 8436 => true, + 8437 => true, + 8438 => true, + 8439 => true, + 8440 => true, + 8441 => true, + 8442 => true, + 8443 => true, + 8444 => true, + 8445 => true, + 8446 => true, + 8447 => true, + 8498 => true, + 8579 => true, + 8588 => true, + 8589 => true, + 8590 => true, + 8591 => true, + 9255 => true, + 9256 => true, + 9257 => true, + 9258 => true, + 9259 => true, + 9260 => true, + 9261 => true, + 9262 => true, + 9263 => true, + 9264 => true, + 9265 => true, + 9266 => true, + 9267 => true, + 9268 => true, + 9269 => true, + 9270 => true, + 9271 => true, + 9272 => true, + 9273 => true, + 9274 => true, + 9275 => true, + 9276 => true, + 9277 => true, + 9278 => true, + 9279 => true, + 9291 => true, + 9292 => true, + 9293 => true, + 9294 => true, + 9295 => true, + 9296 => true, + 9297 => true, + 9298 => true, + 9299 => true, + 9300 => true, + 9301 => true, + 9302 => true, + 9303 => true, + 9304 => true, + 9305 => true, + 9306 => true, + 9307 => true, + 9308 => true, + 9309 => true, + 9310 => true, + 9311 => true, + 9352 => true, + 9353 => true, + 9354 => true, + 9355 => true, + 9356 => true, + 9357 => true, + 9358 => true, + 9359 => true, + 9360 => true, + 9361 => true, + 9362 => true, + 9363 => true, + 9364 => true, + 9365 => true, + 9366 => true, + 9367 => true, + 9368 => true, + 9369 => true, + 9370 => true, + 9371 => true, + 11124 => true, + 11125 => true, + 11158 => true, + 11508 => true, + 11509 => true, + 11510 => true, + 11511 => true, + 11512 => true, + 11558 => true, + 11560 => true, + 11561 => true, + 11562 => true, + 11563 => true, + 11564 => true, + 11566 => true, + 11567 => true, + 11624 => true, + 11625 => true, + 11626 => true, + 11627 => true, + 11628 => true, + 11629 => true, + 11630 => true, + 11633 => true, + 11634 => true, + 11635 => true, + 11636 => true, + 11637 => true, + 11638 => true, + 11639 => true, + 11640 => true, + 11641 => true, + 11642 => true, + 11643 => true, + 11644 => true, + 11645 => true, + 11646 => true, + 11671 => true, + 11672 => true, + 11673 => true, + 11674 => true, + 11675 => true, + 11676 => true, + 11677 => true, + 11678 => true, + 11679 => true, + 11687 => true, + 11695 => true, + 11703 => true, + 11711 => true, + 11719 => true, + 11727 => true, + 11735 => true, + 11743 => true, + 11930 => true, + 12020 => true, + 12021 => true, + 12022 => true, + 12023 => true, + 12024 => true, + 12025 => true, + 12026 => true, + 12027 => true, + 12028 => true, + 12029 => true, + 12030 => true, + 12031 => true, + 12246 => true, + 12247 => true, + 12248 => true, + 12249 => true, + 12250 => true, + 12251 => true, + 12252 => true, + 12253 => true, + 12254 => true, + 12255 => true, + 12256 => true, + 12257 => true, + 12258 => true, + 12259 => true, + 12260 => true, + 12261 => true, + 12262 => true, + 12263 => true, + 12264 => true, + 12265 => true, + 12266 => true, + 12267 => true, + 12268 => true, + 12269 => true, + 12270 => true, + 12271 => true, + 12272 => true, + 12273 => true, + 12274 => true, + 12275 => true, + 12276 => true, + 12277 => true, + 12278 => true, + 12279 => true, + 12280 => true, + 12281 => true, + 12282 => true, + 12283 => true, + 12284 => true, + 12285 => true, + 12286 => true, + 12287 => true, + 12352 => true, + 12439 => true, + 12440 => true, + 12544 => true, + 12545 => true, + 12546 => true, + 12547 => true, + 12548 => true, + 12592 => true, + 12644 => true, + 12687 => true, + 12772 => true, + 12773 => true, + 12774 => true, + 12775 => true, + 12776 => true, + 12777 => true, + 12778 => true, + 12779 => true, + 12780 => true, + 12781 => true, + 12782 => true, + 12783 => true, + 12831 => true, + 13250 => true, + 13255 => true, + 13272 => true, + 42125 => true, + 42126 => true, + 42127 => true, + 42183 => true, + 42184 => true, + 42185 => true, + 42186 => true, + 42187 => true, + 42188 => true, + 42189 => true, + 42190 => true, + 42191 => true, + 42540 => true, + 42541 => true, + 42542 => true, + 42543 => true, + 42544 => true, + 42545 => true, + 42546 => true, + 42547 => true, + 42548 => true, + 42549 => true, + 42550 => true, + 42551 => true, + 42552 => true, + 42553 => true, + 42554 => true, + 42555 => true, + 42556 => true, + 42557 => true, + 42558 => true, + 42559 => true, + 42744 => true, + 42745 => true, + 42746 => true, + 42747 => true, + 42748 => true, + 42749 => true, + 42750 => true, + 42751 => true, + 42955 => true, + 42956 => true, + 42957 => true, + 42958 => true, + 42959 => true, + 42962 => true, + 42964 => true, + 42970 => true, + 42971 => true, + 42972 => true, + 42973 => true, + 42974 => true, + 42975 => true, + 42976 => true, + 42977 => true, + 42978 => true, + 42979 => true, + 42980 => true, + 42981 => true, + 42982 => true, + 42983 => true, + 42984 => true, + 42985 => true, + 42986 => true, + 42987 => true, + 42988 => true, + 42989 => true, + 42990 => true, + 42991 => true, + 42992 => true, + 42993 => true, + 43053 => true, + 43054 => true, + 43055 => true, + 43066 => true, + 43067 => true, + 43068 => true, + 43069 => true, + 43070 => true, + 43071 => true, + 43128 => true, + 43129 => true, + 43130 => true, + 43131 => true, + 43132 => true, + 43133 => true, + 43134 => true, + 43135 => true, + 43206 => true, + 43207 => true, + 43208 => true, + 43209 => true, + 43210 => true, + 43211 => true, + 43212 => true, + 43213 => true, + 43226 => true, + 43227 => true, + 43228 => true, + 43229 => true, + 43230 => true, + 43231 => true, + 43348 => true, + 43349 => true, + 43350 => true, + 43351 => true, + 43352 => true, + 43353 => true, + 43354 => true, + 43355 => true, + 43356 => true, + 43357 => true, + 43358 => true, + 43389 => true, + 43390 => true, + 43391 => true, + 43470 => true, + 43482 => true, + 43483 => true, + 43484 => true, + 43485 => true, + 43519 => true, + 43575 => true, + 43576 => true, + 43577 => true, + 43578 => true, + 43579 => true, + 43580 => true, + 43581 => true, + 43582 => true, + 43583 => true, + 43598 => true, + 43599 => true, + 43610 => true, + 43611 => true, + 43715 => true, + 43716 => true, + 43717 => true, + 43718 => true, + 43719 => true, + 43720 => true, + 43721 => true, + 43722 => true, + 43723 => true, + 43724 => true, + 43725 => true, + 43726 => true, + 43727 => true, + 43728 => true, + 43729 => true, + 43730 => true, + 43731 => true, + 43732 => true, + 43733 => true, + 43734 => true, + 43735 => true, + 43736 => true, + 43737 => true, + 43738 => true, + 43767 => true, + 43768 => true, + 43769 => true, + 43770 => true, + 43771 => true, + 43772 => true, + 43773 => true, + 43774 => true, + 43775 => true, + 43776 => true, + 43783 => true, + 43784 => true, + 43791 => true, + 43792 => true, + 43799 => true, + 43800 => true, + 43801 => true, + 43802 => true, + 43803 => true, + 43804 => true, + 43805 => true, + 43806 => true, + 43807 => true, + 43815 => true, + 43823 => true, + 43884 => true, + 43885 => true, + 43886 => true, + 43887 => true, + 44014 => true, + 44015 => true, + 44026 => true, + 44027 => true, + 44028 => true, + 44029 => true, + 44030 => true, + 44031 => true, + 55204 => true, + 55205 => true, + 55206 => true, + 55207 => true, + 55208 => true, + 55209 => true, + 55210 => true, + 55211 => true, + 55212 => true, + 55213 => true, + 55214 => true, + 55215 => true, + 55239 => true, + 55240 => true, + 55241 => true, + 55242 => true, + 55292 => true, + 55293 => true, + 55294 => true, + 55295 => true, + 64110 => true, + 64111 => true, + 64263 => true, + 64264 => true, + 64265 => true, + 64266 => true, + 64267 => true, + 64268 => true, + 64269 => true, + 64270 => true, + 64271 => true, + 64272 => true, + 64273 => true, + 64274 => true, + 64280 => true, + 64281 => true, + 64282 => true, + 64283 => true, + 64284 => true, + 64311 => true, + 64317 => true, + 64319 => true, + 64322 => true, + 64325 => true, + 64451 => true, + 64452 => true, + 64453 => true, + 64454 => true, + 64455 => true, + 64456 => true, + 64457 => true, + 64458 => true, + 64459 => true, + 64460 => true, + 64461 => true, + 64462 => true, + 64463 => true, + 64464 => true, + 64465 => true, + 64466 => true, + 64912 => true, + 64913 => true, + 64968 => true, + 64969 => true, + 64970 => true, + 64971 => true, + 64972 => true, + 64973 => true, + 64974 => true, + 65042 => true, + 65049 => true, + 65050 => true, + 65051 => true, + 65052 => true, + 65053 => true, + 65054 => true, + 65055 => true, + 65072 => true, + 65106 => true, + 65107 => true, + 65127 => true, + 65132 => true, + 65133 => true, + 65134 => true, + 65135 => true, + 65141 => true, + 65277 => true, + 65278 => true, + 65280 => true, + 65440 => true, + 65471 => true, + 65472 => true, + 65473 => true, + 65480 => true, + 65481 => true, + 65488 => true, + 65489 => true, + 65496 => true, + 65497 => true, + 65501 => true, + 65502 => true, + 65503 => true, + 65511 => true, + 65519 => true, + 65520 => true, + 65521 => true, + 65522 => true, + 65523 => true, + 65524 => true, + 65525 => true, + 65526 => true, + 65527 => true, + 65528 => true, + 65529 => true, + 65530 => true, + 65531 => true, + 65532 => true, + 65533 => true, + 65534 => true, + 65535 => true, + 65548 => true, + 65575 => true, + 65595 => true, + 65598 => true, + 65614 => true, + 65615 => true, + 65787 => true, + 65788 => true, + 65789 => true, + 65790 => true, + 65791 => true, + 65795 => true, + 65796 => true, + 65797 => true, + 65798 => true, + 65844 => true, + 65845 => true, + 65846 => true, + 65935 => true, + 65949 => true, + 65950 => true, + 65951 => true, + 66205 => true, + 66206 => true, + 66207 => true, + 66257 => true, + 66258 => true, + 66259 => true, + 66260 => true, + 66261 => true, + 66262 => true, + 66263 => true, + 66264 => true, + 66265 => true, + 66266 => true, + 66267 => true, + 66268 => true, + 66269 => true, + 66270 => true, + 66271 => true, + 66300 => true, + 66301 => true, + 66302 => true, + 66303 => true, + 66340 => true, + 66341 => true, + 66342 => true, + 66343 => true, + 66344 => true, + 66345 => true, + 66346 => true, + 66347 => true, + 66348 => true, + 66379 => true, + 66380 => true, + 66381 => true, + 66382 => true, + 66383 => true, + 66427 => true, + 66428 => true, + 66429 => true, + 66430 => true, + 66431 => true, + 66462 => true, + 66500 => true, + 66501 => true, + 66502 => true, + 66503 => true, + 66718 => true, + 66719 => true, + 66730 => true, + 66731 => true, + 66732 => true, + 66733 => true, + 66734 => true, + 66735 => true, + 66772 => true, + 66773 => true, + 66774 => true, + 66775 => true, + 66812 => true, + 66813 => true, + 66814 => true, + 66815 => true, + 66856 => true, + 66857 => true, + 66858 => true, + 66859 => true, + 66860 => true, + 66861 => true, + 66862 => true, + 66863 => true, + 66916 => true, + 66917 => true, + 66918 => true, + 66919 => true, + 66920 => true, + 66921 => true, + 66922 => true, + 66923 => true, + 66924 => true, + 66925 => true, + 66926 => true, + 66939 => true, + 66955 => true, + 66963 => true, + 66966 => true, + 66978 => true, + 66994 => true, + 67002 => true, + 67383 => true, + 67384 => true, + 67385 => true, + 67386 => true, + 67387 => true, + 67388 => true, + 67389 => true, + 67390 => true, + 67391 => true, + 67414 => true, + 67415 => true, + 67416 => true, + 67417 => true, + 67418 => true, + 67419 => true, + 67420 => true, + 67421 => true, + 67422 => true, + 67423 => true, + 67432 => true, + 67433 => true, + 67434 => true, + 67435 => true, + 67436 => true, + 67437 => true, + 67438 => true, + 67439 => true, + 67440 => true, + 67441 => true, + 67442 => true, + 67443 => true, + 67444 => true, + 67445 => true, + 67446 => true, + 67447 => true, + 67448 => true, + 67449 => true, + 67450 => true, + 67451 => true, + 67452 => true, + 67453 => true, + 67454 => true, + 67455 => true, + 67462 => true, + 67505 => true, + 67590 => true, + 67591 => true, + 67593 => true, + 67638 => true, + 67641 => true, + 67642 => true, + 67643 => true, + 67645 => true, + 67646 => true, + 67670 => true, + 67743 => true, + 67744 => true, + 67745 => true, + 67746 => true, + 67747 => true, + 67748 => true, + 67749 => true, + 67750 => true, + 67827 => true, + 67830 => true, + 67831 => true, + 67832 => true, + 67833 => true, + 67834 => true, + 67868 => true, + 67869 => true, + 67870 => true, + 67898 => true, + 67899 => true, + 67900 => true, + 67901 => true, + 67902 => true, + 68024 => true, + 68025 => true, + 68026 => true, + 68027 => true, + 68048 => true, + 68049 => true, + 68100 => true, + 68103 => true, + 68104 => true, + 68105 => true, + 68106 => true, + 68107 => true, + 68116 => true, + 68120 => true, + 68150 => true, + 68151 => true, + 68155 => true, + 68156 => true, + 68157 => true, + 68158 => true, + 68169 => true, + 68170 => true, + 68171 => true, + 68172 => true, + 68173 => true, + 68174 => true, + 68175 => true, + 68185 => true, + 68186 => true, + 68187 => true, + 68188 => true, + 68189 => true, + 68190 => true, + 68191 => true, + 68327 => true, + 68328 => true, + 68329 => true, + 68330 => true, + 68343 => true, + 68344 => true, + 68345 => true, + 68346 => true, + 68347 => true, + 68348 => true, + 68349 => true, + 68350 => true, + 68351 => true, + 68406 => true, + 68407 => true, + 68408 => true, + 68438 => true, + 68439 => true, + 68467 => true, + 68468 => true, + 68469 => true, + 68470 => true, + 68471 => true, + 68498 => true, + 68499 => true, + 68500 => true, + 68501 => true, + 68502 => true, + 68503 => true, + 68504 => true, + 68509 => true, + 68510 => true, + 68511 => true, + 68512 => true, + 68513 => true, + 68514 => true, + 68515 => true, + 68516 => true, + 68517 => true, + 68518 => true, + 68519 => true, + 68520 => true, + 68787 => true, + 68788 => true, + 68789 => true, + 68790 => true, + 68791 => true, + 68792 => true, + 68793 => true, + 68794 => true, + 68795 => true, + 68796 => true, + 68797 => true, + 68798 => true, + 68799 => true, + 68851 => true, + 68852 => true, + 68853 => true, + 68854 => true, + 68855 => true, + 68856 => true, + 68857 => true, + 68904 => true, + 68905 => true, + 68906 => true, + 68907 => true, + 68908 => true, + 68909 => true, + 68910 => true, + 68911 => true, + 69247 => true, + 69290 => true, + 69294 => true, + 69295 => true, + 69416 => true, + 69417 => true, + 69418 => true, + 69419 => true, + 69420 => true, + 69421 => true, + 69422 => true, + 69423 => true, + 69466 => true, + 69467 => true, + 69468 => true, + 69469 => true, + 69470 => true, + 69471 => true, + 69472 => true, + 69473 => true, + 69474 => true, + 69475 => true, + 69476 => true, + 69477 => true, + 69478 => true, + 69479 => true, + 69480 => true, + 69481 => true, + 69482 => true, + 69483 => true, + 69484 => true, + 69485 => true, + 69486 => true, + 69487 => true, + 69580 => true, + 69581 => true, + 69582 => true, + 69583 => true, + 69584 => true, + 69585 => true, + 69586 => true, + 69587 => true, + 69588 => true, + 69589 => true, + 69590 => true, + 69591 => true, + 69592 => true, + 69593 => true, + 69594 => true, + 69595 => true, + 69596 => true, + 69597 => true, + 69598 => true, + 69599 => true, + 69623 => true, + 69624 => true, + 69625 => true, + 69626 => true, + 69627 => true, + 69628 => true, + 69629 => true, + 69630 => true, + 69631 => true, + 69710 => true, + 69711 => true, + 69712 => true, + 69713 => true, + 69750 => true, + 69751 => true, + 69752 => true, + 69753 => true, + 69754 => true, + 69755 => true, + 69756 => true, + 69757 => true, + 69758 => true, + 69821 => true, + 69827 => true, + 69828 => true, + 69829 => true, + 69830 => true, + 69831 => true, + 69832 => true, + 69833 => true, + 69834 => true, + 69835 => true, + 69836 => true, + 69837 => true, + 69838 => true, + 69839 => true, + 69865 => true, + 69866 => true, + 69867 => true, + 69868 => true, + 69869 => true, + 69870 => true, + 69871 => true, + 69882 => true, + 69883 => true, + 69884 => true, + 69885 => true, + 69886 => true, + 69887 => true, + 69941 => true, + 69960 => true, + 69961 => true, + 69962 => true, + 69963 => true, + 69964 => true, + 69965 => true, + 69966 => true, + 69967 => true, + 70007 => true, + 70008 => true, + 70009 => true, + 70010 => true, + 70011 => true, + 70012 => true, + 70013 => true, + 70014 => true, + 70015 => true, + 70112 => true, + 70133 => true, + 70134 => true, + 70135 => true, + 70136 => true, + 70137 => true, + 70138 => true, + 70139 => true, + 70140 => true, + 70141 => true, + 70142 => true, + 70143 => true, + 70162 => true, + 70279 => true, + 70281 => true, + 70286 => true, + 70302 => true, + 70314 => true, + 70315 => true, + 70316 => true, + 70317 => true, + 70318 => true, + 70319 => true, + 70379 => true, + 70380 => true, + 70381 => true, + 70382 => true, + 70383 => true, + 70394 => true, + 70395 => true, + 70396 => true, + 70397 => true, + 70398 => true, + 70399 => true, + 70404 => true, + 70413 => true, + 70414 => true, + 70417 => true, + 70418 => true, + 70441 => true, + 70449 => true, + 70452 => true, + 70458 => true, + 70469 => true, + 70470 => true, + 70473 => true, + 70474 => true, + 70478 => true, + 70479 => true, + 70481 => true, + 70482 => true, + 70483 => true, + 70484 => true, + 70485 => true, + 70486 => true, + 70488 => true, + 70489 => true, + 70490 => true, + 70491 => true, + 70492 => true, + 70500 => true, + 70501 => true, + 70509 => true, + 70510 => true, + 70511 => true, + 70748 => true, + 70754 => true, + 70755 => true, + 70756 => true, + 70757 => true, + 70758 => true, + 70759 => true, + 70760 => true, + 70761 => true, + 70762 => true, + 70763 => true, + 70764 => true, + 70765 => true, + 70766 => true, + 70767 => true, + 70768 => true, + 70769 => true, + 70770 => true, + 70771 => true, + 70772 => true, + 70773 => true, + 70774 => true, + 70775 => true, + 70776 => true, + 70777 => true, + 70778 => true, + 70779 => true, + 70780 => true, + 70781 => true, + 70782 => true, + 70783 => true, + 70856 => true, + 70857 => true, + 70858 => true, + 70859 => true, + 70860 => true, + 70861 => true, + 70862 => true, + 70863 => true, + 71094 => true, + 71095 => true, + 71237 => true, + 71238 => true, + 71239 => true, + 71240 => true, + 71241 => true, + 71242 => true, + 71243 => true, + 71244 => true, + 71245 => true, + 71246 => true, + 71247 => true, + 71258 => true, + 71259 => true, + 71260 => true, + 71261 => true, + 71262 => true, + 71263 => true, + 71277 => true, + 71278 => true, + 71279 => true, + 71280 => true, + 71281 => true, + 71282 => true, + 71283 => true, + 71284 => true, + 71285 => true, + 71286 => true, + 71287 => true, + 71288 => true, + 71289 => true, + 71290 => true, + 71291 => true, + 71292 => true, + 71293 => true, + 71294 => true, + 71295 => true, + 71354 => true, + 71355 => true, + 71356 => true, + 71357 => true, + 71358 => true, + 71359 => true, + 71451 => true, + 71452 => true, + 71468 => true, + 71469 => true, + 71470 => true, + 71471 => true, + 71923 => true, + 71924 => true, + 71925 => true, + 71926 => true, + 71927 => true, + 71928 => true, + 71929 => true, + 71930 => true, + 71931 => true, + 71932 => true, + 71933 => true, + 71934 => true, + 71943 => true, + 71944 => true, + 71946 => true, + 71947 => true, + 71956 => true, + 71959 => true, + 71990 => true, + 71993 => true, + 71994 => true, + 72007 => true, + 72008 => true, + 72009 => true, + 72010 => true, + 72011 => true, + 72012 => true, + 72013 => true, + 72014 => true, + 72015 => true, + 72104 => true, + 72105 => true, + 72152 => true, + 72153 => true, + 72165 => true, + 72166 => true, + 72167 => true, + 72168 => true, + 72169 => true, + 72170 => true, + 72171 => true, + 72172 => true, + 72173 => true, + 72174 => true, + 72175 => true, + 72176 => true, + 72177 => true, + 72178 => true, + 72179 => true, + 72180 => true, + 72181 => true, + 72182 => true, + 72183 => true, + 72184 => true, + 72185 => true, + 72186 => true, + 72187 => true, + 72188 => true, + 72189 => true, + 72190 => true, + 72191 => true, + 72264 => true, + 72265 => true, + 72266 => true, + 72267 => true, + 72268 => true, + 72269 => true, + 72270 => true, + 72271 => true, + 72355 => true, + 72356 => true, + 72357 => true, + 72358 => true, + 72359 => true, + 72360 => true, + 72361 => true, + 72362 => true, + 72363 => true, + 72364 => true, + 72365 => true, + 72366 => true, + 72367 => true, + 72713 => true, + 72759 => true, + 72774 => true, + 72775 => true, + 72776 => true, + 72777 => true, + 72778 => true, + 72779 => true, + 72780 => true, + 72781 => true, + 72782 => true, + 72783 => true, + 72813 => true, + 72814 => true, + 72815 => true, + 72848 => true, + 72849 => true, + 72872 => true, + 72967 => true, + 72970 => true, + 73015 => true, + 73016 => true, + 73017 => true, + 73019 => true, + 73022 => true, + 73032 => true, + 73033 => true, + 73034 => true, + 73035 => true, + 73036 => true, + 73037 => true, + 73038 => true, + 73039 => true, + 73050 => true, + 73051 => true, + 73052 => true, + 73053 => true, + 73054 => true, + 73055 => true, + 73062 => true, + 73065 => true, + 73103 => true, + 73106 => true, + 73113 => true, + 73114 => true, + 73115 => true, + 73116 => true, + 73117 => true, + 73118 => true, + 73119 => true, + 73649 => true, + 73650 => true, + 73651 => true, + 73652 => true, + 73653 => true, + 73654 => true, + 73655 => true, + 73656 => true, + 73657 => true, + 73658 => true, + 73659 => true, + 73660 => true, + 73661 => true, + 73662 => true, + 73663 => true, + 73714 => true, + 73715 => true, + 73716 => true, + 73717 => true, + 73718 => true, + 73719 => true, + 73720 => true, + 73721 => true, + 73722 => true, + 73723 => true, + 73724 => true, + 73725 => true, + 73726 => true, + 74863 => true, + 74869 => true, + 74870 => true, + 74871 => true, + 74872 => true, + 74873 => true, + 74874 => true, + 74875 => true, + 74876 => true, + 74877 => true, + 74878 => true, + 74879 => true, + 77811 => true, + 77812 => true, + 77813 => true, + 77814 => true, + 77815 => true, + 77816 => true, + 77817 => true, + 77818 => true, + 77819 => true, + 77820 => true, + 77821 => true, + 77822 => true, + 77823 => true, + 78895 => true, + 78896 => true, + 78897 => true, + 78898 => true, + 78899 => true, + 78900 => true, + 78901 => true, + 78902 => true, + 78903 => true, + 78904 => true, + 92729 => true, + 92730 => true, + 92731 => true, + 92732 => true, + 92733 => true, + 92734 => true, + 92735 => true, + 92767 => true, + 92778 => true, + 92779 => true, + 92780 => true, + 92781 => true, + 92863 => true, + 92874 => true, + 92875 => true, + 92876 => true, + 92877 => true, + 92878 => true, + 92879 => true, + 92910 => true, + 92911 => true, + 92918 => true, + 92919 => true, + 92920 => true, + 92921 => true, + 92922 => true, + 92923 => true, + 92924 => true, + 92925 => true, + 92926 => true, + 92927 => true, + 92998 => true, + 92999 => true, + 93000 => true, + 93001 => true, + 93002 => true, + 93003 => true, + 93004 => true, + 93005 => true, + 93006 => true, + 93007 => true, + 93018 => true, + 93026 => true, + 93048 => true, + 93049 => true, + 93050 => true, + 93051 => true, + 93052 => true, + 94027 => true, + 94028 => true, + 94029 => true, + 94030 => true, + 94088 => true, + 94089 => true, + 94090 => true, + 94091 => true, + 94092 => true, + 94093 => true, + 94094 => true, + 94181 => true, + 94182 => true, + 94183 => true, + 94184 => true, + 94185 => true, + 94186 => true, + 94187 => true, + 94188 => true, + 94189 => true, + 94190 => true, + 94191 => true, + 94194 => true, + 94195 => true, + 94196 => true, + 94197 => true, + 94198 => true, + 94199 => true, + 94200 => true, + 94201 => true, + 94202 => true, + 94203 => true, + 94204 => true, + 94205 => true, + 94206 => true, + 94207 => true, + 100344 => true, + 100345 => true, + 100346 => true, + 100347 => true, + 100348 => true, + 100349 => true, + 100350 => true, + 100351 => true, + 110580 => true, + 110588 => true, + 110591 => true, + 110931 => true, + 110932 => true, + 110933 => true, + 110934 => true, + 110935 => true, + 110936 => true, + 110937 => true, + 110938 => true, + 110939 => true, + 110940 => true, + 110941 => true, + 110942 => true, + 110943 => true, + 110944 => true, + 110945 => true, + 110946 => true, + 110947 => true, + 110952 => true, + 110953 => true, + 110954 => true, + 110955 => true, + 110956 => true, + 110957 => true, + 110958 => true, + 110959 => true, + 113771 => true, + 113772 => true, + 113773 => true, + 113774 => true, + 113775 => true, + 113789 => true, + 113790 => true, + 113791 => true, + 113801 => true, + 113802 => true, + 113803 => true, + 113804 => true, + 113805 => true, + 113806 => true, + 113807 => true, + 113818 => true, + 113819 => true, + 118574 => true, + 118575 => true, + 118599 => true, + 118600 => true, + 118601 => true, + 118602 => true, + 118603 => true, + 118604 => true, + 118605 => true, + 118606 => true, + 118607 => true, + 119030 => true, + 119031 => true, + 119032 => true, + 119033 => true, + 119034 => true, + 119035 => true, + 119036 => true, + 119037 => true, + 119038 => true, + 119039 => true, + 119079 => true, + 119080 => true, + 119155 => true, + 119156 => true, + 119157 => true, + 119158 => true, + 119159 => true, + 119160 => true, + 119161 => true, + 119162 => true, + 119275 => true, + 119276 => true, + 119277 => true, + 119278 => true, + 119279 => true, + 119280 => true, + 119281 => true, + 119282 => true, + 119283 => true, + 119284 => true, + 119285 => true, + 119286 => true, + 119287 => true, + 119288 => true, + 119289 => true, + 119290 => true, + 119291 => true, + 119292 => true, + 119293 => true, + 119294 => true, + 119295 => true, + 119540 => true, + 119541 => true, + 119542 => true, + 119543 => true, + 119544 => true, + 119545 => true, + 119546 => true, + 119547 => true, + 119548 => true, + 119549 => true, + 119550 => true, + 119551 => true, + 119639 => true, + 119640 => true, + 119641 => true, + 119642 => true, + 119643 => true, + 119644 => true, + 119645 => true, + 119646 => true, + 119647 => true, + 119893 => true, + 119965 => true, + 119968 => true, + 119969 => true, + 119971 => true, + 119972 => true, + 119975 => true, + 119976 => true, + 119981 => true, + 119994 => true, + 119996 => true, + 120004 => true, + 120070 => true, + 120075 => true, + 120076 => true, + 120085 => true, + 120093 => true, + 120122 => true, + 120127 => true, + 120133 => true, + 120135 => true, + 120136 => true, + 120137 => true, + 120145 => true, + 120486 => true, + 120487 => true, + 120780 => true, + 120781 => true, + 121484 => true, + 121485 => true, + 121486 => true, + 121487 => true, + 121488 => true, + 121489 => true, + 121490 => true, + 121491 => true, + 121492 => true, + 121493 => true, + 121494 => true, + 121495 => true, + 121496 => true, + 121497 => true, + 121498 => true, + 121504 => true, + 122887 => true, + 122905 => true, + 122906 => true, + 122914 => true, + 122917 => true, + 123181 => true, + 123182 => true, + 123183 => true, + 123198 => true, + 123199 => true, + 123210 => true, + 123211 => true, + 123212 => true, + 123213 => true, + 123567 => true, + 123568 => true, + 123569 => true, + 123570 => true, + 123571 => true, + 123572 => true, + 123573 => true, + 123574 => true, + 123575 => true, + 123576 => true, + 123577 => true, + 123578 => true, + 123579 => true, + 123580 => true, + 123581 => true, + 123582 => true, + 123583 => true, + 123642 => true, + 123643 => true, + 123644 => true, + 123645 => true, + 123646 => true, + 124903 => true, + 124908 => true, + 124911 => true, + 124927 => true, + 125125 => true, + 125126 => true, + 125260 => true, + 125261 => true, + 125262 => true, + 125263 => true, + 125274 => true, + 125275 => true, + 125276 => true, + 125277 => true, + 126468 => true, + 126496 => true, + 126499 => true, + 126501 => true, + 126502 => true, + 126504 => true, + 126515 => true, + 126520 => true, + 126522 => true, + 126524 => true, + 126525 => true, + 126526 => true, + 126527 => true, + 126528 => true, + 126529 => true, + 126531 => true, + 126532 => true, + 126533 => true, + 126534 => true, + 126536 => true, + 126538 => true, + 126540 => true, + 126544 => true, + 126547 => true, + 126549 => true, + 126550 => true, + 126552 => true, + 126554 => true, + 126556 => true, + 126558 => true, + 126560 => true, + 126563 => true, + 126565 => true, + 126566 => true, + 126571 => true, + 126579 => true, + 126584 => true, + 126589 => true, + 126591 => true, + 126602 => true, + 126620 => true, + 126621 => true, + 126622 => true, + 126623 => true, + 126624 => true, + 126628 => true, + 126634 => true, + 127020 => true, + 127021 => true, + 127022 => true, + 127023 => true, + 127124 => true, + 127125 => true, + 127126 => true, + 127127 => true, + 127128 => true, + 127129 => true, + 127130 => true, + 127131 => true, + 127132 => true, + 127133 => true, + 127134 => true, + 127135 => true, + 127151 => true, + 127152 => true, + 127168 => true, + 127184 => true, + 127222 => true, + 127223 => true, + 127224 => true, + 127225 => true, + 127226 => true, + 127227 => true, + 127228 => true, + 127229 => true, + 127230 => true, + 127231 => true, + 127232 => true, + 127491 => true, + 127492 => true, + 127493 => true, + 127494 => true, + 127495 => true, + 127496 => true, + 127497 => true, + 127498 => true, + 127499 => true, + 127500 => true, + 127501 => true, + 127502 => true, + 127503 => true, + 127548 => true, + 127549 => true, + 127550 => true, + 127551 => true, + 127561 => true, + 127562 => true, + 127563 => true, + 127564 => true, + 127565 => true, + 127566 => true, + 127567 => true, + 127570 => true, + 127571 => true, + 127572 => true, + 127573 => true, + 127574 => true, + 127575 => true, + 127576 => true, + 127577 => true, + 127578 => true, + 127579 => true, + 127580 => true, + 127581 => true, + 127582 => true, + 127583 => true, + 128728 => true, + 128729 => true, + 128730 => true, + 128731 => true, + 128732 => true, + 128749 => true, + 128750 => true, + 128751 => true, + 128765 => true, + 128766 => true, + 128767 => true, + 128884 => true, + 128885 => true, + 128886 => true, + 128887 => true, + 128888 => true, + 128889 => true, + 128890 => true, + 128891 => true, + 128892 => true, + 128893 => true, + 128894 => true, + 128895 => true, + 128985 => true, + 128986 => true, + 128987 => true, + 128988 => true, + 128989 => true, + 128990 => true, + 128991 => true, + 129004 => true, + 129005 => true, + 129006 => true, + 129007 => true, + 129009 => true, + 129010 => true, + 129011 => true, + 129012 => true, + 129013 => true, + 129014 => true, + 129015 => true, + 129016 => true, + 129017 => true, + 129018 => true, + 129019 => true, + 129020 => true, + 129021 => true, + 129022 => true, + 129023 => true, + 129036 => true, + 129037 => true, + 129038 => true, + 129039 => true, + 129096 => true, + 129097 => true, + 129098 => true, + 129099 => true, + 129100 => true, + 129101 => true, + 129102 => true, + 129103 => true, + 129114 => true, + 129115 => true, + 129116 => true, + 129117 => true, + 129118 => true, + 129119 => true, + 129160 => true, + 129161 => true, + 129162 => true, + 129163 => true, + 129164 => true, + 129165 => true, + 129166 => true, + 129167 => true, + 129198 => true, + 129199 => true, + 129620 => true, + 129621 => true, + 129622 => true, + 129623 => true, + 129624 => true, + 129625 => true, + 129626 => true, + 129627 => true, + 129628 => true, + 129629 => true, + 129630 => true, + 129631 => true, + 129646 => true, + 129647 => true, + 129653 => true, + 129654 => true, + 129655 => true, + 129661 => true, + 129662 => true, + 129663 => true, + 129671 => true, + 129672 => true, + 129673 => true, + 129674 => true, + 129675 => true, + 129676 => true, + 129677 => true, + 129678 => true, + 129679 => true, + 129709 => true, + 129710 => true, + 129711 => true, + 129723 => true, + 129724 => true, + 129725 => true, + 129726 => true, + 129727 => true, + 129734 => true, + 129735 => true, + 129736 => true, + 129737 => true, + 129738 => true, + 129739 => true, + 129740 => true, + 129741 => true, + 129742 => true, + 129743 => true, + 129754 => true, + 129755 => true, + 129756 => true, + 129757 => true, + 129758 => true, + 129759 => true, + 129768 => true, + 129769 => true, + 129770 => true, + 129771 => true, + 129772 => true, + 129773 => true, + 129774 => true, + 129775 => true, + 129783 => true, + 129784 => true, + 129785 => true, + 129786 => true, + 129787 => true, + 129788 => true, + 129789 => true, + 129790 => true, + 129791 => true, + 129939 => true, + 131070 => true, + 131071 => true, + 177977 => true, + 177978 => true, + 177979 => true, + 177980 => true, + 177981 => true, + 177982 => true, + 177983 => true, + 178206 => true, + 178207 => true, + 183970 => true, + 183971 => true, + 183972 => true, + 183973 => true, + 183974 => true, + 183975 => true, + 183976 => true, + 183977 => true, + 183978 => true, + 183979 => true, + 183980 => true, + 183981 => true, + 183982 => true, + 183983 => true, + 194664 => true, + 194676 => true, + 194847 => true, + 194911 => true, + 195007 => true, + 196606 => true, + 196607 => true, + 262142 => true, + 262143 => true, + 327678 => true, + 327679 => true, + 393214 => true, + 393215 => true, + 458750 => true, + 458751 => true, + 524286 => true, + 524287 => true, + 589822 => true, + 589823 => true, + 655358 => true, + 655359 => true, + 720894 => true, + 720895 => true, + 786430 => true, + 786431 => true, + 851966 => true, + 851967 => true, + 917502 => true, + 917503 => true, + 917504 => true, + 917505 => true, + 917506 => true, + 917507 => true, + 917508 => true, + 917509 => true, + 917510 => true, + 917511 => true, + 917512 => true, + 917513 => true, + 917514 => true, + 917515 => true, + 917516 => true, + 917517 => true, + 917518 => true, + 917519 => true, + 917520 => true, + 917521 => true, + 917522 => true, + 917523 => true, + 917524 => true, + 917525 => true, + 917526 => true, + 917527 => true, + 917528 => true, + 917529 => true, + 917530 => true, + 917531 => true, + 917532 => true, + 917533 => true, + 917534 => true, + 917535 => true, + 983038 => true, + 983039 => true, + 1048574 => true, + 1048575 => true, + 1114110 => true, + 1114111 => true, +); diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed_STD3_mapped.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed_STD3_mapped.php new file mode 100644 index 0000000000..54f21cc0cd --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed_STD3_mapped.php @@ -0,0 +1,308 @@ + ' ', + 168 => ' ̈', + 175 => ' ̄', + 180 => ' ́', + 184 => ' ̧', + 728 => ' ̆', + 729 => ' ̇', + 730 => ' ̊', + 731 => ' ̨', + 732 => ' ̃', + 733 => ' ̋', + 890 => ' ι', + 894 => ';', + 900 => ' ́', + 901 => ' ̈́', + 8125 => ' ̓', + 8127 => ' ̓', + 8128 => ' ͂', + 8129 => ' ̈͂', + 8141 => ' ̓̀', + 8142 => ' ̓́', + 8143 => ' ̓͂', + 8157 => ' ̔̀', + 8158 => ' ̔́', + 8159 => ' ̔͂', + 8173 => ' ̈̀', + 8174 => ' ̈́', + 8175 => '`', + 8189 => ' ́', + 8190 => ' ̔', + 8192 => ' ', + 8193 => ' ', + 8194 => ' ', + 8195 => ' ', + 8196 => ' ', + 8197 => ' ', + 8198 => ' ', + 8199 => ' ', + 8200 => ' ', + 8201 => ' ', + 8202 => ' ', + 8215 => ' ̳', + 8239 => ' ', + 8252 => '!!', + 8254 => ' ̅', + 8263 => '??', + 8264 => '?!', + 8265 => '!?', + 8287 => ' ', + 8314 => '+', + 8316 => '=', + 8317 => '(', + 8318 => ')', + 8330 => '+', + 8332 => '=', + 8333 => '(', + 8334 => ')', + 8448 => 'a/c', + 8449 => 'a/s', + 8453 => 'c/o', + 8454 => 'c/u', + 9332 => '(1)', + 9333 => '(2)', + 9334 => '(3)', + 9335 => '(4)', + 9336 => '(5)', + 9337 => '(6)', + 9338 => '(7)', + 9339 => '(8)', + 9340 => '(9)', + 9341 => '(10)', + 9342 => '(11)', + 9343 => '(12)', + 9344 => '(13)', + 9345 => '(14)', + 9346 => '(15)', + 9347 => '(16)', + 9348 => '(17)', + 9349 => '(18)', + 9350 => '(19)', + 9351 => '(20)', + 9372 => '(a)', + 9373 => '(b)', + 9374 => '(c)', + 9375 => '(d)', + 9376 => '(e)', + 9377 => '(f)', + 9378 => '(g)', + 9379 => '(h)', + 9380 => '(i)', + 9381 => '(j)', + 9382 => '(k)', + 9383 => '(l)', + 9384 => '(m)', + 9385 => '(n)', + 9386 => '(o)', + 9387 => '(p)', + 9388 => '(q)', + 9389 => '(r)', + 9390 => '(s)', + 9391 => '(t)', + 9392 => '(u)', + 9393 => '(v)', + 9394 => '(w)', + 9395 => '(x)', + 9396 => '(y)', + 9397 => '(z)', + 10868 => '::=', + 10869 => '==', + 10870 => '===', + 12288 => ' ', + 12443 => ' ゙', + 12444 => ' ゚', + 12800 => '(ᄀ)', + 12801 => '(ᄂ)', + 12802 => '(ᄃ)', + 12803 => '(ᄅ)', + 12804 => '(ᄆ)', + 12805 => '(ᄇ)', + 12806 => '(ᄉ)', + 12807 => '(ᄋ)', + 12808 => '(ᄌ)', + 12809 => '(ᄎ)', + 12810 => '(ᄏ)', + 12811 => '(ᄐ)', + 12812 => '(ᄑ)', + 12813 => '(ᄒ)', + 12814 => '(가)', + 12815 => '(나)', + 12816 => '(다)', + 12817 => '(라)', + 12818 => '(마)', + 12819 => '(바)', + 12820 => '(사)', + 12821 => '(아)', + 12822 => '(자)', + 12823 => '(차)', + 12824 => '(카)', + 12825 => '(타)', + 12826 => '(파)', + 12827 => '(하)', + 12828 => '(주)', + 12829 => '(오전)', + 12830 => '(오후)', + 12832 => '(一)', + 12833 => '(二)', + 12834 => '(三)', + 12835 => '(四)', + 12836 => '(五)', + 12837 => '(六)', + 12838 => '(七)', + 12839 => '(八)', + 12840 => '(九)', + 12841 => '(十)', + 12842 => '(月)', + 12843 => '(火)', + 12844 => '(水)', + 12845 => '(木)', + 12846 => '(金)', + 12847 => '(土)', + 12848 => '(日)', + 12849 => '(株)', + 12850 => '(有)', + 12851 => '(社)', + 12852 => '(名)', + 12853 => '(特)', + 12854 => '(財)', + 12855 => '(祝)', + 12856 => '(労)', + 12857 => '(代)', + 12858 => '(呼)', + 12859 => '(学)', + 12860 => '(監)', + 12861 => '(企)', + 12862 => '(資)', + 12863 => '(協)', + 12864 => '(祭)', + 12865 => '(休)', + 12866 => '(自)', + 12867 => '(至)', + 64297 => '+', + 64606 => ' ٌّ', + 64607 => ' ٍّ', + 64608 => ' َّ', + 64609 => ' ُّ', + 64610 => ' ِّ', + 64611 => ' ّٰ', + 65018 => 'صلى الله عليه وسلم', + 65019 => 'جل جلاله', + 65040 => ',', + 65043 => ':', + 65044 => ';', + 65045 => '!', + 65046 => '?', + 65075 => '_', + 65076 => '_', + 65077 => '(', + 65078 => ')', + 65079 => '{', + 65080 => '}', + 65095 => '[', + 65096 => ']', + 65097 => ' ̅', + 65098 => ' ̅', + 65099 => ' ̅', + 65100 => ' ̅', + 65101 => '_', + 65102 => '_', + 65103 => '_', + 65104 => ',', + 65108 => ';', + 65109 => ':', + 65110 => '?', + 65111 => '!', + 65113 => '(', + 65114 => ')', + 65115 => '{', + 65116 => '}', + 65119 => '#', + 65120 => '&', + 65121 => '*', + 65122 => '+', + 65124 => '<', + 65125 => '>', + 65126 => '=', + 65128 => '\\', + 65129 => '$', + 65130 => '%', + 65131 => '@', + 65136 => ' ً', + 65138 => ' ٌ', + 65140 => ' ٍ', + 65142 => ' َ', + 65144 => ' ُ', + 65146 => ' ِ', + 65148 => ' ّ', + 65150 => ' ْ', + 65281 => '!', + 65282 => '"', + 65283 => '#', + 65284 => '$', + 65285 => '%', + 65286 => '&', + 65287 => '\'', + 65288 => '(', + 65289 => ')', + 65290 => '*', + 65291 => '+', + 65292 => ',', + 65295 => '/', + 65306 => ':', + 65307 => ';', + 65308 => '<', + 65309 => '=', + 65310 => '>', + 65311 => '?', + 65312 => '@', + 65339 => '[', + 65340 => '\\', + 65341 => ']', + 65342 => '^', + 65343 => '_', + 65344 => '`', + 65371 => '{', + 65372 => '|', + 65373 => '}', + 65374 => '~', + 65507 => ' ̄', + 127233 => '0,', + 127234 => '1,', + 127235 => '2,', + 127236 => '3,', + 127237 => '4,', + 127238 => '5,', + 127239 => '6,', + 127240 => '7,', + 127241 => '8,', + 127242 => '9,', + 127248 => '(a)', + 127249 => '(b)', + 127250 => '(c)', + 127251 => '(d)', + 127252 => '(e)', + 127253 => '(f)', + 127254 => '(g)', + 127255 => '(h)', + 127256 => '(i)', + 127257 => '(j)', + 127258 => '(k)', + 127259 => '(l)', + 127260 => '(m)', + 127261 => '(n)', + 127262 => '(o)', + 127263 => '(p)', + 127264 => '(q)', + 127265 => '(r)', + 127266 => '(s)', + 127267 => '(t)', + 127268 => '(u)', + 127269 => '(v)', + 127270 => '(w)', + 127271 => '(x)', + 127272 => '(y)', + 127273 => '(z)', +); diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed_STD3_valid.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed_STD3_valid.php new file mode 100644 index 0000000000..223396ec4c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/disallowed_STD3_valid.php @@ -0,0 +1,71 @@ + true, + 1 => true, + 2 => true, + 3 => true, + 4 => true, + 5 => true, + 6 => true, + 7 => true, + 8 => true, + 9 => true, + 10 => true, + 11 => true, + 12 => true, + 13 => true, + 14 => true, + 15 => true, + 16 => true, + 17 => true, + 18 => true, + 19 => true, + 20 => true, + 21 => true, + 22 => true, + 23 => true, + 24 => true, + 25 => true, + 26 => true, + 27 => true, + 28 => true, + 29 => true, + 30 => true, + 31 => true, + 32 => true, + 33 => true, + 34 => true, + 35 => true, + 36 => true, + 37 => true, + 38 => true, + 39 => true, + 40 => true, + 41 => true, + 42 => true, + 43 => true, + 44 => true, + 47 => true, + 58 => true, + 59 => true, + 60 => true, + 61 => true, + 62 => true, + 63 => true, + 64 => true, + 91 => true, + 92 => true, + 93 => true, + 94 => true, + 95 => true, + 96 => true, + 123 => true, + 124 => true, + 125 => true, + 126 => true, + 127 => true, + 8800 => true, + 8814 => true, + 8815 => true, +); diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/ignored.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/ignored.php new file mode 100644 index 0000000000..c421fe3261 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/ignored.php @@ -0,0 +1,274 @@ + true, + 847 => true, + 6155 => true, + 6156 => true, + 6157 => true, + 6159 => true, + 8203 => true, + 8288 => true, + 8292 => true, + 65024 => true, + 65025 => true, + 65026 => true, + 65027 => true, + 65028 => true, + 65029 => true, + 65030 => true, + 65031 => true, + 65032 => true, + 65033 => true, + 65034 => true, + 65035 => true, + 65036 => true, + 65037 => true, + 65038 => true, + 65039 => true, + 65279 => true, + 113824 => true, + 113825 => true, + 113826 => true, + 113827 => true, + 917760 => true, + 917761 => true, + 917762 => true, + 917763 => true, + 917764 => true, + 917765 => true, + 917766 => true, + 917767 => true, + 917768 => true, + 917769 => true, + 917770 => true, + 917771 => true, + 917772 => true, + 917773 => true, + 917774 => true, + 917775 => true, + 917776 => true, + 917777 => true, + 917778 => true, + 917779 => true, + 917780 => true, + 917781 => true, + 917782 => true, + 917783 => true, + 917784 => true, + 917785 => true, + 917786 => true, + 917787 => true, + 917788 => true, + 917789 => true, + 917790 => true, + 917791 => true, + 917792 => true, + 917793 => true, + 917794 => true, + 917795 => true, + 917796 => true, + 917797 => true, + 917798 => true, + 917799 => true, + 917800 => true, + 917801 => true, + 917802 => true, + 917803 => true, + 917804 => true, + 917805 => true, + 917806 => true, + 917807 => true, + 917808 => true, + 917809 => true, + 917810 => true, + 917811 => true, + 917812 => true, + 917813 => true, + 917814 => true, + 917815 => true, + 917816 => true, + 917817 => true, + 917818 => true, + 917819 => true, + 917820 => true, + 917821 => true, + 917822 => true, + 917823 => true, + 917824 => true, + 917825 => true, + 917826 => true, + 917827 => true, + 917828 => true, + 917829 => true, + 917830 => true, + 917831 => true, + 917832 => true, + 917833 => true, + 917834 => true, + 917835 => true, + 917836 => true, + 917837 => true, + 917838 => true, + 917839 => true, + 917840 => true, + 917841 => true, + 917842 => true, + 917843 => true, + 917844 => true, + 917845 => true, + 917846 => true, + 917847 => true, + 917848 => true, + 917849 => true, + 917850 => true, + 917851 => true, + 917852 => true, + 917853 => true, + 917854 => true, + 917855 => true, + 917856 => true, + 917857 => true, + 917858 => true, + 917859 => true, + 917860 => true, + 917861 => true, + 917862 => true, + 917863 => true, + 917864 => true, + 917865 => true, + 917866 => true, + 917867 => true, + 917868 => true, + 917869 => true, + 917870 => true, + 917871 => true, + 917872 => true, + 917873 => true, + 917874 => true, + 917875 => true, + 917876 => true, + 917877 => true, + 917878 => true, + 917879 => true, + 917880 => true, + 917881 => true, + 917882 => true, + 917883 => true, + 917884 => true, + 917885 => true, + 917886 => true, + 917887 => true, + 917888 => true, + 917889 => true, + 917890 => true, + 917891 => true, + 917892 => true, + 917893 => true, + 917894 => true, + 917895 => true, + 917896 => true, + 917897 => true, + 917898 => true, + 917899 => true, + 917900 => true, + 917901 => true, + 917902 => true, + 917903 => true, + 917904 => true, + 917905 => true, + 917906 => true, + 917907 => true, + 917908 => true, + 917909 => true, + 917910 => true, + 917911 => true, + 917912 => true, + 917913 => true, + 917914 => true, + 917915 => true, + 917916 => true, + 917917 => true, + 917918 => true, + 917919 => true, + 917920 => true, + 917921 => true, + 917922 => true, + 917923 => true, + 917924 => true, + 917925 => true, + 917926 => true, + 917927 => true, + 917928 => true, + 917929 => true, + 917930 => true, + 917931 => true, + 917932 => true, + 917933 => true, + 917934 => true, + 917935 => true, + 917936 => true, + 917937 => true, + 917938 => true, + 917939 => true, + 917940 => true, + 917941 => true, + 917942 => true, + 917943 => true, + 917944 => true, + 917945 => true, + 917946 => true, + 917947 => true, + 917948 => true, + 917949 => true, + 917950 => true, + 917951 => true, + 917952 => true, + 917953 => true, + 917954 => true, + 917955 => true, + 917956 => true, + 917957 => true, + 917958 => true, + 917959 => true, + 917960 => true, + 917961 => true, + 917962 => true, + 917963 => true, + 917964 => true, + 917965 => true, + 917966 => true, + 917967 => true, + 917968 => true, + 917969 => true, + 917970 => true, + 917971 => true, + 917972 => true, + 917973 => true, + 917974 => true, + 917975 => true, + 917976 => true, + 917977 => true, + 917978 => true, + 917979 => true, + 917980 => true, + 917981 => true, + 917982 => true, + 917983 => true, + 917984 => true, + 917985 => true, + 917986 => true, + 917987 => true, + 917988 => true, + 917989 => true, + 917990 => true, + 917991 => true, + 917992 => true, + 917993 => true, + 917994 => true, + 917995 => true, + 917996 => true, + 917997 => true, + 917998 => true, + 917999 => true, +); diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/mapped.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/mapped.php new file mode 100644 index 0000000000..8ff3b91d23 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/mapped.php @@ -0,0 +1,5877 @@ + 'a', + 66 => 'b', + 67 => 'c', + 68 => 'd', + 69 => 'e', + 70 => 'f', + 71 => 'g', + 72 => 'h', + 73 => 'i', + 74 => 'j', + 75 => 'k', + 76 => 'l', + 77 => 'm', + 78 => 'n', + 79 => 'o', + 80 => 'p', + 81 => 'q', + 82 => 'r', + 83 => 's', + 84 => 't', + 85 => 'u', + 86 => 'v', + 87 => 'w', + 88 => 'x', + 89 => 'y', + 90 => 'z', + 170 => 'a', + 178 => '2', + 179 => '3', + 181 => 'μ', + 185 => '1', + 186 => 'o', + 188 => '1⁄4', + 189 => '1⁄2', + 190 => '3⁄4', + 192 => 'à', + 193 => 'á', + 194 => 'â', + 195 => 'ã', + 196 => 'ä', + 197 => 'å', + 198 => 'æ', + 199 => 'ç', + 200 => 'è', + 201 => 'é', + 202 => 'ê', + 203 => 'ë', + 204 => 'ì', + 205 => 'í', + 206 => 'î', + 207 => 'ï', + 208 => 'ð', + 209 => 'ñ', + 210 => 'ò', + 211 => 'ó', + 212 => 'ô', + 213 => 'õ', + 214 => 'ö', + 216 => 'ø', + 217 => 'ù', + 218 => 'ú', + 219 => 'û', + 220 => 'ü', + 221 => 'ý', + 222 => 'þ', + 256 => 'ā', + 258 => 'ă', + 260 => 'ą', + 262 => 'ć', + 264 => 'ĉ', + 266 => 'ċ', + 268 => 'č', + 270 => 'ď', + 272 => 'đ', + 274 => 'ē', + 276 => 'ĕ', + 278 => 'ė', + 280 => 'ę', + 282 => 'ě', + 284 => 'ĝ', + 286 => 'ğ', + 288 => 'ġ', + 290 => 'ģ', + 292 => 'ĥ', + 294 => 'ħ', + 296 => 'ĩ', + 298 => 'ī', + 300 => 'ĭ', + 302 => 'į', + 304 => 'i̇', + 306 => 'ij', + 307 => 'ij', + 308 => 'ĵ', + 310 => 'ķ', + 313 => 'ĺ', + 315 => 'ļ', + 317 => 'ľ', + 319 => 'l·', + 320 => 'l·', + 321 => 'ł', + 323 => 'ń', + 325 => 'ņ', + 327 => 'ň', + 329 => 'ʼn', + 330 => 'ŋ', + 332 => 'ō', + 334 => 'ŏ', + 336 => 'ő', + 338 => 'œ', + 340 => 'ŕ', + 342 => 'ŗ', + 344 => 'ř', + 346 => 'ś', + 348 => 'ŝ', + 350 => 'ş', + 352 => 'š', + 354 => 'ţ', + 356 => 'ť', + 358 => 'ŧ', + 360 => 'ũ', + 362 => 'ū', + 364 => 'ŭ', + 366 => 'ů', + 368 => 'ű', + 370 => 'ų', + 372 => 'ŵ', + 374 => 'ŷ', + 376 => 'ÿ', + 377 => 'ź', + 379 => 'ż', + 381 => 'ž', + 383 => 's', + 385 => 'ɓ', + 386 => 'ƃ', + 388 => 'ƅ', + 390 => 'ɔ', + 391 => 'ƈ', + 393 => 'ɖ', + 394 => 'ɗ', + 395 => 'ƌ', + 398 => 'ǝ', + 399 => 'ə', + 400 => 'ɛ', + 401 => 'ƒ', + 403 => 'ɠ', + 404 => 'ɣ', + 406 => 'ɩ', + 407 => 'ɨ', + 408 => 'ƙ', + 412 => 'ɯ', + 413 => 'ɲ', + 415 => 'ɵ', + 416 => 'ơ', + 418 => 'ƣ', + 420 => 'ƥ', + 422 => 'ʀ', + 423 => 'ƨ', + 425 => 'ʃ', + 428 => 'ƭ', + 430 => 'ʈ', + 431 => 'ư', + 433 => 'ʊ', + 434 => 'ʋ', + 435 => 'ƴ', + 437 => 'ƶ', + 439 => 'ʒ', + 440 => 'ƹ', + 444 => 'ƽ', + 452 => 'dž', + 453 => 'dž', + 454 => 'dž', + 455 => 'lj', + 456 => 'lj', + 457 => 'lj', + 458 => 'nj', + 459 => 'nj', + 460 => 'nj', + 461 => 'ǎ', + 463 => 'ǐ', + 465 => 'ǒ', + 467 => 'ǔ', + 469 => 'ǖ', + 471 => 'ǘ', + 473 => 'ǚ', + 475 => 'ǜ', + 478 => 'ǟ', + 480 => 'ǡ', + 482 => 'ǣ', + 484 => 'ǥ', + 486 => 'ǧ', + 488 => 'ǩ', + 490 => 'ǫ', + 492 => 'ǭ', + 494 => 'ǯ', + 497 => 'dz', + 498 => 'dz', + 499 => 'dz', + 500 => 'ǵ', + 502 => 'ƕ', + 503 => 'ƿ', + 504 => 'ǹ', + 506 => 'ǻ', + 508 => 'ǽ', + 510 => 'ǿ', + 512 => 'ȁ', + 514 => 'ȃ', + 516 => 'ȅ', + 518 => 'ȇ', + 520 => 'ȉ', + 522 => 'ȋ', + 524 => 'ȍ', + 526 => 'ȏ', + 528 => 'ȑ', + 530 => 'ȓ', + 532 => 'ȕ', + 534 => 'ȗ', + 536 => 'ș', + 538 => 'ț', + 540 => 'ȝ', + 542 => 'ȟ', + 544 => 'ƞ', + 546 => 'ȣ', + 548 => 'ȥ', + 550 => 'ȧ', + 552 => 'ȩ', + 554 => 'ȫ', + 556 => 'ȭ', + 558 => 'ȯ', + 560 => 'ȱ', + 562 => 'ȳ', + 570 => 'ⱥ', + 571 => 'ȼ', + 573 => 'ƚ', + 574 => 'ⱦ', + 577 => 'ɂ', + 579 => 'ƀ', + 580 => 'ʉ', + 581 => 'ʌ', + 582 => 'ɇ', + 584 => 'ɉ', + 586 => 'ɋ', + 588 => 'ɍ', + 590 => 'ɏ', + 688 => 'h', + 689 => 'ɦ', + 690 => 'j', + 691 => 'r', + 692 => 'ɹ', + 693 => 'ɻ', + 694 => 'ʁ', + 695 => 'w', + 696 => 'y', + 736 => 'ɣ', + 737 => 'l', + 738 => 's', + 739 => 'x', + 740 => 'ʕ', + 832 => '̀', + 833 => '́', + 835 => '̓', + 836 => '̈́', + 837 => 'ι', + 880 => 'ͱ', + 882 => 'ͳ', + 884 => 'ʹ', + 886 => 'ͷ', + 895 => 'ϳ', + 902 => 'ά', + 903 => '·', + 904 => 'έ', + 905 => 'ή', + 906 => 'ί', + 908 => 'ό', + 910 => 'ύ', + 911 => 'ώ', + 913 => 'α', + 914 => 'β', + 915 => 'γ', + 916 => 'δ', + 917 => 'ε', + 918 => 'ζ', + 919 => 'η', + 920 => 'θ', + 921 => 'ι', + 922 => 'κ', + 923 => 'λ', + 924 => 'μ', + 925 => 'ν', + 926 => 'ξ', + 927 => 'ο', + 928 => 'π', + 929 => 'ρ', + 931 => 'σ', + 932 => 'τ', + 933 => 'υ', + 934 => 'φ', + 935 => 'χ', + 936 => 'ψ', + 937 => 'ω', + 938 => 'ϊ', + 939 => 'ϋ', + 975 => 'ϗ', + 976 => 'β', + 977 => 'θ', + 978 => 'υ', + 979 => 'ύ', + 980 => 'ϋ', + 981 => 'φ', + 982 => 'π', + 984 => 'ϙ', + 986 => 'ϛ', + 988 => 'ϝ', + 990 => 'ϟ', + 992 => 'ϡ', + 994 => 'ϣ', + 996 => 'ϥ', + 998 => 'ϧ', + 1000 => 'ϩ', + 1002 => 'ϫ', + 1004 => 'ϭ', + 1006 => 'ϯ', + 1008 => 'κ', + 1009 => 'ρ', + 1010 => 'σ', + 1012 => 'θ', + 1013 => 'ε', + 1015 => 'ϸ', + 1017 => 'σ', + 1018 => 'ϻ', + 1021 => 'ͻ', + 1022 => 'ͼ', + 1023 => 'ͽ', + 1024 => 'ѐ', + 1025 => 'ё', + 1026 => 'ђ', + 1027 => 'ѓ', + 1028 => 'є', + 1029 => 'ѕ', + 1030 => 'і', + 1031 => 'ї', + 1032 => 'ј', + 1033 => 'љ', + 1034 => 'њ', + 1035 => 'ћ', + 1036 => 'ќ', + 1037 => 'ѝ', + 1038 => 'ў', + 1039 => 'џ', + 1040 => 'а', + 1041 => 'б', + 1042 => 'в', + 1043 => 'г', + 1044 => 'д', + 1045 => 'е', + 1046 => 'ж', + 1047 => 'з', + 1048 => 'и', + 1049 => 'й', + 1050 => 'к', + 1051 => 'л', + 1052 => 'м', + 1053 => 'н', + 1054 => 'о', + 1055 => 'п', + 1056 => 'р', + 1057 => 'с', + 1058 => 'т', + 1059 => 'у', + 1060 => 'ф', + 1061 => 'х', + 1062 => 'ц', + 1063 => 'ч', + 1064 => 'ш', + 1065 => 'щ', + 1066 => 'ъ', + 1067 => 'ы', + 1068 => 'ь', + 1069 => 'э', + 1070 => 'ю', + 1071 => 'я', + 1120 => 'ѡ', + 1122 => 'ѣ', + 1124 => 'ѥ', + 1126 => 'ѧ', + 1128 => 'ѩ', + 1130 => 'ѫ', + 1132 => 'ѭ', + 1134 => 'ѯ', + 1136 => 'ѱ', + 1138 => 'ѳ', + 1140 => 'ѵ', + 1142 => 'ѷ', + 1144 => 'ѹ', + 1146 => 'ѻ', + 1148 => 'ѽ', + 1150 => 'ѿ', + 1152 => 'ҁ', + 1162 => 'ҋ', + 1164 => 'ҍ', + 1166 => 'ҏ', + 1168 => 'ґ', + 1170 => 'ғ', + 1172 => 'ҕ', + 1174 => 'җ', + 1176 => 'ҙ', + 1178 => 'қ', + 1180 => 'ҝ', + 1182 => 'ҟ', + 1184 => 'ҡ', + 1186 => 'ң', + 1188 => 'ҥ', + 1190 => 'ҧ', + 1192 => 'ҩ', + 1194 => 'ҫ', + 1196 => 'ҭ', + 1198 => 'ү', + 1200 => 'ұ', + 1202 => 'ҳ', + 1204 => 'ҵ', + 1206 => 'ҷ', + 1208 => 'ҹ', + 1210 => 'һ', + 1212 => 'ҽ', + 1214 => 'ҿ', + 1217 => 'ӂ', + 1219 => 'ӄ', + 1221 => 'ӆ', + 1223 => 'ӈ', + 1225 => 'ӊ', + 1227 => 'ӌ', + 1229 => 'ӎ', + 1232 => 'ӑ', + 1234 => 'ӓ', + 1236 => 'ӕ', + 1238 => 'ӗ', + 1240 => 'ә', + 1242 => 'ӛ', + 1244 => 'ӝ', + 1246 => 'ӟ', + 1248 => 'ӡ', + 1250 => 'ӣ', + 1252 => 'ӥ', + 1254 => 'ӧ', + 1256 => 'ө', + 1258 => 'ӫ', + 1260 => 'ӭ', + 1262 => 'ӯ', + 1264 => 'ӱ', + 1266 => 'ӳ', + 1268 => 'ӵ', + 1270 => 'ӷ', + 1272 => 'ӹ', + 1274 => 'ӻ', + 1276 => 'ӽ', + 1278 => 'ӿ', + 1280 => 'ԁ', + 1282 => 'ԃ', + 1284 => 'ԅ', + 1286 => 'ԇ', + 1288 => 'ԉ', + 1290 => 'ԋ', + 1292 => 'ԍ', + 1294 => 'ԏ', + 1296 => 'ԑ', + 1298 => 'ԓ', + 1300 => 'ԕ', + 1302 => 'ԗ', + 1304 => 'ԙ', + 1306 => 'ԛ', + 1308 => 'ԝ', + 1310 => 'ԟ', + 1312 => 'ԡ', + 1314 => 'ԣ', + 1316 => 'ԥ', + 1318 => 'ԧ', + 1320 => 'ԩ', + 1322 => 'ԫ', + 1324 => 'ԭ', + 1326 => 'ԯ', + 1329 => 'ա', + 1330 => 'բ', + 1331 => 'գ', + 1332 => 'դ', + 1333 => 'ե', + 1334 => 'զ', + 1335 => 'է', + 1336 => 'ը', + 1337 => 'թ', + 1338 => 'ժ', + 1339 => 'ի', + 1340 => 'լ', + 1341 => 'խ', + 1342 => 'ծ', + 1343 => 'կ', + 1344 => 'հ', + 1345 => 'ձ', + 1346 => 'ղ', + 1347 => 'ճ', + 1348 => 'մ', + 1349 => 'յ', + 1350 => 'ն', + 1351 => 'շ', + 1352 => 'ո', + 1353 => 'չ', + 1354 => 'պ', + 1355 => 'ջ', + 1356 => 'ռ', + 1357 => 'ս', + 1358 => 'վ', + 1359 => 'տ', + 1360 => 'ր', + 1361 => 'ց', + 1362 => 'ւ', + 1363 => 'փ', + 1364 => 'ք', + 1365 => 'օ', + 1366 => 'ֆ', + 1415 => 'եւ', + 1653 => 'اٴ', + 1654 => 'وٴ', + 1655 => 'ۇٴ', + 1656 => 'يٴ', + 2392 => 'क़', + 2393 => 'ख़', + 2394 => 'ग़', + 2395 => 'ज़', + 2396 => 'ड़', + 2397 => 'ढ़', + 2398 => 'फ़', + 2399 => 'य़', + 2524 => 'ড়', + 2525 => 'ঢ়', + 2527 => 'য়', + 2611 => 'ਲ਼', + 2614 => 'ਸ਼', + 2649 => 'ਖ਼', + 2650 => 'ਗ਼', + 2651 => 'ਜ਼', + 2654 => 'ਫ਼', + 2908 => 'ଡ଼', + 2909 => 'ଢ଼', + 3635 => 'ํา', + 3763 => 'ໍາ', + 3804 => 'ຫນ', + 3805 => 'ຫມ', + 3852 => '་', + 3907 => 'གྷ', + 3917 => 'ཌྷ', + 3922 => 'དྷ', + 3927 => 'བྷ', + 3932 => 'ཛྷ', + 3945 => 'ཀྵ', + 3955 => 'ཱི', + 3957 => 'ཱུ', + 3958 => 'ྲྀ', + 3959 => 'ྲཱྀ', + 3960 => 'ླྀ', + 3961 => 'ླཱྀ', + 3969 => 'ཱྀ', + 3987 => 'ྒྷ', + 3997 => 'ྜྷ', + 4002 => 'ྡྷ', + 4007 => 'ྦྷ', + 4012 => 'ྫྷ', + 4025 => 'ྐྵ', + 4295 => 'ⴧ', + 4301 => 'ⴭ', + 4348 => 'ნ', + 5112 => 'Ᏸ', + 5113 => 'Ᏹ', + 5114 => 'Ᏺ', + 5115 => 'Ᏻ', + 5116 => 'Ᏼ', + 5117 => 'Ᏽ', + 7296 => 'в', + 7297 => 'д', + 7298 => 'о', + 7299 => 'с', + 7300 => 'т', + 7301 => 'т', + 7302 => 'ъ', + 7303 => 'ѣ', + 7304 => 'ꙋ', + 7312 => 'ა', + 7313 => 'ბ', + 7314 => 'გ', + 7315 => 'დ', + 7316 => 'ე', + 7317 => 'ვ', + 7318 => 'ზ', + 7319 => 'თ', + 7320 => 'ი', + 7321 => 'კ', + 7322 => 'ლ', + 7323 => 'მ', + 7324 => 'ნ', + 7325 => 'ო', + 7326 => 'პ', + 7327 => 'ჟ', + 7328 => 'რ', + 7329 => 'ს', + 7330 => 'ტ', + 7331 => 'უ', + 7332 => 'ფ', + 7333 => 'ქ', + 7334 => 'ღ', + 7335 => 'ყ', + 7336 => 'შ', + 7337 => 'ჩ', + 7338 => 'ც', + 7339 => 'ძ', + 7340 => 'წ', + 7341 => 'ჭ', + 7342 => 'ხ', + 7343 => 'ჯ', + 7344 => 'ჰ', + 7345 => 'ჱ', + 7346 => 'ჲ', + 7347 => 'ჳ', + 7348 => 'ჴ', + 7349 => 'ჵ', + 7350 => 'ჶ', + 7351 => 'ჷ', + 7352 => 'ჸ', + 7353 => 'ჹ', + 7354 => 'ჺ', + 7357 => 'ჽ', + 7358 => 'ჾ', + 7359 => 'ჿ', + 7468 => 'a', + 7469 => 'æ', + 7470 => 'b', + 7472 => 'd', + 7473 => 'e', + 7474 => 'ǝ', + 7475 => 'g', + 7476 => 'h', + 7477 => 'i', + 7478 => 'j', + 7479 => 'k', + 7480 => 'l', + 7481 => 'm', + 7482 => 'n', + 7484 => 'o', + 7485 => 'ȣ', + 7486 => 'p', + 7487 => 'r', + 7488 => 't', + 7489 => 'u', + 7490 => 'w', + 7491 => 'a', + 7492 => 'ɐ', + 7493 => 'ɑ', + 7494 => 'ᴂ', + 7495 => 'b', + 7496 => 'd', + 7497 => 'e', + 7498 => 'ə', + 7499 => 'ɛ', + 7500 => 'ɜ', + 7501 => 'g', + 7503 => 'k', + 7504 => 'm', + 7505 => 'ŋ', + 7506 => 'o', + 7507 => 'ɔ', + 7508 => 'ᴖ', + 7509 => 'ᴗ', + 7510 => 'p', + 7511 => 't', + 7512 => 'u', + 7513 => 'ᴝ', + 7514 => 'ɯ', + 7515 => 'v', + 7516 => 'ᴥ', + 7517 => 'β', + 7518 => 'γ', + 7519 => 'δ', + 7520 => 'φ', + 7521 => 'χ', + 7522 => 'i', + 7523 => 'r', + 7524 => 'u', + 7525 => 'v', + 7526 => 'β', + 7527 => 'γ', + 7528 => 'ρ', + 7529 => 'φ', + 7530 => 'χ', + 7544 => 'н', + 7579 => 'ɒ', + 7580 => 'c', + 7581 => 'ɕ', + 7582 => 'ð', + 7583 => 'ɜ', + 7584 => 'f', + 7585 => 'ɟ', + 7586 => 'ɡ', + 7587 => 'ɥ', + 7588 => 'ɨ', + 7589 => 'ɩ', + 7590 => 'ɪ', + 7591 => 'ᵻ', + 7592 => 'ʝ', + 7593 => 'ɭ', + 7594 => 'ᶅ', + 7595 => 'ʟ', + 7596 => 'ɱ', + 7597 => 'ɰ', + 7598 => 'ɲ', + 7599 => 'ɳ', + 7600 => 'ɴ', + 7601 => 'ɵ', + 7602 => 'ɸ', + 7603 => 'ʂ', + 7604 => 'ʃ', + 7605 => 'ƫ', + 7606 => 'ʉ', + 7607 => 'ʊ', + 7608 => 'ᴜ', + 7609 => 'ʋ', + 7610 => 'ʌ', + 7611 => 'z', + 7612 => 'ʐ', + 7613 => 'ʑ', + 7614 => 'ʒ', + 7615 => 'θ', + 7680 => 'ḁ', + 7682 => 'ḃ', + 7684 => 'ḅ', + 7686 => 'ḇ', + 7688 => 'ḉ', + 7690 => 'ḋ', + 7692 => 'ḍ', + 7694 => 'ḏ', + 7696 => 'ḑ', + 7698 => 'ḓ', + 7700 => 'ḕ', + 7702 => 'ḗ', + 7704 => 'ḙ', + 7706 => 'ḛ', + 7708 => 'ḝ', + 7710 => 'ḟ', + 7712 => 'ḡ', + 7714 => 'ḣ', + 7716 => 'ḥ', + 7718 => 'ḧ', + 7720 => 'ḩ', + 7722 => 'ḫ', + 7724 => 'ḭ', + 7726 => 'ḯ', + 7728 => 'ḱ', + 7730 => 'ḳ', + 7732 => 'ḵ', + 7734 => 'ḷ', + 7736 => 'ḹ', + 7738 => 'ḻ', + 7740 => 'ḽ', + 7742 => 'ḿ', + 7744 => 'ṁ', + 7746 => 'ṃ', + 7748 => 'ṅ', + 7750 => 'ṇ', + 7752 => 'ṉ', + 7754 => 'ṋ', + 7756 => 'ṍ', + 7758 => 'ṏ', + 7760 => 'ṑ', + 7762 => 'ṓ', + 7764 => 'ṕ', + 7766 => 'ṗ', + 7768 => 'ṙ', + 7770 => 'ṛ', + 7772 => 'ṝ', + 7774 => 'ṟ', + 7776 => 'ṡ', + 7778 => 'ṣ', + 7780 => 'ṥ', + 7782 => 'ṧ', + 7784 => 'ṩ', + 7786 => 'ṫ', + 7788 => 'ṭ', + 7790 => 'ṯ', + 7792 => 'ṱ', + 7794 => 'ṳ', + 7796 => 'ṵ', + 7798 => 'ṷ', + 7800 => 'ṹ', + 7802 => 'ṻ', + 7804 => 'ṽ', + 7806 => 'ṿ', + 7808 => 'ẁ', + 7810 => 'ẃ', + 7812 => 'ẅ', + 7814 => 'ẇ', + 7816 => 'ẉ', + 7818 => 'ẋ', + 7820 => 'ẍ', + 7822 => 'ẏ', + 7824 => 'ẑ', + 7826 => 'ẓ', + 7828 => 'ẕ', + 7834 => 'aʾ', + 7835 => 'ṡ', + 7838 => 'ss', + 7840 => 'ạ', + 7842 => 'ả', + 7844 => 'ấ', + 7846 => 'ầ', + 7848 => 'ẩ', + 7850 => 'ẫ', + 7852 => 'ậ', + 7854 => 'ắ', + 7856 => 'ằ', + 7858 => 'ẳ', + 7860 => 'ẵ', + 7862 => 'ặ', + 7864 => 'ẹ', + 7866 => 'ẻ', + 7868 => 'ẽ', + 7870 => 'ế', + 7872 => 'ề', + 7874 => 'ể', + 7876 => 'ễ', + 7878 => 'ệ', + 7880 => 'ỉ', + 7882 => 'ị', + 7884 => 'ọ', + 7886 => 'ỏ', + 7888 => 'ố', + 7890 => 'ồ', + 7892 => 'ổ', + 7894 => 'ỗ', + 7896 => 'ộ', + 7898 => 'ớ', + 7900 => 'ờ', + 7902 => 'ở', + 7904 => 'ỡ', + 7906 => 'ợ', + 7908 => 'ụ', + 7910 => 'ủ', + 7912 => 'ứ', + 7914 => 'ừ', + 7916 => 'ử', + 7918 => 'ữ', + 7920 => 'ự', + 7922 => 'ỳ', + 7924 => 'ỵ', + 7926 => 'ỷ', + 7928 => 'ỹ', + 7930 => 'ỻ', + 7932 => 'ỽ', + 7934 => 'ỿ', + 7944 => 'ἀ', + 7945 => 'ἁ', + 7946 => 'ἂ', + 7947 => 'ἃ', + 7948 => 'ἄ', + 7949 => 'ἅ', + 7950 => 'ἆ', + 7951 => 'ἇ', + 7960 => 'ἐ', + 7961 => 'ἑ', + 7962 => 'ἒ', + 7963 => 'ἓ', + 7964 => 'ἔ', + 7965 => 'ἕ', + 7976 => 'ἠ', + 7977 => 'ἡ', + 7978 => 'ἢ', + 7979 => 'ἣ', + 7980 => 'ἤ', + 7981 => 'ἥ', + 7982 => 'ἦ', + 7983 => 'ἧ', + 7992 => 'ἰ', + 7993 => 'ἱ', + 7994 => 'ἲ', + 7995 => 'ἳ', + 7996 => 'ἴ', + 7997 => 'ἵ', + 7998 => 'ἶ', + 7999 => 'ἷ', + 8008 => 'ὀ', + 8009 => 'ὁ', + 8010 => 'ὂ', + 8011 => 'ὃ', + 8012 => 'ὄ', + 8013 => 'ὅ', + 8025 => 'ὑ', + 8027 => 'ὓ', + 8029 => 'ὕ', + 8031 => 'ὗ', + 8040 => 'ὠ', + 8041 => 'ὡ', + 8042 => 'ὢ', + 8043 => 'ὣ', + 8044 => 'ὤ', + 8045 => 'ὥ', + 8046 => 'ὦ', + 8047 => 'ὧ', + 8049 => 'ά', + 8051 => 'έ', + 8053 => 'ή', + 8055 => 'ί', + 8057 => 'ό', + 8059 => 'ύ', + 8061 => 'ώ', + 8064 => 'ἀι', + 8065 => 'ἁι', + 8066 => 'ἂι', + 8067 => 'ἃι', + 8068 => 'ἄι', + 8069 => 'ἅι', + 8070 => 'ἆι', + 8071 => 'ἇι', + 8072 => 'ἀι', + 8073 => 'ἁι', + 8074 => 'ἂι', + 8075 => 'ἃι', + 8076 => 'ἄι', + 8077 => 'ἅι', + 8078 => 'ἆι', + 8079 => 'ἇι', + 8080 => 'ἠι', + 8081 => 'ἡι', + 8082 => 'ἢι', + 8083 => 'ἣι', + 8084 => 'ἤι', + 8085 => 'ἥι', + 8086 => 'ἦι', + 8087 => 'ἧι', + 8088 => 'ἠι', + 8089 => 'ἡι', + 8090 => 'ἢι', + 8091 => 'ἣι', + 8092 => 'ἤι', + 8093 => 'ἥι', + 8094 => 'ἦι', + 8095 => 'ἧι', + 8096 => 'ὠι', + 8097 => 'ὡι', + 8098 => 'ὢι', + 8099 => 'ὣι', + 8100 => 'ὤι', + 8101 => 'ὥι', + 8102 => 'ὦι', + 8103 => 'ὧι', + 8104 => 'ὠι', + 8105 => 'ὡι', + 8106 => 'ὢι', + 8107 => 'ὣι', + 8108 => 'ὤι', + 8109 => 'ὥι', + 8110 => 'ὦι', + 8111 => 'ὧι', + 8114 => 'ὰι', + 8115 => 'αι', + 8116 => 'άι', + 8119 => 'ᾶι', + 8120 => 'ᾰ', + 8121 => 'ᾱ', + 8122 => 'ὰ', + 8123 => 'ά', + 8124 => 'αι', + 8126 => 'ι', + 8130 => 'ὴι', + 8131 => 'ηι', + 8132 => 'ήι', + 8135 => 'ῆι', + 8136 => 'ὲ', + 8137 => 'έ', + 8138 => 'ὴ', + 8139 => 'ή', + 8140 => 'ηι', + 8147 => 'ΐ', + 8152 => 'ῐ', + 8153 => 'ῑ', + 8154 => 'ὶ', + 8155 => 'ί', + 8163 => 'ΰ', + 8168 => 'ῠ', + 8169 => 'ῡ', + 8170 => 'ὺ', + 8171 => 'ύ', + 8172 => 'ῥ', + 8178 => 'ὼι', + 8179 => 'ωι', + 8180 => 'ώι', + 8183 => 'ῶι', + 8184 => 'ὸ', + 8185 => 'ό', + 8186 => 'ὼ', + 8187 => 'ώ', + 8188 => 'ωι', + 8209 => '‐', + 8243 => '′′', + 8244 => '′′′', + 8246 => '‵‵', + 8247 => '‵‵‵', + 8279 => '′′′′', + 8304 => '0', + 8305 => 'i', + 8308 => '4', + 8309 => '5', + 8310 => '6', + 8311 => '7', + 8312 => '8', + 8313 => '9', + 8315 => '−', + 8319 => 'n', + 8320 => '0', + 8321 => '1', + 8322 => '2', + 8323 => '3', + 8324 => '4', + 8325 => '5', + 8326 => '6', + 8327 => '7', + 8328 => '8', + 8329 => '9', + 8331 => '−', + 8336 => 'a', + 8337 => 'e', + 8338 => 'o', + 8339 => 'x', + 8340 => 'ə', + 8341 => 'h', + 8342 => 'k', + 8343 => 'l', + 8344 => 'm', + 8345 => 'n', + 8346 => 'p', + 8347 => 's', + 8348 => 't', + 8360 => 'rs', + 8450 => 'c', + 8451 => '°c', + 8455 => 'ɛ', + 8457 => '°f', + 8458 => 'g', + 8459 => 'h', + 8460 => 'h', + 8461 => 'h', + 8462 => 'h', + 8463 => 'ħ', + 8464 => 'i', + 8465 => 'i', + 8466 => 'l', + 8467 => 'l', + 8469 => 'n', + 8470 => 'no', + 8473 => 'p', + 8474 => 'q', + 8475 => 'r', + 8476 => 'r', + 8477 => 'r', + 8480 => 'sm', + 8481 => 'tel', + 8482 => 'tm', + 8484 => 'z', + 8486 => 'ω', + 8488 => 'z', + 8490 => 'k', + 8491 => 'å', + 8492 => 'b', + 8493 => 'c', + 8495 => 'e', + 8496 => 'e', + 8497 => 'f', + 8499 => 'm', + 8500 => 'o', + 8501 => 'א', + 8502 => 'ב', + 8503 => 'ג', + 8504 => 'ד', + 8505 => 'i', + 8507 => 'fax', + 8508 => 'π', + 8509 => 'γ', + 8510 => 'γ', + 8511 => 'π', + 8512 => '∑', + 8517 => 'd', + 8518 => 'd', + 8519 => 'e', + 8520 => 'i', + 8521 => 'j', + 8528 => '1⁄7', + 8529 => '1⁄9', + 8530 => '1⁄10', + 8531 => '1⁄3', + 8532 => '2⁄3', + 8533 => '1⁄5', + 8534 => '2⁄5', + 8535 => '3⁄5', + 8536 => '4⁄5', + 8537 => '1⁄6', + 8538 => '5⁄6', + 8539 => '1⁄8', + 8540 => '3⁄8', + 8541 => '5⁄8', + 8542 => '7⁄8', + 8543 => '1⁄', + 8544 => 'i', + 8545 => 'ii', + 8546 => 'iii', + 8547 => 'iv', + 8548 => 'v', + 8549 => 'vi', + 8550 => 'vii', + 8551 => 'viii', + 8552 => 'ix', + 8553 => 'x', + 8554 => 'xi', + 8555 => 'xii', + 8556 => 'l', + 8557 => 'c', + 8558 => 'd', + 8559 => 'm', + 8560 => 'i', + 8561 => 'ii', + 8562 => 'iii', + 8563 => 'iv', + 8564 => 'v', + 8565 => 'vi', + 8566 => 'vii', + 8567 => 'viii', + 8568 => 'ix', + 8569 => 'x', + 8570 => 'xi', + 8571 => 'xii', + 8572 => 'l', + 8573 => 'c', + 8574 => 'd', + 8575 => 'm', + 8585 => '0⁄3', + 8748 => '∫∫', + 8749 => '∫∫∫', + 8751 => '∮∮', + 8752 => '∮∮∮', + 9001 => '〈', + 9002 => '〉', + 9312 => '1', + 9313 => '2', + 9314 => '3', + 9315 => '4', + 9316 => '5', + 9317 => '6', + 9318 => '7', + 9319 => '8', + 9320 => '9', + 9321 => '10', + 9322 => '11', + 9323 => '12', + 9324 => '13', + 9325 => '14', + 9326 => '15', + 9327 => '16', + 9328 => '17', + 9329 => '18', + 9330 => '19', + 9331 => '20', + 9398 => 'a', + 9399 => 'b', + 9400 => 'c', + 9401 => 'd', + 9402 => 'e', + 9403 => 'f', + 9404 => 'g', + 9405 => 'h', + 9406 => 'i', + 9407 => 'j', + 9408 => 'k', + 9409 => 'l', + 9410 => 'm', + 9411 => 'n', + 9412 => 'o', + 9413 => 'p', + 9414 => 'q', + 9415 => 'r', + 9416 => 's', + 9417 => 't', + 9418 => 'u', + 9419 => 'v', + 9420 => 'w', + 9421 => 'x', + 9422 => 'y', + 9423 => 'z', + 9424 => 'a', + 9425 => 'b', + 9426 => 'c', + 9427 => 'd', + 9428 => 'e', + 9429 => 'f', + 9430 => 'g', + 9431 => 'h', + 9432 => 'i', + 9433 => 'j', + 9434 => 'k', + 9435 => 'l', + 9436 => 'm', + 9437 => 'n', + 9438 => 'o', + 9439 => 'p', + 9440 => 'q', + 9441 => 'r', + 9442 => 's', + 9443 => 't', + 9444 => 'u', + 9445 => 'v', + 9446 => 'w', + 9447 => 'x', + 9448 => 'y', + 9449 => 'z', + 9450 => '0', + 10764 => '∫∫∫∫', + 10972 => '⫝̸', + 11264 => 'ⰰ', + 11265 => 'ⰱ', + 11266 => 'ⰲ', + 11267 => 'ⰳ', + 11268 => 'ⰴ', + 11269 => 'ⰵ', + 11270 => 'ⰶ', + 11271 => 'ⰷ', + 11272 => 'ⰸ', + 11273 => 'ⰹ', + 11274 => 'ⰺ', + 11275 => 'ⰻ', + 11276 => 'ⰼ', + 11277 => 'ⰽ', + 11278 => 'ⰾ', + 11279 => 'ⰿ', + 11280 => 'ⱀ', + 11281 => 'ⱁ', + 11282 => 'ⱂ', + 11283 => 'ⱃ', + 11284 => 'ⱄ', + 11285 => 'ⱅ', + 11286 => 'ⱆ', + 11287 => 'ⱇ', + 11288 => 'ⱈ', + 11289 => 'ⱉ', + 11290 => 'ⱊ', + 11291 => 'ⱋ', + 11292 => 'ⱌ', + 11293 => 'ⱍ', + 11294 => 'ⱎ', + 11295 => 'ⱏ', + 11296 => 'ⱐ', + 11297 => 'ⱑ', + 11298 => 'ⱒ', + 11299 => 'ⱓ', + 11300 => 'ⱔ', + 11301 => 'ⱕ', + 11302 => 'ⱖ', + 11303 => 'ⱗ', + 11304 => 'ⱘ', + 11305 => 'ⱙ', + 11306 => 'ⱚ', + 11307 => 'ⱛ', + 11308 => 'ⱜ', + 11309 => 'ⱝ', + 11310 => 'ⱞ', + 11311 => 'ⱟ', + 11360 => 'ⱡ', + 11362 => 'ɫ', + 11363 => 'ᵽ', + 11364 => 'ɽ', + 11367 => 'ⱨ', + 11369 => 'ⱪ', + 11371 => 'ⱬ', + 11373 => 'ɑ', + 11374 => 'ɱ', + 11375 => 'ɐ', + 11376 => 'ɒ', + 11378 => 'ⱳ', + 11381 => 'ⱶ', + 11388 => 'j', + 11389 => 'v', + 11390 => 'ȿ', + 11391 => 'ɀ', + 11392 => 'ⲁ', + 11394 => 'ⲃ', + 11396 => 'ⲅ', + 11398 => 'ⲇ', + 11400 => 'ⲉ', + 11402 => 'ⲋ', + 11404 => 'ⲍ', + 11406 => 'ⲏ', + 11408 => 'ⲑ', + 11410 => 'ⲓ', + 11412 => 'ⲕ', + 11414 => 'ⲗ', + 11416 => 'ⲙ', + 11418 => 'ⲛ', + 11420 => 'ⲝ', + 11422 => 'ⲟ', + 11424 => 'ⲡ', + 11426 => 'ⲣ', + 11428 => 'ⲥ', + 11430 => 'ⲧ', + 11432 => 'ⲩ', + 11434 => 'ⲫ', + 11436 => 'ⲭ', + 11438 => 'ⲯ', + 11440 => 'ⲱ', + 11442 => 'ⲳ', + 11444 => 'ⲵ', + 11446 => 'ⲷ', + 11448 => 'ⲹ', + 11450 => 'ⲻ', + 11452 => 'ⲽ', + 11454 => 'ⲿ', + 11456 => 'ⳁ', + 11458 => 'ⳃ', + 11460 => 'ⳅ', + 11462 => 'ⳇ', + 11464 => 'ⳉ', + 11466 => 'ⳋ', + 11468 => 'ⳍ', + 11470 => 'ⳏ', + 11472 => 'ⳑ', + 11474 => 'ⳓ', + 11476 => 'ⳕ', + 11478 => 'ⳗ', + 11480 => 'ⳙ', + 11482 => 'ⳛ', + 11484 => 'ⳝ', + 11486 => 'ⳟ', + 11488 => 'ⳡ', + 11490 => 'ⳣ', + 11499 => 'ⳬ', + 11501 => 'ⳮ', + 11506 => 'ⳳ', + 11631 => 'ⵡ', + 11935 => '母', + 12019 => '龟', + 12032 => '一', + 12033 => '丨', + 12034 => '丶', + 12035 => '丿', + 12036 => '乙', + 12037 => '亅', + 12038 => '二', + 12039 => '亠', + 12040 => '人', + 12041 => '儿', + 12042 => '入', + 12043 => '八', + 12044 => '冂', + 12045 => '冖', + 12046 => '冫', + 12047 => '几', + 12048 => '凵', + 12049 => '刀', + 12050 => '力', + 12051 => '勹', + 12052 => '匕', + 12053 => '匚', + 12054 => '匸', + 12055 => '十', + 12056 => '卜', + 12057 => '卩', + 12058 => '厂', + 12059 => '厶', + 12060 => '又', + 12061 => '口', + 12062 => '囗', + 12063 => '土', + 12064 => '士', + 12065 => '夂', + 12066 => '夊', + 12067 => '夕', + 12068 => '大', + 12069 => '女', + 12070 => '子', + 12071 => '宀', + 12072 => '寸', + 12073 => '小', + 12074 => '尢', + 12075 => '尸', + 12076 => '屮', + 12077 => '山', + 12078 => '巛', + 12079 => '工', + 12080 => '己', + 12081 => '巾', + 12082 => '干', + 12083 => '幺', + 12084 => '广', + 12085 => '廴', + 12086 => '廾', + 12087 => '弋', + 12088 => '弓', + 12089 => '彐', + 12090 => '彡', + 12091 => '彳', + 12092 => '心', + 12093 => '戈', + 12094 => '戶', + 12095 => '手', + 12096 => '支', + 12097 => '攴', + 12098 => '文', + 12099 => '斗', + 12100 => '斤', + 12101 => '方', + 12102 => '无', + 12103 => '日', + 12104 => '曰', + 12105 => '月', + 12106 => '木', + 12107 => '欠', + 12108 => '止', + 12109 => '歹', + 12110 => '殳', + 12111 => '毋', + 12112 => '比', + 12113 => '毛', + 12114 => '氏', + 12115 => '气', + 12116 => '水', + 12117 => '火', + 12118 => '爪', + 12119 => '父', + 12120 => '爻', + 12121 => '爿', + 12122 => '片', + 12123 => '牙', + 12124 => '牛', + 12125 => '犬', + 12126 => '玄', + 12127 => '玉', + 12128 => '瓜', + 12129 => '瓦', + 12130 => '甘', + 12131 => '生', + 12132 => '用', + 12133 => '田', + 12134 => '疋', + 12135 => '疒', + 12136 => '癶', + 12137 => '白', + 12138 => '皮', + 12139 => '皿', + 12140 => '目', + 12141 => '矛', + 12142 => '矢', + 12143 => '石', + 12144 => '示', + 12145 => '禸', + 12146 => '禾', + 12147 => '穴', + 12148 => '立', + 12149 => '竹', + 12150 => '米', + 12151 => '糸', + 12152 => '缶', + 12153 => '网', + 12154 => '羊', + 12155 => '羽', + 12156 => '老', + 12157 => '而', + 12158 => '耒', + 12159 => '耳', + 12160 => '聿', + 12161 => '肉', + 12162 => '臣', + 12163 => '自', + 12164 => '至', + 12165 => '臼', + 12166 => '舌', + 12167 => '舛', + 12168 => '舟', + 12169 => '艮', + 12170 => '色', + 12171 => '艸', + 12172 => '虍', + 12173 => '虫', + 12174 => '血', + 12175 => '行', + 12176 => '衣', + 12177 => '襾', + 12178 => '見', + 12179 => '角', + 12180 => '言', + 12181 => '谷', + 12182 => '豆', + 12183 => '豕', + 12184 => '豸', + 12185 => '貝', + 12186 => '赤', + 12187 => '走', + 12188 => '足', + 12189 => '身', + 12190 => '車', + 12191 => '辛', + 12192 => '辰', + 12193 => '辵', + 12194 => '邑', + 12195 => '酉', + 12196 => '釆', + 12197 => '里', + 12198 => '金', + 12199 => '長', + 12200 => '門', + 12201 => '阜', + 12202 => '隶', + 12203 => '隹', + 12204 => '雨', + 12205 => '靑', + 12206 => '非', + 12207 => '面', + 12208 => '革', + 12209 => '韋', + 12210 => '韭', + 12211 => '音', + 12212 => '頁', + 12213 => '風', + 12214 => '飛', + 12215 => '食', + 12216 => '首', + 12217 => '香', + 12218 => '馬', + 12219 => '骨', + 12220 => '高', + 12221 => '髟', + 12222 => '鬥', + 12223 => '鬯', + 12224 => '鬲', + 12225 => '鬼', + 12226 => '魚', + 12227 => '鳥', + 12228 => '鹵', + 12229 => '鹿', + 12230 => '麥', + 12231 => '麻', + 12232 => '黃', + 12233 => '黍', + 12234 => '黑', + 12235 => '黹', + 12236 => '黽', + 12237 => '鼎', + 12238 => '鼓', + 12239 => '鼠', + 12240 => '鼻', + 12241 => '齊', + 12242 => '齒', + 12243 => '龍', + 12244 => '龜', + 12245 => '龠', + 12290 => '.', + 12342 => '〒', + 12344 => '十', + 12345 => '卄', + 12346 => '卅', + 12447 => 'より', + 12543 => 'コト', + 12593 => 'ᄀ', + 12594 => 'ᄁ', + 12595 => 'ᆪ', + 12596 => 'ᄂ', + 12597 => 'ᆬ', + 12598 => 'ᆭ', + 12599 => 'ᄃ', + 12600 => 'ᄄ', + 12601 => 'ᄅ', + 12602 => 'ᆰ', + 12603 => 'ᆱ', + 12604 => 'ᆲ', + 12605 => 'ᆳ', + 12606 => 'ᆴ', + 12607 => 'ᆵ', + 12608 => 'ᄚ', + 12609 => 'ᄆ', + 12610 => 'ᄇ', + 12611 => 'ᄈ', + 12612 => 'ᄡ', + 12613 => 'ᄉ', + 12614 => 'ᄊ', + 12615 => 'ᄋ', + 12616 => 'ᄌ', + 12617 => 'ᄍ', + 12618 => 'ᄎ', + 12619 => 'ᄏ', + 12620 => 'ᄐ', + 12621 => 'ᄑ', + 12622 => 'ᄒ', + 12623 => 'ᅡ', + 12624 => 'ᅢ', + 12625 => 'ᅣ', + 12626 => 'ᅤ', + 12627 => 'ᅥ', + 12628 => 'ᅦ', + 12629 => 'ᅧ', + 12630 => 'ᅨ', + 12631 => 'ᅩ', + 12632 => 'ᅪ', + 12633 => 'ᅫ', + 12634 => 'ᅬ', + 12635 => 'ᅭ', + 12636 => 'ᅮ', + 12637 => 'ᅯ', + 12638 => 'ᅰ', + 12639 => 'ᅱ', + 12640 => 'ᅲ', + 12641 => 'ᅳ', + 12642 => 'ᅴ', + 12643 => 'ᅵ', + 12645 => 'ᄔ', + 12646 => 'ᄕ', + 12647 => 'ᇇ', + 12648 => 'ᇈ', + 12649 => 'ᇌ', + 12650 => 'ᇎ', + 12651 => 'ᇓ', + 12652 => 'ᇗ', + 12653 => 'ᇙ', + 12654 => 'ᄜ', + 12655 => 'ᇝ', + 12656 => 'ᇟ', + 12657 => 'ᄝ', + 12658 => 'ᄞ', + 12659 => 'ᄠ', + 12660 => 'ᄢ', + 12661 => 'ᄣ', + 12662 => 'ᄧ', + 12663 => 'ᄩ', + 12664 => 'ᄫ', + 12665 => 'ᄬ', + 12666 => 'ᄭ', + 12667 => 'ᄮ', + 12668 => 'ᄯ', + 12669 => 'ᄲ', + 12670 => 'ᄶ', + 12671 => 'ᅀ', + 12672 => 'ᅇ', + 12673 => 'ᅌ', + 12674 => 'ᇱ', + 12675 => 'ᇲ', + 12676 => 'ᅗ', + 12677 => 'ᅘ', + 12678 => 'ᅙ', + 12679 => 'ᆄ', + 12680 => 'ᆅ', + 12681 => 'ᆈ', + 12682 => 'ᆑ', + 12683 => 'ᆒ', + 12684 => 'ᆔ', + 12685 => 'ᆞ', + 12686 => 'ᆡ', + 12690 => '一', + 12691 => '二', + 12692 => '三', + 12693 => '四', + 12694 => '上', + 12695 => '中', + 12696 => '下', + 12697 => '甲', + 12698 => '乙', + 12699 => '丙', + 12700 => '丁', + 12701 => '天', + 12702 => '地', + 12703 => '人', + 12868 => '問', + 12869 => '幼', + 12870 => '文', + 12871 => '箏', + 12880 => 'pte', + 12881 => '21', + 12882 => '22', + 12883 => '23', + 12884 => '24', + 12885 => '25', + 12886 => '26', + 12887 => '27', + 12888 => '28', + 12889 => '29', + 12890 => '30', + 12891 => '31', + 12892 => '32', + 12893 => '33', + 12894 => '34', + 12895 => '35', + 12896 => 'ᄀ', + 12897 => 'ᄂ', + 12898 => 'ᄃ', + 12899 => 'ᄅ', + 12900 => 'ᄆ', + 12901 => 'ᄇ', + 12902 => 'ᄉ', + 12903 => 'ᄋ', + 12904 => 'ᄌ', + 12905 => 'ᄎ', + 12906 => 'ᄏ', + 12907 => 'ᄐ', + 12908 => 'ᄑ', + 12909 => 'ᄒ', + 12910 => '가', + 12911 => '나', + 12912 => '다', + 12913 => '라', + 12914 => '마', + 12915 => '바', + 12916 => '사', + 12917 => '아', + 12918 => '자', + 12919 => '차', + 12920 => '카', + 12921 => '타', + 12922 => '파', + 12923 => '하', + 12924 => '참고', + 12925 => '주의', + 12926 => '우', + 12928 => '一', + 12929 => '二', + 12930 => '三', + 12931 => '四', + 12932 => '五', + 12933 => '六', + 12934 => '七', + 12935 => '八', + 12936 => '九', + 12937 => '十', + 12938 => '月', + 12939 => '火', + 12940 => '水', + 12941 => '木', + 12942 => '金', + 12943 => '土', + 12944 => '日', + 12945 => '株', + 12946 => '有', + 12947 => '社', + 12948 => '名', + 12949 => '特', + 12950 => '財', + 12951 => '祝', + 12952 => '労', + 12953 => '秘', + 12954 => '男', + 12955 => '女', + 12956 => '適', + 12957 => '優', + 12958 => '印', + 12959 => '注', + 12960 => '項', + 12961 => '休', + 12962 => '写', + 12963 => '正', + 12964 => '上', + 12965 => '中', + 12966 => '下', + 12967 => '左', + 12968 => '右', + 12969 => '医', + 12970 => '宗', + 12971 => '学', + 12972 => '監', + 12973 => '企', + 12974 => '資', + 12975 => '協', + 12976 => '夜', + 12977 => '36', + 12978 => '37', + 12979 => '38', + 12980 => '39', + 12981 => '40', + 12982 => '41', + 12983 => '42', + 12984 => '43', + 12985 => '44', + 12986 => '45', + 12987 => '46', + 12988 => '47', + 12989 => '48', + 12990 => '49', + 12991 => '50', + 12992 => '1月', + 12993 => '2月', + 12994 => '3月', + 12995 => '4月', + 12996 => '5月', + 12997 => '6月', + 12998 => '7月', + 12999 => '8月', + 13000 => '9月', + 13001 => '10月', + 13002 => '11月', + 13003 => '12月', + 13004 => 'hg', + 13005 => 'erg', + 13006 => 'ev', + 13007 => 'ltd', + 13008 => 'ア', + 13009 => 'イ', + 13010 => 'ウ', + 13011 => 'エ', + 13012 => 'オ', + 13013 => 'カ', + 13014 => 'キ', + 13015 => 'ク', + 13016 => 'ケ', + 13017 => 'コ', + 13018 => 'サ', + 13019 => 'シ', + 13020 => 'ス', + 13021 => 'セ', + 13022 => 'ソ', + 13023 => 'タ', + 13024 => 'チ', + 13025 => 'ツ', + 13026 => 'テ', + 13027 => 'ト', + 13028 => 'ナ', + 13029 => 'ニ', + 13030 => 'ヌ', + 13031 => 'ネ', + 13032 => 'ノ', + 13033 => 'ハ', + 13034 => 'ヒ', + 13035 => 'フ', + 13036 => 'ヘ', + 13037 => 'ホ', + 13038 => 'マ', + 13039 => 'ミ', + 13040 => 'ム', + 13041 => 'メ', + 13042 => 'モ', + 13043 => 'ヤ', + 13044 => 'ユ', + 13045 => 'ヨ', + 13046 => 'ラ', + 13047 => 'リ', + 13048 => 'ル', + 13049 => 'レ', + 13050 => 'ロ', + 13051 => 'ワ', + 13052 => 'ヰ', + 13053 => 'ヱ', + 13054 => 'ヲ', + 13055 => '令和', + 13056 => 'アパート', + 13057 => 'アルファ', + 13058 => 'アンペア', + 13059 => 'アール', + 13060 => 'イニング', + 13061 => 'インチ', + 13062 => 'ウォン', + 13063 => 'エスクード', + 13064 => 'エーカー', + 13065 => 'オンス', + 13066 => 'オーム', + 13067 => 'カイリ', + 13068 => 'カラット', + 13069 => 'カロリー', + 13070 => 'ガロン', + 13071 => 'ガンマ', + 13072 => 'ギガ', + 13073 => 'ギニー', + 13074 => 'キュリー', + 13075 => 'ギルダー', + 13076 => 'キロ', + 13077 => 'キログラム', + 13078 => 'キロメートル', + 13079 => 'キロワット', + 13080 => 'グラム', + 13081 => 'グラムトン', + 13082 => 'クルゼイロ', + 13083 => 'クローネ', + 13084 => 'ケース', + 13085 => 'コルナ', + 13086 => 'コーポ', + 13087 => 'サイクル', + 13088 => 'サンチーム', + 13089 => 'シリング', + 13090 => 'センチ', + 13091 => 'セント', + 13092 => 'ダース', + 13093 => 'デシ', + 13094 => 'ドル', + 13095 => 'トン', + 13096 => 'ナノ', + 13097 => 'ノット', + 13098 => 'ハイツ', + 13099 => 'パーセント', + 13100 => 'パーツ', + 13101 => 'バーレル', + 13102 => 'ピアストル', + 13103 => 'ピクル', + 13104 => 'ピコ', + 13105 => 'ビル', + 13106 => 'ファラッド', + 13107 => 'フィート', + 13108 => 'ブッシェル', + 13109 => 'フラン', + 13110 => 'ヘクタール', + 13111 => 'ペソ', + 13112 => 'ペニヒ', + 13113 => 'ヘルツ', + 13114 => 'ペンス', + 13115 => 'ページ', + 13116 => 'ベータ', + 13117 => 'ポイント', + 13118 => 'ボルト', + 13119 => 'ホン', + 13120 => 'ポンド', + 13121 => 'ホール', + 13122 => 'ホーン', + 13123 => 'マイクロ', + 13124 => 'マイル', + 13125 => 'マッハ', + 13126 => 'マルク', + 13127 => 'マンション', + 13128 => 'ミクロン', + 13129 => 'ミリ', + 13130 => 'ミリバール', + 13131 => 'メガ', + 13132 => 'メガトン', + 13133 => 'メートル', + 13134 => 'ヤード', + 13135 => 'ヤール', + 13136 => 'ユアン', + 13137 => 'リットル', + 13138 => 'リラ', + 13139 => 'ルピー', + 13140 => 'ルーブル', + 13141 => 'レム', + 13142 => 'レントゲン', + 13143 => 'ワット', + 13144 => '0点', + 13145 => '1点', + 13146 => '2点', + 13147 => '3点', + 13148 => '4点', + 13149 => '5点', + 13150 => '6点', + 13151 => '7点', + 13152 => '8点', + 13153 => '9点', + 13154 => '10点', + 13155 => '11点', + 13156 => '12点', + 13157 => '13点', + 13158 => '14点', + 13159 => '15点', + 13160 => '16点', + 13161 => '17点', + 13162 => '18点', + 13163 => '19点', + 13164 => '20点', + 13165 => '21点', + 13166 => '22点', + 13167 => '23点', + 13168 => '24点', + 13169 => 'hpa', + 13170 => 'da', + 13171 => 'au', + 13172 => 'bar', + 13173 => 'ov', + 13174 => 'pc', + 13175 => 'dm', + 13176 => 'dm2', + 13177 => 'dm3', + 13178 => 'iu', + 13179 => '平成', + 13180 => '昭和', + 13181 => '大正', + 13182 => '明治', + 13183 => '株式会社', + 13184 => 'pa', + 13185 => 'na', + 13186 => 'μa', + 13187 => 'ma', + 13188 => 'ka', + 13189 => 'kb', + 13190 => 'mb', + 13191 => 'gb', + 13192 => 'cal', + 13193 => 'kcal', + 13194 => 'pf', + 13195 => 'nf', + 13196 => 'μf', + 13197 => 'μg', + 13198 => 'mg', + 13199 => 'kg', + 13200 => 'hz', + 13201 => 'khz', + 13202 => 'mhz', + 13203 => 'ghz', + 13204 => 'thz', + 13205 => 'μl', + 13206 => 'ml', + 13207 => 'dl', + 13208 => 'kl', + 13209 => 'fm', + 13210 => 'nm', + 13211 => 'μm', + 13212 => 'mm', + 13213 => 'cm', + 13214 => 'km', + 13215 => 'mm2', + 13216 => 'cm2', + 13217 => 'm2', + 13218 => 'km2', + 13219 => 'mm3', + 13220 => 'cm3', + 13221 => 'm3', + 13222 => 'km3', + 13223 => 'm∕s', + 13224 => 'm∕s2', + 13225 => 'pa', + 13226 => 'kpa', + 13227 => 'mpa', + 13228 => 'gpa', + 13229 => 'rad', + 13230 => 'rad∕s', + 13231 => 'rad∕s2', + 13232 => 'ps', + 13233 => 'ns', + 13234 => 'μs', + 13235 => 'ms', + 13236 => 'pv', + 13237 => 'nv', + 13238 => 'μv', + 13239 => 'mv', + 13240 => 'kv', + 13241 => 'mv', + 13242 => 'pw', + 13243 => 'nw', + 13244 => 'μw', + 13245 => 'mw', + 13246 => 'kw', + 13247 => 'mw', + 13248 => 'kω', + 13249 => 'mω', + 13251 => 'bq', + 13252 => 'cc', + 13253 => 'cd', + 13254 => 'c∕kg', + 13256 => 'db', + 13257 => 'gy', + 13258 => 'ha', + 13259 => 'hp', + 13260 => 'in', + 13261 => 'kk', + 13262 => 'km', + 13263 => 'kt', + 13264 => 'lm', + 13265 => 'ln', + 13266 => 'log', + 13267 => 'lx', + 13268 => 'mb', + 13269 => 'mil', + 13270 => 'mol', + 13271 => 'ph', + 13273 => 'ppm', + 13274 => 'pr', + 13275 => 'sr', + 13276 => 'sv', + 13277 => 'wb', + 13278 => 'v∕m', + 13279 => 'a∕m', + 13280 => '1日', + 13281 => '2日', + 13282 => '3日', + 13283 => '4日', + 13284 => '5日', + 13285 => '6日', + 13286 => '7日', + 13287 => '8日', + 13288 => '9日', + 13289 => '10日', + 13290 => '11日', + 13291 => '12日', + 13292 => '13日', + 13293 => '14日', + 13294 => '15日', + 13295 => '16日', + 13296 => '17日', + 13297 => '18日', + 13298 => '19日', + 13299 => '20日', + 13300 => '21日', + 13301 => '22日', + 13302 => '23日', + 13303 => '24日', + 13304 => '25日', + 13305 => '26日', + 13306 => '27日', + 13307 => '28日', + 13308 => '29日', + 13309 => '30日', + 13310 => '31日', + 13311 => 'gal', + 42560 => 'ꙁ', + 42562 => 'ꙃ', + 42564 => 'ꙅ', + 42566 => 'ꙇ', + 42568 => 'ꙉ', + 42570 => 'ꙋ', + 42572 => 'ꙍ', + 42574 => 'ꙏ', + 42576 => 'ꙑ', + 42578 => 'ꙓ', + 42580 => 'ꙕ', + 42582 => 'ꙗ', + 42584 => 'ꙙ', + 42586 => 'ꙛ', + 42588 => 'ꙝ', + 42590 => 'ꙟ', + 42592 => 'ꙡ', + 42594 => 'ꙣ', + 42596 => 'ꙥ', + 42598 => 'ꙧ', + 42600 => 'ꙩ', + 42602 => 'ꙫ', + 42604 => 'ꙭ', + 42624 => 'ꚁ', + 42626 => 'ꚃ', + 42628 => 'ꚅ', + 42630 => 'ꚇ', + 42632 => 'ꚉ', + 42634 => 'ꚋ', + 42636 => 'ꚍ', + 42638 => 'ꚏ', + 42640 => 'ꚑ', + 42642 => 'ꚓ', + 42644 => 'ꚕ', + 42646 => 'ꚗ', + 42648 => 'ꚙ', + 42650 => 'ꚛ', + 42652 => 'ъ', + 42653 => 'ь', + 42786 => 'ꜣ', + 42788 => 'ꜥ', + 42790 => 'ꜧ', + 42792 => 'ꜩ', + 42794 => 'ꜫ', + 42796 => 'ꜭ', + 42798 => 'ꜯ', + 42802 => 'ꜳ', + 42804 => 'ꜵ', + 42806 => 'ꜷ', + 42808 => 'ꜹ', + 42810 => 'ꜻ', + 42812 => 'ꜽ', + 42814 => 'ꜿ', + 42816 => 'ꝁ', + 42818 => 'ꝃ', + 42820 => 'ꝅ', + 42822 => 'ꝇ', + 42824 => 'ꝉ', + 42826 => 'ꝋ', + 42828 => 'ꝍ', + 42830 => 'ꝏ', + 42832 => 'ꝑ', + 42834 => 'ꝓ', + 42836 => 'ꝕ', + 42838 => 'ꝗ', + 42840 => 'ꝙ', + 42842 => 'ꝛ', + 42844 => 'ꝝ', + 42846 => 'ꝟ', + 42848 => 'ꝡ', + 42850 => 'ꝣ', + 42852 => 'ꝥ', + 42854 => 'ꝧ', + 42856 => 'ꝩ', + 42858 => 'ꝫ', + 42860 => 'ꝭ', + 42862 => 'ꝯ', + 42864 => 'ꝯ', + 42873 => 'ꝺ', + 42875 => 'ꝼ', + 42877 => 'ᵹ', + 42878 => 'ꝿ', + 42880 => 'ꞁ', + 42882 => 'ꞃ', + 42884 => 'ꞅ', + 42886 => 'ꞇ', + 42891 => 'ꞌ', + 42893 => 'ɥ', + 42896 => 'ꞑ', + 42898 => 'ꞓ', + 42902 => 'ꞗ', + 42904 => 'ꞙ', + 42906 => 'ꞛ', + 42908 => 'ꞝ', + 42910 => 'ꞟ', + 42912 => 'ꞡ', + 42914 => 'ꞣ', + 42916 => 'ꞥ', + 42918 => 'ꞧ', + 42920 => 'ꞩ', + 42922 => 'ɦ', + 42923 => 'ɜ', + 42924 => 'ɡ', + 42925 => 'ɬ', + 42926 => 'ɪ', + 42928 => 'ʞ', + 42929 => 'ʇ', + 42930 => 'ʝ', + 42931 => 'ꭓ', + 42932 => 'ꞵ', + 42934 => 'ꞷ', + 42936 => 'ꞹ', + 42938 => 'ꞻ', + 42940 => 'ꞽ', + 42942 => 'ꞿ', + 42944 => 'ꟁ', + 42946 => 'ꟃ', + 42948 => 'ꞔ', + 42949 => 'ʂ', + 42950 => 'ᶎ', + 42951 => 'ꟈ', + 42953 => 'ꟊ', + 42960 => 'ꟑ', + 42966 => 'ꟗ', + 42968 => 'ꟙ', + 42994 => 'c', + 42995 => 'f', + 42996 => 'q', + 42997 => 'ꟶ', + 43000 => 'ħ', + 43001 => 'œ', + 43868 => 'ꜧ', + 43869 => 'ꬷ', + 43870 => 'ɫ', + 43871 => 'ꭒ', + 43881 => 'ʍ', + 43888 => 'Ꭰ', + 43889 => 'Ꭱ', + 43890 => 'Ꭲ', + 43891 => 'Ꭳ', + 43892 => 'Ꭴ', + 43893 => 'Ꭵ', + 43894 => 'Ꭶ', + 43895 => 'Ꭷ', + 43896 => 'Ꭸ', + 43897 => 'Ꭹ', + 43898 => 'Ꭺ', + 43899 => 'Ꭻ', + 43900 => 'Ꭼ', + 43901 => 'Ꭽ', + 43902 => 'Ꭾ', + 43903 => 'Ꭿ', + 43904 => 'Ꮀ', + 43905 => 'Ꮁ', + 43906 => 'Ꮂ', + 43907 => 'Ꮃ', + 43908 => 'Ꮄ', + 43909 => 'Ꮅ', + 43910 => 'Ꮆ', + 43911 => 'Ꮇ', + 43912 => 'Ꮈ', + 43913 => 'Ꮉ', + 43914 => 'Ꮊ', + 43915 => 'Ꮋ', + 43916 => 'Ꮌ', + 43917 => 'Ꮍ', + 43918 => 'Ꮎ', + 43919 => 'Ꮏ', + 43920 => 'Ꮐ', + 43921 => 'Ꮑ', + 43922 => 'Ꮒ', + 43923 => 'Ꮓ', + 43924 => 'Ꮔ', + 43925 => 'Ꮕ', + 43926 => 'Ꮖ', + 43927 => 'Ꮗ', + 43928 => 'Ꮘ', + 43929 => 'Ꮙ', + 43930 => 'Ꮚ', + 43931 => 'Ꮛ', + 43932 => 'Ꮜ', + 43933 => 'Ꮝ', + 43934 => 'Ꮞ', + 43935 => 'Ꮟ', + 43936 => 'Ꮠ', + 43937 => 'Ꮡ', + 43938 => 'Ꮢ', + 43939 => 'Ꮣ', + 43940 => 'Ꮤ', + 43941 => 'Ꮥ', + 43942 => 'Ꮦ', + 43943 => 'Ꮧ', + 43944 => 'Ꮨ', + 43945 => 'Ꮩ', + 43946 => 'Ꮪ', + 43947 => 'Ꮫ', + 43948 => 'Ꮬ', + 43949 => 'Ꮭ', + 43950 => 'Ꮮ', + 43951 => 'Ꮯ', + 43952 => 'Ꮰ', + 43953 => 'Ꮱ', + 43954 => 'Ꮲ', + 43955 => 'Ꮳ', + 43956 => 'Ꮴ', + 43957 => 'Ꮵ', + 43958 => 'Ꮶ', + 43959 => 'Ꮷ', + 43960 => 'Ꮸ', + 43961 => 'Ꮹ', + 43962 => 'Ꮺ', + 43963 => 'Ꮻ', + 43964 => 'Ꮼ', + 43965 => 'Ꮽ', + 43966 => 'Ꮾ', + 43967 => 'Ꮿ', + 63744 => '豈', + 63745 => '更', + 63746 => '車', + 63747 => '賈', + 63748 => '滑', + 63749 => '串', + 63750 => '句', + 63751 => '龜', + 63752 => '龜', + 63753 => '契', + 63754 => '金', + 63755 => '喇', + 63756 => '奈', + 63757 => '懶', + 63758 => '癩', + 63759 => '羅', + 63760 => '蘿', + 63761 => '螺', + 63762 => '裸', + 63763 => '邏', + 63764 => '樂', + 63765 => '洛', + 63766 => '烙', + 63767 => '珞', + 63768 => '落', + 63769 => '酪', + 63770 => '駱', + 63771 => '亂', + 63772 => '卵', + 63773 => '欄', + 63774 => '爛', + 63775 => '蘭', + 63776 => '鸞', + 63777 => '嵐', + 63778 => '濫', + 63779 => '藍', + 63780 => '襤', + 63781 => '拉', + 63782 => '臘', + 63783 => '蠟', + 63784 => '廊', + 63785 => '朗', + 63786 => '浪', + 63787 => '狼', + 63788 => '郎', + 63789 => '來', + 63790 => '冷', + 63791 => '勞', + 63792 => '擄', + 63793 => '櫓', + 63794 => '爐', + 63795 => '盧', + 63796 => '老', + 63797 => '蘆', + 63798 => '虜', + 63799 => '路', + 63800 => '露', + 63801 => '魯', + 63802 => '鷺', + 63803 => '碌', + 63804 => '祿', + 63805 => '綠', + 63806 => '菉', + 63807 => '錄', + 63808 => '鹿', + 63809 => '論', + 63810 => '壟', + 63811 => '弄', + 63812 => '籠', + 63813 => '聾', + 63814 => '牢', + 63815 => '磊', + 63816 => '賂', + 63817 => '雷', + 63818 => '壘', + 63819 => '屢', + 63820 => '樓', + 63821 => '淚', + 63822 => '漏', + 63823 => '累', + 63824 => '縷', + 63825 => '陋', + 63826 => '勒', + 63827 => '肋', + 63828 => '凜', + 63829 => '凌', + 63830 => '稜', + 63831 => '綾', + 63832 => '菱', + 63833 => '陵', + 63834 => '讀', + 63835 => '拏', + 63836 => '樂', + 63837 => '諾', + 63838 => '丹', + 63839 => '寧', + 63840 => '怒', + 63841 => '率', + 63842 => '異', + 63843 => '北', + 63844 => '磻', + 63845 => '便', + 63846 => '復', + 63847 => '不', + 63848 => '泌', + 63849 => '數', + 63850 => '索', + 63851 => '參', + 63852 => '塞', + 63853 => '省', + 63854 => '葉', + 63855 => '說', + 63856 => '殺', + 63857 => '辰', + 63858 => '沈', + 63859 => '拾', + 63860 => '若', + 63861 => '掠', + 63862 => '略', + 63863 => '亮', + 63864 => '兩', + 63865 => '凉', + 63866 => '梁', + 63867 => '糧', + 63868 => '良', + 63869 => '諒', + 63870 => '量', + 63871 => '勵', + 63872 => '呂', + 63873 => '女', + 63874 => '廬', + 63875 => '旅', + 63876 => '濾', + 63877 => '礪', + 63878 => '閭', + 63879 => '驪', + 63880 => '麗', + 63881 => '黎', + 63882 => '力', + 63883 => '曆', + 63884 => '歷', + 63885 => '轢', + 63886 => '年', + 63887 => '憐', + 63888 => '戀', + 63889 => '撚', + 63890 => '漣', + 63891 => '煉', + 63892 => '璉', + 63893 => '秊', + 63894 => '練', + 63895 => '聯', + 63896 => '輦', + 63897 => '蓮', + 63898 => '連', + 63899 => '鍊', + 63900 => '列', + 63901 => '劣', + 63902 => '咽', + 63903 => '烈', + 63904 => '裂', + 63905 => '說', + 63906 => '廉', + 63907 => '念', + 63908 => '捻', + 63909 => '殮', + 63910 => '簾', + 63911 => '獵', + 63912 => '令', + 63913 => '囹', + 63914 => '寧', + 63915 => '嶺', + 63916 => '怜', + 63917 => '玲', + 63918 => '瑩', + 63919 => '羚', + 63920 => '聆', + 63921 => '鈴', + 63922 => '零', + 63923 => '靈', + 63924 => '領', + 63925 => '例', + 63926 => '禮', + 63927 => '醴', + 63928 => '隸', + 63929 => '惡', + 63930 => '了', + 63931 => '僚', + 63932 => '寮', + 63933 => '尿', + 63934 => '料', + 63935 => '樂', + 63936 => '燎', + 63937 => '療', + 63938 => '蓼', + 63939 => '遼', + 63940 => '龍', + 63941 => '暈', + 63942 => '阮', + 63943 => '劉', + 63944 => '杻', + 63945 => '柳', + 63946 => '流', + 63947 => '溜', + 63948 => '琉', + 63949 => '留', + 63950 => '硫', + 63951 => '紐', + 63952 => '類', + 63953 => '六', + 63954 => '戮', + 63955 => '陸', + 63956 => '倫', + 63957 => '崙', + 63958 => '淪', + 63959 => '輪', + 63960 => '律', + 63961 => '慄', + 63962 => '栗', + 63963 => '率', + 63964 => '隆', + 63965 => '利', + 63966 => '吏', + 63967 => '履', + 63968 => '易', + 63969 => '李', + 63970 => '梨', + 63971 => '泥', + 63972 => '理', + 63973 => '痢', + 63974 => '罹', + 63975 => '裏', + 63976 => '裡', + 63977 => '里', + 63978 => '離', + 63979 => '匿', + 63980 => '溺', + 63981 => '吝', + 63982 => '燐', + 63983 => '璘', + 63984 => '藺', + 63985 => '隣', + 63986 => '鱗', + 63987 => '麟', + 63988 => '林', + 63989 => '淋', + 63990 => '臨', + 63991 => '立', + 63992 => '笠', + 63993 => '粒', + 63994 => '狀', + 63995 => '炙', + 63996 => '識', + 63997 => '什', + 63998 => '茶', + 63999 => '刺', + 64000 => '切', + 64001 => '度', + 64002 => '拓', + 64003 => '糖', + 64004 => '宅', + 64005 => '洞', + 64006 => '暴', + 64007 => '輻', + 64008 => '行', + 64009 => '降', + 64010 => '見', + 64011 => '廓', + 64012 => '兀', + 64013 => '嗀', + 64016 => '塚', + 64018 => '晴', + 64021 => '凞', + 64022 => '猪', + 64023 => '益', + 64024 => '礼', + 64025 => '神', + 64026 => '祥', + 64027 => '福', + 64028 => '靖', + 64029 => '精', + 64030 => '羽', + 64032 => '蘒', + 64034 => '諸', + 64037 => '逸', + 64038 => '都', + 64042 => '飯', + 64043 => '飼', + 64044 => '館', + 64045 => '鶴', + 64046 => '郞', + 64047 => '隷', + 64048 => '侮', + 64049 => '僧', + 64050 => '免', + 64051 => '勉', + 64052 => '勤', + 64053 => '卑', + 64054 => '喝', + 64055 => '嘆', + 64056 => '器', + 64057 => '塀', + 64058 => '墨', + 64059 => '層', + 64060 => '屮', + 64061 => '悔', + 64062 => '慨', + 64063 => '憎', + 64064 => '懲', + 64065 => '敏', + 64066 => '既', + 64067 => '暑', + 64068 => '梅', + 64069 => '海', + 64070 => '渚', + 64071 => '漢', + 64072 => '煮', + 64073 => '爫', + 64074 => '琢', + 64075 => '碑', + 64076 => '社', + 64077 => '祉', + 64078 => '祈', + 64079 => '祐', + 64080 => '祖', + 64081 => '祝', + 64082 => '禍', + 64083 => '禎', + 64084 => '穀', + 64085 => '突', + 64086 => '節', + 64087 => '練', + 64088 => '縉', + 64089 => '繁', + 64090 => '署', + 64091 => '者', + 64092 => '臭', + 64093 => '艹', + 64094 => '艹', + 64095 => '著', + 64096 => '褐', + 64097 => '視', + 64098 => '謁', + 64099 => '謹', + 64100 => '賓', + 64101 => '贈', + 64102 => '辶', + 64103 => '逸', + 64104 => '難', + 64105 => '響', + 64106 => '頻', + 64107 => '恵', + 64108 => '𤋮', + 64109 => '舘', + 64112 => '並', + 64113 => '况', + 64114 => '全', + 64115 => '侀', + 64116 => '充', + 64117 => '冀', + 64118 => '勇', + 64119 => '勺', + 64120 => '喝', + 64121 => '啕', + 64122 => '喙', + 64123 => '嗢', + 64124 => '塚', + 64125 => '墳', + 64126 => '奄', + 64127 => '奔', + 64128 => '婢', + 64129 => '嬨', + 64130 => '廒', + 64131 => '廙', + 64132 => '彩', + 64133 => '徭', + 64134 => '惘', + 64135 => '慎', + 64136 => '愈', + 64137 => '憎', + 64138 => '慠', + 64139 => '懲', + 64140 => '戴', + 64141 => '揄', + 64142 => '搜', + 64143 => '摒', + 64144 => '敖', + 64145 => '晴', + 64146 => '朗', + 64147 => '望', + 64148 => '杖', + 64149 => '歹', + 64150 => '殺', + 64151 => '流', + 64152 => '滛', + 64153 => '滋', + 64154 => '漢', + 64155 => '瀞', + 64156 => '煮', + 64157 => '瞧', + 64158 => '爵', + 64159 => '犯', + 64160 => '猪', + 64161 => '瑱', + 64162 => '甆', + 64163 => '画', + 64164 => '瘝', + 64165 => '瘟', + 64166 => '益', + 64167 => '盛', + 64168 => '直', + 64169 => '睊', + 64170 => '着', + 64171 => '磌', + 64172 => '窱', + 64173 => '節', + 64174 => '类', + 64175 => '絛', + 64176 => '練', + 64177 => '缾', + 64178 => '者', + 64179 => '荒', + 64180 => '華', + 64181 => '蝹', + 64182 => '襁', + 64183 => '覆', + 64184 => '視', + 64185 => '調', + 64186 => '諸', + 64187 => '請', + 64188 => '謁', + 64189 => '諾', + 64190 => '諭', + 64191 => '謹', + 64192 => '變', + 64193 => '贈', + 64194 => '輸', + 64195 => '遲', + 64196 => '醙', + 64197 => '鉶', + 64198 => '陼', + 64199 => '難', + 64200 => '靖', + 64201 => '韛', + 64202 => '響', + 64203 => '頋', + 64204 => '頻', + 64205 => '鬒', + 64206 => '龜', + 64207 => '𢡊', + 64208 => '𢡄', + 64209 => '𣏕', + 64210 => '㮝', + 64211 => '䀘', + 64212 => '䀹', + 64213 => '𥉉', + 64214 => '𥳐', + 64215 => '𧻓', + 64216 => '齃', + 64217 => '龎', + 64256 => 'ff', + 64257 => 'fi', + 64258 => 'fl', + 64259 => 'ffi', + 64260 => 'ffl', + 64261 => 'st', + 64262 => 'st', + 64275 => 'մն', + 64276 => 'մե', + 64277 => 'մի', + 64278 => 'վն', + 64279 => 'մխ', + 64285 => 'יִ', + 64287 => 'ײַ', + 64288 => 'ע', + 64289 => 'א', + 64290 => 'ד', + 64291 => 'ה', + 64292 => 'כ', + 64293 => 'ל', + 64294 => 'ם', + 64295 => 'ר', + 64296 => 'ת', + 64298 => 'שׁ', + 64299 => 'שׂ', + 64300 => 'שּׁ', + 64301 => 'שּׂ', + 64302 => 'אַ', + 64303 => 'אָ', + 64304 => 'אּ', + 64305 => 'בּ', + 64306 => 'גּ', + 64307 => 'דּ', + 64308 => 'הּ', + 64309 => 'וּ', + 64310 => 'זּ', + 64312 => 'טּ', + 64313 => 'יּ', + 64314 => 'ךּ', + 64315 => 'כּ', + 64316 => 'לּ', + 64318 => 'מּ', + 64320 => 'נּ', + 64321 => 'סּ', + 64323 => 'ףּ', + 64324 => 'פּ', + 64326 => 'צּ', + 64327 => 'קּ', + 64328 => 'רּ', + 64329 => 'שּ', + 64330 => 'תּ', + 64331 => 'וֹ', + 64332 => 'בֿ', + 64333 => 'כֿ', + 64334 => 'פֿ', + 64335 => 'אל', + 64336 => 'ٱ', + 64337 => 'ٱ', + 64338 => 'ٻ', + 64339 => 'ٻ', + 64340 => 'ٻ', + 64341 => 'ٻ', + 64342 => 'پ', + 64343 => 'پ', + 64344 => 'پ', + 64345 => 'پ', + 64346 => 'ڀ', + 64347 => 'ڀ', + 64348 => 'ڀ', + 64349 => 'ڀ', + 64350 => 'ٺ', + 64351 => 'ٺ', + 64352 => 'ٺ', + 64353 => 'ٺ', + 64354 => 'ٿ', + 64355 => 'ٿ', + 64356 => 'ٿ', + 64357 => 'ٿ', + 64358 => 'ٹ', + 64359 => 'ٹ', + 64360 => 'ٹ', + 64361 => 'ٹ', + 64362 => 'ڤ', + 64363 => 'ڤ', + 64364 => 'ڤ', + 64365 => 'ڤ', + 64366 => 'ڦ', + 64367 => 'ڦ', + 64368 => 'ڦ', + 64369 => 'ڦ', + 64370 => 'ڄ', + 64371 => 'ڄ', + 64372 => 'ڄ', + 64373 => 'ڄ', + 64374 => 'ڃ', + 64375 => 'ڃ', + 64376 => 'ڃ', + 64377 => 'ڃ', + 64378 => 'چ', + 64379 => 'چ', + 64380 => 'چ', + 64381 => 'چ', + 64382 => 'ڇ', + 64383 => 'ڇ', + 64384 => 'ڇ', + 64385 => 'ڇ', + 64386 => 'ڍ', + 64387 => 'ڍ', + 64388 => 'ڌ', + 64389 => 'ڌ', + 64390 => 'ڎ', + 64391 => 'ڎ', + 64392 => 'ڈ', + 64393 => 'ڈ', + 64394 => 'ژ', + 64395 => 'ژ', + 64396 => 'ڑ', + 64397 => 'ڑ', + 64398 => 'ک', + 64399 => 'ک', + 64400 => 'ک', + 64401 => 'ک', + 64402 => 'گ', + 64403 => 'گ', + 64404 => 'گ', + 64405 => 'گ', + 64406 => 'ڳ', + 64407 => 'ڳ', + 64408 => 'ڳ', + 64409 => 'ڳ', + 64410 => 'ڱ', + 64411 => 'ڱ', + 64412 => 'ڱ', + 64413 => 'ڱ', + 64414 => 'ں', + 64415 => 'ں', + 64416 => 'ڻ', + 64417 => 'ڻ', + 64418 => 'ڻ', + 64419 => 'ڻ', + 64420 => 'ۀ', + 64421 => 'ۀ', + 64422 => 'ہ', + 64423 => 'ہ', + 64424 => 'ہ', + 64425 => 'ہ', + 64426 => 'ھ', + 64427 => 'ھ', + 64428 => 'ھ', + 64429 => 'ھ', + 64430 => 'ے', + 64431 => 'ے', + 64432 => 'ۓ', + 64433 => 'ۓ', + 64467 => 'ڭ', + 64468 => 'ڭ', + 64469 => 'ڭ', + 64470 => 'ڭ', + 64471 => 'ۇ', + 64472 => 'ۇ', + 64473 => 'ۆ', + 64474 => 'ۆ', + 64475 => 'ۈ', + 64476 => 'ۈ', + 64477 => 'ۇٴ', + 64478 => 'ۋ', + 64479 => 'ۋ', + 64480 => 'ۅ', + 64481 => 'ۅ', + 64482 => 'ۉ', + 64483 => 'ۉ', + 64484 => 'ې', + 64485 => 'ې', + 64486 => 'ې', + 64487 => 'ې', + 64488 => 'ى', + 64489 => 'ى', + 64490 => 'ئا', + 64491 => 'ئا', + 64492 => 'ئە', + 64493 => 'ئە', + 64494 => 'ئو', + 64495 => 'ئو', + 64496 => 'ئۇ', + 64497 => 'ئۇ', + 64498 => 'ئۆ', + 64499 => 'ئۆ', + 64500 => 'ئۈ', + 64501 => 'ئۈ', + 64502 => 'ئې', + 64503 => 'ئې', + 64504 => 'ئې', + 64505 => 'ئى', + 64506 => 'ئى', + 64507 => 'ئى', + 64508 => 'ی', + 64509 => 'ی', + 64510 => 'ی', + 64511 => 'ی', + 64512 => 'ئج', + 64513 => 'ئح', + 64514 => 'ئم', + 64515 => 'ئى', + 64516 => 'ئي', + 64517 => 'بج', + 64518 => 'بح', + 64519 => 'بخ', + 64520 => 'بم', + 64521 => 'بى', + 64522 => 'بي', + 64523 => 'تج', + 64524 => 'تح', + 64525 => 'تخ', + 64526 => 'تم', + 64527 => 'تى', + 64528 => 'تي', + 64529 => 'ثج', + 64530 => 'ثم', + 64531 => 'ثى', + 64532 => 'ثي', + 64533 => 'جح', + 64534 => 'جم', + 64535 => 'حج', + 64536 => 'حم', + 64537 => 'خج', + 64538 => 'خح', + 64539 => 'خم', + 64540 => 'سج', + 64541 => 'سح', + 64542 => 'سخ', + 64543 => 'سم', + 64544 => 'صح', + 64545 => 'صم', + 64546 => 'ضج', + 64547 => 'ضح', + 64548 => 'ضخ', + 64549 => 'ضم', + 64550 => 'طح', + 64551 => 'طم', + 64552 => 'ظم', + 64553 => 'عج', + 64554 => 'عم', + 64555 => 'غج', + 64556 => 'غم', + 64557 => 'فج', + 64558 => 'فح', + 64559 => 'فخ', + 64560 => 'فم', + 64561 => 'فى', + 64562 => 'في', + 64563 => 'قح', + 64564 => 'قم', + 64565 => 'قى', + 64566 => 'قي', + 64567 => 'كا', + 64568 => 'كج', + 64569 => 'كح', + 64570 => 'كخ', + 64571 => 'كل', + 64572 => 'كم', + 64573 => 'كى', + 64574 => 'كي', + 64575 => 'لج', + 64576 => 'لح', + 64577 => 'لخ', + 64578 => 'لم', + 64579 => 'لى', + 64580 => 'لي', + 64581 => 'مج', + 64582 => 'مح', + 64583 => 'مخ', + 64584 => 'مم', + 64585 => 'مى', + 64586 => 'مي', + 64587 => 'نج', + 64588 => 'نح', + 64589 => 'نخ', + 64590 => 'نم', + 64591 => 'نى', + 64592 => 'ني', + 64593 => 'هج', + 64594 => 'هم', + 64595 => 'هى', + 64596 => 'هي', + 64597 => 'يج', + 64598 => 'يح', + 64599 => 'يخ', + 64600 => 'يم', + 64601 => 'يى', + 64602 => 'يي', + 64603 => 'ذٰ', + 64604 => 'رٰ', + 64605 => 'ىٰ', + 64612 => 'ئر', + 64613 => 'ئز', + 64614 => 'ئم', + 64615 => 'ئن', + 64616 => 'ئى', + 64617 => 'ئي', + 64618 => 'بر', + 64619 => 'بز', + 64620 => 'بم', + 64621 => 'بن', + 64622 => 'بى', + 64623 => 'بي', + 64624 => 'تر', + 64625 => 'تز', + 64626 => 'تم', + 64627 => 'تن', + 64628 => 'تى', + 64629 => 'تي', + 64630 => 'ثر', + 64631 => 'ثز', + 64632 => 'ثم', + 64633 => 'ثن', + 64634 => 'ثى', + 64635 => 'ثي', + 64636 => 'فى', + 64637 => 'في', + 64638 => 'قى', + 64639 => 'قي', + 64640 => 'كا', + 64641 => 'كل', + 64642 => 'كم', + 64643 => 'كى', + 64644 => 'كي', + 64645 => 'لم', + 64646 => 'لى', + 64647 => 'لي', + 64648 => 'ما', + 64649 => 'مم', + 64650 => 'نر', + 64651 => 'نز', + 64652 => 'نم', + 64653 => 'نن', + 64654 => 'نى', + 64655 => 'ني', + 64656 => 'ىٰ', + 64657 => 'ير', + 64658 => 'يز', + 64659 => 'يم', + 64660 => 'ين', + 64661 => 'يى', + 64662 => 'يي', + 64663 => 'ئج', + 64664 => 'ئح', + 64665 => 'ئخ', + 64666 => 'ئم', + 64667 => 'ئه', + 64668 => 'بج', + 64669 => 'بح', + 64670 => 'بخ', + 64671 => 'بم', + 64672 => 'به', + 64673 => 'تج', + 64674 => 'تح', + 64675 => 'تخ', + 64676 => 'تم', + 64677 => 'ته', + 64678 => 'ثم', + 64679 => 'جح', + 64680 => 'جم', + 64681 => 'حج', + 64682 => 'حم', + 64683 => 'خج', + 64684 => 'خم', + 64685 => 'سج', + 64686 => 'سح', + 64687 => 'سخ', + 64688 => 'سم', + 64689 => 'صح', + 64690 => 'صخ', + 64691 => 'صم', + 64692 => 'ضج', + 64693 => 'ضح', + 64694 => 'ضخ', + 64695 => 'ضم', + 64696 => 'طح', + 64697 => 'ظم', + 64698 => 'عج', + 64699 => 'عم', + 64700 => 'غج', + 64701 => 'غم', + 64702 => 'فج', + 64703 => 'فح', + 64704 => 'فخ', + 64705 => 'فم', + 64706 => 'قح', + 64707 => 'قم', + 64708 => 'كج', + 64709 => 'كح', + 64710 => 'كخ', + 64711 => 'كل', + 64712 => 'كم', + 64713 => 'لج', + 64714 => 'لح', + 64715 => 'لخ', + 64716 => 'لم', + 64717 => 'له', + 64718 => 'مج', + 64719 => 'مح', + 64720 => 'مخ', + 64721 => 'مم', + 64722 => 'نج', + 64723 => 'نح', + 64724 => 'نخ', + 64725 => 'نم', + 64726 => 'نه', + 64727 => 'هج', + 64728 => 'هم', + 64729 => 'هٰ', + 64730 => 'يج', + 64731 => 'يح', + 64732 => 'يخ', + 64733 => 'يم', + 64734 => 'يه', + 64735 => 'ئم', + 64736 => 'ئه', + 64737 => 'بم', + 64738 => 'به', + 64739 => 'تم', + 64740 => 'ته', + 64741 => 'ثم', + 64742 => 'ثه', + 64743 => 'سم', + 64744 => 'سه', + 64745 => 'شم', + 64746 => 'شه', + 64747 => 'كل', + 64748 => 'كم', + 64749 => 'لم', + 64750 => 'نم', + 64751 => 'نه', + 64752 => 'يم', + 64753 => 'يه', + 64754 => 'ـَّ', + 64755 => 'ـُّ', + 64756 => 'ـِّ', + 64757 => 'طى', + 64758 => 'طي', + 64759 => 'عى', + 64760 => 'عي', + 64761 => 'غى', + 64762 => 'غي', + 64763 => 'سى', + 64764 => 'سي', + 64765 => 'شى', + 64766 => 'شي', + 64767 => 'حى', + 64768 => 'حي', + 64769 => 'جى', + 64770 => 'جي', + 64771 => 'خى', + 64772 => 'خي', + 64773 => 'صى', + 64774 => 'صي', + 64775 => 'ضى', + 64776 => 'ضي', + 64777 => 'شج', + 64778 => 'شح', + 64779 => 'شخ', + 64780 => 'شم', + 64781 => 'شر', + 64782 => 'سر', + 64783 => 'صر', + 64784 => 'ضر', + 64785 => 'طى', + 64786 => 'طي', + 64787 => 'عى', + 64788 => 'عي', + 64789 => 'غى', + 64790 => 'غي', + 64791 => 'سى', + 64792 => 'سي', + 64793 => 'شى', + 64794 => 'شي', + 64795 => 'حى', + 64796 => 'حي', + 64797 => 'جى', + 64798 => 'جي', + 64799 => 'خى', + 64800 => 'خي', + 64801 => 'صى', + 64802 => 'صي', + 64803 => 'ضى', + 64804 => 'ضي', + 64805 => 'شج', + 64806 => 'شح', + 64807 => 'شخ', + 64808 => 'شم', + 64809 => 'شر', + 64810 => 'سر', + 64811 => 'صر', + 64812 => 'ضر', + 64813 => 'شج', + 64814 => 'شح', + 64815 => 'شخ', + 64816 => 'شم', + 64817 => 'سه', + 64818 => 'شه', + 64819 => 'طم', + 64820 => 'سج', + 64821 => 'سح', + 64822 => 'سخ', + 64823 => 'شج', + 64824 => 'شح', + 64825 => 'شخ', + 64826 => 'طم', + 64827 => 'ظم', + 64828 => 'اً', + 64829 => 'اً', + 64848 => 'تجم', + 64849 => 'تحج', + 64850 => 'تحج', + 64851 => 'تحم', + 64852 => 'تخم', + 64853 => 'تمج', + 64854 => 'تمح', + 64855 => 'تمخ', + 64856 => 'جمح', + 64857 => 'جمح', + 64858 => 'حمي', + 64859 => 'حمى', + 64860 => 'سحج', + 64861 => 'سجح', + 64862 => 'سجى', + 64863 => 'سمح', + 64864 => 'سمح', + 64865 => 'سمج', + 64866 => 'سمم', + 64867 => 'سمم', + 64868 => 'صحح', + 64869 => 'صحح', + 64870 => 'صمم', + 64871 => 'شحم', + 64872 => 'شحم', + 64873 => 'شجي', + 64874 => 'شمخ', + 64875 => 'شمخ', + 64876 => 'شمم', + 64877 => 'شمم', + 64878 => 'ضحى', + 64879 => 'ضخم', + 64880 => 'ضخم', + 64881 => 'طمح', + 64882 => 'طمح', + 64883 => 'طمم', + 64884 => 'طمي', + 64885 => 'عجم', + 64886 => 'عمم', + 64887 => 'عمم', + 64888 => 'عمى', + 64889 => 'غمم', + 64890 => 'غمي', + 64891 => 'غمى', + 64892 => 'فخم', + 64893 => 'فخم', + 64894 => 'قمح', + 64895 => 'قمم', + 64896 => 'لحم', + 64897 => 'لحي', + 64898 => 'لحى', + 64899 => 'لجج', + 64900 => 'لجج', + 64901 => 'لخم', + 64902 => 'لخم', + 64903 => 'لمح', + 64904 => 'لمح', + 64905 => 'محج', + 64906 => 'محم', + 64907 => 'محي', + 64908 => 'مجح', + 64909 => 'مجم', + 64910 => 'مخج', + 64911 => 'مخم', + 64914 => 'مجخ', + 64915 => 'همج', + 64916 => 'همم', + 64917 => 'نحم', + 64918 => 'نحى', + 64919 => 'نجم', + 64920 => 'نجم', + 64921 => 'نجى', + 64922 => 'نمي', + 64923 => 'نمى', + 64924 => 'يمم', + 64925 => 'يمم', + 64926 => 'بخي', + 64927 => 'تجي', + 64928 => 'تجى', + 64929 => 'تخي', + 64930 => 'تخى', + 64931 => 'تمي', + 64932 => 'تمى', + 64933 => 'جمي', + 64934 => 'جحى', + 64935 => 'جمى', + 64936 => 'سخى', + 64937 => 'صحي', + 64938 => 'شحي', + 64939 => 'ضحي', + 64940 => 'لجي', + 64941 => 'لمي', + 64942 => 'يحي', + 64943 => 'يجي', + 64944 => 'يمي', + 64945 => 'ممي', + 64946 => 'قمي', + 64947 => 'نحي', + 64948 => 'قمح', + 64949 => 'لحم', + 64950 => 'عمي', + 64951 => 'كمي', + 64952 => 'نجح', + 64953 => 'مخي', + 64954 => 'لجم', + 64955 => 'كمم', + 64956 => 'لجم', + 64957 => 'نجح', + 64958 => 'جحي', + 64959 => 'حجي', + 64960 => 'مجي', + 64961 => 'فمي', + 64962 => 'بحي', + 64963 => 'كمم', + 64964 => 'عجم', + 64965 => 'صمم', + 64966 => 'سخي', + 64967 => 'نجي', + 65008 => 'صلے', + 65009 => 'قلے', + 65010 => 'الله', + 65011 => 'اكبر', + 65012 => 'محمد', + 65013 => 'صلعم', + 65014 => 'رسول', + 65015 => 'عليه', + 65016 => 'وسلم', + 65017 => 'صلى', + 65020 => 'ریال', + 65041 => '、', + 65047 => '〖', + 65048 => '〗', + 65073 => '—', + 65074 => '–', + 65081 => '〔', + 65082 => '〕', + 65083 => '【', + 65084 => '】', + 65085 => '《', + 65086 => '》', + 65087 => '〈', + 65088 => '〉', + 65089 => '「', + 65090 => '」', + 65091 => '『', + 65092 => '』', + 65105 => '、', + 65112 => '—', + 65117 => '〔', + 65118 => '〕', + 65123 => '-', + 65137 => 'ـً', + 65143 => 'ـَ', + 65145 => 'ـُ', + 65147 => 'ـِ', + 65149 => 'ـّ', + 65151 => 'ـْ', + 65152 => 'ء', + 65153 => 'آ', + 65154 => 'آ', + 65155 => 'أ', + 65156 => 'أ', + 65157 => 'ؤ', + 65158 => 'ؤ', + 65159 => 'إ', + 65160 => 'إ', + 65161 => 'ئ', + 65162 => 'ئ', + 65163 => 'ئ', + 65164 => 'ئ', + 65165 => 'ا', + 65166 => 'ا', + 65167 => 'ب', + 65168 => 'ب', + 65169 => 'ب', + 65170 => 'ب', + 65171 => 'ة', + 65172 => 'ة', + 65173 => 'ت', + 65174 => 'ت', + 65175 => 'ت', + 65176 => 'ت', + 65177 => 'ث', + 65178 => 'ث', + 65179 => 'ث', + 65180 => 'ث', + 65181 => 'ج', + 65182 => 'ج', + 65183 => 'ج', + 65184 => 'ج', + 65185 => 'ح', + 65186 => 'ح', + 65187 => 'ح', + 65188 => 'ح', + 65189 => 'خ', + 65190 => 'خ', + 65191 => 'خ', + 65192 => 'خ', + 65193 => 'د', + 65194 => 'د', + 65195 => 'ذ', + 65196 => 'ذ', + 65197 => 'ر', + 65198 => 'ر', + 65199 => 'ز', + 65200 => 'ز', + 65201 => 'س', + 65202 => 'س', + 65203 => 'س', + 65204 => 'س', + 65205 => 'ش', + 65206 => 'ش', + 65207 => 'ش', + 65208 => 'ش', + 65209 => 'ص', + 65210 => 'ص', + 65211 => 'ص', + 65212 => 'ص', + 65213 => 'ض', + 65214 => 'ض', + 65215 => 'ض', + 65216 => 'ض', + 65217 => 'ط', + 65218 => 'ط', + 65219 => 'ط', + 65220 => 'ط', + 65221 => 'ظ', + 65222 => 'ظ', + 65223 => 'ظ', + 65224 => 'ظ', + 65225 => 'ع', + 65226 => 'ع', + 65227 => 'ع', + 65228 => 'ع', + 65229 => 'غ', + 65230 => 'غ', + 65231 => 'غ', + 65232 => 'غ', + 65233 => 'ف', + 65234 => 'ف', + 65235 => 'ف', + 65236 => 'ف', + 65237 => 'ق', + 65238 => 'ق', + 65239 => 'ق', + 65240 => 'ق', + 65241 => 'ك', + 65242 => 'ك', + 65243 => 'ك', + 65244 => 'ك', + 65245 => 'ل', + 65246 => 'ل', + 65247 => 'ل', + 65248 => 'ل', + 65249 => 'م', + 65250 => 'م', + 65251 => 'م', + 65252 => 'م', + 65253 => 'ن', + 65254 => 'ن', + 65255 => 'ن', + 65256 => 'ن', + 65257 => 'ه', + 65258 => 'ه', + 65259 => 'ه', + 65260 => 'ه', + 65261 => 'و', + 65262 => 'و', + 65263 => 'ى', + 65264 => 'ى', + 65265 => 'ي', + 65266 => 'ي', + 65267 => 'ي', + 65268 => 'ي', + 65269 => 'لآ', + 65270 => 'لآ', + 65271 => 'لأ', + 65272 => 'لأ', + 65273 => 'لإ', + 65274 => 'لإ', + 65275 => 'لا', + 65276 => 'لا', + 65293 => '-', + 65294 => '.', + 65296 => '0', + 65297 => '1', + 65298 => '2', + 65299 => '3', + 65300 => '4', + 65301 => '5', + 65302 => '6', + 65303 => '7', + 65304 => '8', + 65305 => '9', + 65313 => 'a', + 65314 => 'b', + 65315 => 'c', + 65316 => 'd', + 65317 => 'e', + 65318 => 'f', + 65319 => 'g', + 65320 => 'h', + 65321 => 'i', + 65322 => 'j', + 65323 => 'k', + 65324 => 'l', + 65325 => 'm', + 65326 => 'n', + 65327 => 'o', + 65328 => 'p', + 65329 => 'q', + 65330 => 'r', + 65331 => 's', + 65332 => 't', + 65333 => 'u', + 65334 => 'v', + 65335 => 'w', + 65336 => 'x', + 65337 => 'y', + 65338 => 'z', + 65345 => 'a', + 65346 => 'b', + 65347 => 'c', + 65348 => 'd', + 65349 => 'e', + 65350 => 'f', + 65351 => 'g', + 65352 => 'h', + 65353 => 'i', + 65354 => 'j', + 65355 => 'k', + 65356 => 'l', + 65357 => 'm', + 65358 => 'n', + 65359 => 'o', + 65360 => 'p', + 65361 => 'q', + 65362 => 'r', + 65363 => 's', + 65364 => 't', + 65365 => 'u', + 65366 => 'v', + 65367 => 'w', + 65368 => 'x', + 65369 => 'y', + 65370 => 'z', + 65375 => '⦅', + 65376 => '⦆', + 65377 => '.', + 65378 => '「', + 65379 => '」', + 65380 => '、', + 65381 => '・', + 65382 => 'ヲ', + 65383 => 'ァ', + 65384 => 'ィ', + 65385 => 'ゥ', + 65386 => 'ェ', + 65387 => 'ォ', + 65388 => 'ャ', + 65389 => 'ュ', + 65390 => 'ョ', + 65391 => 'ッ', + 65392 => 'ー', + 65393 => 'ア', + 65394 => 'イ', + 65395 => 'ウ', + 65396 => 'エ', + 65397 => 'オ', + 65398 => 'カ', + 65399 => 'キ', + 65400 => 'ク', + 65401 => 'ケ', + 65402 => 'コ', + 65403 => 'サ', + 65404 => 'シ', + 65405 => 'ス', + 65406 => 'セ', + 65407 => 'ソ', + 65408 => 'タ', + 65409 => 'チ', + 65410 => 'ツ', + 65411 => 'テ', + 65412 => 'ト', + 65413 => 'ナ', + 65414 => 'ニ', + 65415 => 'ヌ', + 65416 => 'ネ', + 65417 => 'ノ', + 65418 => 'ハ', + 65419 => 'ヒ', + 65420 => 'フ', + 65421 => 'ヘ', + 65422 => 'ホ', + 65423 => 'マ', + 65424 => 'ミ', + 65425 => 'ム', + 65426 => 'メ', + 65427 => 'モ', + 65428 => 'ヤ', + 65429 => 'ユ', + 65430 => 'ヨ', + 65431 => 'ラ', + 65432 => 'リ', + 65433 => 'ル', + 65434 => 'レ', + 65435 => 'ロ', + 65436 => 'ワ', + 65437 => 'ン', + 65438 => '゙', + 65439 => '゚', + 65441 => 'ᄀ', + 65442 => 'ᄁ', + 65443 => 'ᆪ', + 65444 => 'ᄂ', + 65445 => 'ᆬ', + 65446 => 'ᆭ', + 65447 => 'ᄃ', + 65448 => 'ᄄ', + 65449 => 'ᄅ', + 65450 => 'ᆰ', + 65451 => 'ᆱ', + 65452 => 'ᆲ', + 65453 => 'ᆳ', + 65454 => 'ᆴ', + 65455 => 'ᆵ', + 65456 => 'ᄚ', + 65457 => 'ᄆ', + 65458 => 'ᄇ', + 65459 => 'ᄈ', + 65460 => 'ᄡ', + 65461 => 'ᄉ', + 65462 => 'ᄊ', + 65463 => 'ᄋ', + 65464 => 'ᄌ', + 65465 => 'ᄍ', + 65466 => 'ᄎ', + 65467 => 'ᄏ', + 65468 => 'ᄐ', + 65469 => 'ᄑ', + 65470 => 'ᄒ', + 65474 => 'ᅡ', + 65475 => 'ᅢ', + 65476 => 'ᅣ', + 65477 => 'ᅤ', + 65478 => 'ᅥ', + 65479 => 'ᅦ', + 65482 => 'ᅧ', + 65483 => 'ᅨ', + 65484 => 'ᅩ', + 65485 => 'ᅪ', + 65486 => 'ᅫ', + 65487 => 'ᅬ', + 65490 => 'ᅭ', + 65491 => 'ᅮ', + 65492 => 'ᅯ', + 65493 => 'ᅰ', + 65494 => 'ᅱ', + 65495 => 'ᅲ', + 65498 => 'ᅳ', + 65499 => 'ᅴ', + 65500 => 'ᅵ', + 65504 => '¢', + 65505 => '£', + 65506 => '¬', + 65508 => '¦', + 65509 => '¥', + 65510 => '₩', + 65512 => '│', + 65513 => '←', + 65514 => '↑', + 65515 => '→', + 65516 => '↓', + 65517 => '■', + 65518 => '○', + 66560 => '𐐨', + 66561 => '𐐩', + 66562 => '𐐪', + 66563 => '𐐫', + 66564 => '𐐬', + 66565 => '𐐭', + 66566 => '𐐮', + 66567 => '𐐯', + 66568 => '𐐰', + 66569 => '𐐱', + 66570 => '𐐲', + 66571 => '𐐳', + 66572 => '𐐴', + 66573 => '𐐵', + 66574 => '𐐶', + 66575 => '𐐷', + 66576 => '𐐸', + 66577 => '𐐹', + 66578 => '𐐺', + 66579 => '𐐻', + 66580 => '𐐼', + 66581 => '𐐽', + 66582 => '𐐾', + 66583 => '𐐿', + 66584 => '𐑀', + 66585 => '𐑁', + 66586 => '𐑂', + 66587 => '𐑃', + 66588 => '𐑄', + 66589 => '𐑅', + 66590 => '𐑆', + 66591 => '𐑇', + 66592 => '𐑈', + 66593 => '𐑉', + 66594 => '𐑊', + 66595 => '𐑋', + 66596 => '𐑌', + 66597 => '𐑍', + 66598 => '𐑎', + 66599 => '𐑏', + 66736 => '𐓘', + 66737 => '𐓙', + 66738 => '𐓚', + 66739 => '𐓛', + 66740 => '𐓜', + 66741 => '𐓝', + 66742 => '𐓞', + 66743 => '𐓟', + 66744 => '𐓠', + 66745 => '𐓡', + 66746 => '𐓢', + 66747 => '𐓣', + 66748 => '𐓤', + 66749 => '𐓥', + 66750 => '𐓦', + 66751 => '𐓧', + 66752 => '𐓨', + 66753 => '𐓩', + 66754 => '𐓪', + 66755 => '𐓫', + 66756 => '𐓬', + 66757 => '𐓭', + 66758 => '𐓮', + 66759 => '𐓯', + 66760 => '𐓰', + 66761 => '𐓱', + 66762 => '𐓲', + 66763 => '𐓳', + 66764 => '𐓴', + 66765 => '𐓵', + 66766 => '𐓶', + 66767 => '𐓷', + 66768 => '𐓸', + 66769 => '𐓹', + 66770 => '𐓺', + 66771 => '𐓻', + 66928 => '𐖗', + 66929 => '𐖘', + 66930 => '𐖙', + 66931 => '𐖚', + 66932 => '𐖛', + 66933 => '𐖜', + 66934 => '𐖝', + 66935 => '𐖞', + 66936 => '𐖟', + 66937 => '𐖠', + 66938 => '𐖡', + 66940 => '𐖣', + 66941 => '𐖤', + 66942 => '𐖥', + 66943 => '𐖦', + 66944 => '𐖧', + 66945 => '𐖨', + 66946 => '𐖩', + 66947 => '𐖪', + 66948 => '𐖫', + 66949 => '𐖬', + 66950 => '𐖭', + 66951 => '𐖮', + 66952 => '𐖯', + 66953 => '𐖰', + 66954 => '𐖱', + 66956 => '𐖳', + 66957 => '𐖴', + 66958 => '𐖵', + 66959 => '𐖶', + 66960 => '𐖷', + 66961 => '𐖸', + 66962 => '𐖹', + 66964 => '𐖻', + 66965 => '𐖼', + 67457 => 'ː', + 67458 => 'ˑ', + 67459 => 'æ', + 67460 => 'ʙ', + 67461 => 'ɓ', + 67463 => 'ʣ', + 67464 => 'ꭦ', + 67465 => 'ʥ', + 67466 => 'ʤ', + 67467 => 'ɖ', + 67468 => 'ɗ', + 67469 => 'ᶑ', + 67470 => 'ɘ', + 67471 => 'ɞ', + 67472 => 'ʩ', + 67473 => 'ɤ', + 67474 => 'ɢ', + 67475 => 'ɠ', + 67476 => 'ʛ', + 67477 => 'ħ', + 67478 => 'ʜ', + 67479 => 'ɧ', + 67480 => 'ʄ', + 67481 => 'ʪ', + 67482 => 'ʫ', + 67483 => 'ɬ', + 67484 => '𝼄', + 67485 => 'ꞎ', + 67486 => 'ɮ', + 67487 => '𝼅', + 67488 => 'ʎ', + 67489 => '𝼆', + 67490 => 'ø', + 67491 => 'ɶ', + 67492 => 'ɷ', + 67493 => 'q', + 67494 => 'ɺ', + 67495 => '𝼈', + 67496 => 'ɽ', + 67497 => 'ɾ', + 67498 => 'ʀ', + 67499 => 'ʨ', + 67500 => 'ʦ', + 67501 => 'ꭧ', + 67502 => 'ʧ', + 67503 => 'ʈ', + 67504 => 'ⱱ', + 67506 => 'ʏ', + 67507 => 'ʡ', + 67508 => 'ʢ', + 67509 => 'ʘ', + 67510 => 'ǀ', + 67511 => 'ǁ', + 67512 => 'ǂ', + 67513 => '𝼊', + 67514 => '𝼞', + 68736 => '𐳀', + 68737 => '𐳁', + 68738 => '𐳂', + 68739 => '𐳃', + 68740 => '𐳄', + 68741 => '𐳅', + 68742 => '𐳆', + 68743 => '𐳇', + 68744 => '𐳈', + 68745 => '𐳉', + 68746 => '𐳊', + 68747 => '𐳋', + 68748 => '𐳌', + 68749 => '𐳍', + 68750 => '𐳎', + 68751 => '𐳏', + 68752 => '𐳐', + 68753 => '𐳑', + 68754 => '𐳒', + 68755 => '𐳓', + 68756 => '𐳔', + 68757 => '𐳕', + 68758 => '𐳖', + 68759 => '𐳗', + 68760 => '𐳘', + 68761 => '𐳙', + 68762 => '𐳚', + 68763 => '𐳛', + 68764 => '𐳜', + 68765 => '𐳝', + 68766 => '𐳞', + 68767 => '𐳟', + 68768 => '𐳠', + 68769 => '𐳡', + 68770 => '𐳢', + 68771 => '𐳣', + 68772 => '𐳤', + 68773 => '𐳥', + 68774 => '𐳦', + 68775 => '𐳧', + 68776 => '𐳨', + 68777 => '𐳩', + 68778 => '𐳪', + 68779 => '𐳫', + 68780 => '𐳬', + 68781 => '𐳭', + 68782 => '𐳮', + 68783 => '𐳯', + 68784 => '𐳰', + 68785 => '𐳱', + 68786 => '𐳲', + 71840 => '𑣀', + 71841 => '𑣁', + 71842 => '𑣂', + 71843 => '𑣃', + 71844 => '𑣄', + 71845 => '𑣅', + 71846 => '𑣆', + 71847 => '𑣇', + 71848 => '𑣈', + 71849 => '𑣉', + 71850 => '𑣊', + 71851 => '𑣋', + 71852 => '𑣌', + 71853 => '𑣍', + 71854 => '𑣎', + 71855 => '𑣏', + 71856 => '𑣐', + 71857 => '𑣑', + 71858 => '𑣒', + 71859 => '𑣓', + 71860 => '𑣔', + 71861 => '𑣕', + 71862 => '𑣖', + 71863 => '𑣗', + 71864 => '𑣘', + 71865 => '𑣙', + 71866 => '𑣚', + 71867 => '𑣛', + 71868 => '𑣜', + 71869 => '𑣝', + 71870 => '𑣞', + 71871 => '𑣟', + 93760 => '𖹠', + 93761 => '𖹡', + 93762 => '𖹢', + 93763 => '𖹣', + 93764 => '𖹤', + 93765 => '𖹥', + 93766 => '𖹦', + 93767 => '𖹧', + 93768 => '𖹨', + 93769 => '𖹩', + 93770 => '𖹪', + 93771 => '𖹫', + 93772 => '𖹬', + 93773 => '𖹭', + 93774 => '𖹮', + 93775 => '𖹯', + 93776 => '𖹰', + 93777 => '𖹱', + 93778 => '𖹲', + 93779 => '𖹳', + 93780 => '𖹴', + 93781 => '𖹵', + 93782 => '𖹶', + 93783 => '𖹷', + 93784 => '𖹸', + 93785 => '𖹹', + 93786 => '𖹺', + 93787 => '𖹻', + 93788 => '𖹼', + 93789 => '𖹽', + 93790 => '𖹾', + 93791 => '𖹿', + 119134 => '𝅗𝅥', + 119135 => '𝅘𝅥', + 119136 => '𝅘𝅥𝅮', + 119137 => '𝅘𝅥𝅯', + 119138 => '𝅘𝅥𝅰', + 119139 => '𝅘𝅥𝅱', + 119140 => '𝅘𝅥𝅲', + 119227 => '𝆹𝅥', + 119228 => '𝆺𝅥', + 119229 => '𝆹𝅥𝅮', + 119230 => '𝆺𝅥𝅮', + 119231 => '𝆹𝅥𝅯', + 119232 => '𝆺𝅥𝅯', + 119808 => 'a', + 119809 => 'b', + 119810 => 'c', + 119811 => 'd', + 119812 => 'e', + 119813 => 'f', + 119814 => 'g', + 119815 => 'h', + 119816 => 'i', + 119817 => 'j', + 119818 => 'k', + 119819 => 'l', + 119820 => 'm', + 119821 => 'n', + 119822 => 'o', + 119823 => 'p', + 119824 => 'q', + 119825 => 'r', + 119826 => 's', + 119827 => 't', + 119828 => 'u', + 119829 => 'v', + 119830 => 'w', + 119831 => 'x', + 119832 => 'y', + 119833 => 'z', + 119834 => 'a', + 119835 => 'b', + 119836 => 'c', + 119837 => 'd', + 119838 => 'e', + 119839 => 'f', + 119840 => 'g', + 119841 => 'h', + 119842 => 'i', + 119843 => 'j', + 119844 => 'k', + 119845 => 'l', + 119846 => 'm', + 119847 => 'n', + 119848 => 'o', + 119849 => 'p', + 119850 => 'q', + 119851 => 'r', + 119852 => 's', + 119853 => 't', + 119854 => 'u', + 119855 => 'v', + 119856 => 'w', + 119857 => 'x', + 119858 => 'y', + 119859 => 'z', + 119860 => 'a', + 119861 => 'b', + 119862 => 'c', + 119863 => 'd', + 119864 => 'e', + 119865 => 'f', + 119866 => 'g', + 119867 => 'h', + 119868 => 'i', + 119869 => 'j', + 119870 => 'k', + 119871 => 'l', + 119872 => 'm', + 119873 => 'n', + 119874 => 'o', + 119875 => 'p', + 119876 => 'q', + 119877 => 'r', + 119878 => 's', + 119879 => 't', + 119880 => 'u', + 119881 => 'v', + 119882 => 'w', + 119883 => 'x', + 119884 => 'y', + 119885 => 'z', + 119886 => 'a', + 119887 => 'b', + 119888 => 'c', + 119889 => 'd', + 119890 => 'e', + 119891 => 'f', + 119892 => 'g', + 119894 => 'i', + 119895 => 'j', + 119896 => 'k', + 119897 => 'l', + 119898 => 'm', + 119899 => 'n', + 119900 => 'o', + 119901 => 'p', + 119902 => 'q', + 119903 => 'r', + 119904 => 's', + 119905 => 't', + 119906 => 'u', + 119907 => 'v', + 119908 => 'w', + 119909 => 'x', + 119910 => 'y', + 119911 => 'z', + 119912 => 'a', + 119913 => 'b', + 119914 => 'c', + 119915 => 'd', + 119916 => 'e', + 119917 => 'f', + 119918 => 'g', + 119919 => 'h', + 119920 => 'i', + 119921 => 'j', + 119922 => 'k', + 119923 => 'l', + 119924 => 'm', + 119925 => 'n', + 119926 => 'o', + 119927 => 'p', + 119928 => 'q', + 119929 => 'r', + 119930 => 's', + 119931 => 't', + 119932 => 'u', + 119933 => 'v', + 119934 => 'w', + 119935 => 'x', + 119936 => 'y', + 119937 => 'z', + 119938 => 'a', + 119939 => 'b', + 119940 => 'c', + 119941 => 'd', + 119942 => 'e', + 119943 => 'f', + 119944 => 'g', + 119945 => 'h', + 119946 => 'i', + 119947 => 'j', + 119948 => 'k', + 119949 => 'l', + 119950 => 'm', + 119951 => 'n', + 119952 => 'o', + 119953 => 'p', + 119954 => 'q', + 119955 => 'r', + 119956 => 's', + 119957 => 't', + 119958 => 'u', + 119959 => 'v', + 119960 => 'w', + 119961 => 'x', + 119962 => 'y', + 119963 => 'z', + 119964 => 'a', + 119966 => 'c', + 119967 => 'd', + 119970 => 'g', + 119973 => 'j', + 119974 => 'k', + 119977 => 'n', + 119978 => 'o', + 119979 => 'p', + 119980 => 'q', + 119982 => 's', + 119983 => 't', + 119984 => 'u', + 119985 => 'v', + 119986 => 'w', + 119987 => 'x', + 119988 => 'y', + 119989 => 'z', + 119990 => 'a', + 119991 => 'b', + 119992 => 'c', + 119993 => 'd', + 119995 => 'f', + 119997 => 'h', + 119998 => 'i', + 119999 => 'j', + 120000 => 'k', + 120001 => 'l', + 120002 => 'm', + 120003 => 'n', + 120005 => 'p', + 120006 => 'q', + 120007 => 'r', + 120008 => 's', + 120009 => 't', + 120010 => 'u', + 120011 => 'v', + 120012 => 'w', + 120013 => 'x', + 120014 => 'y', + 120015 => 'z', + 120016 => 'a', + 120017 => 'b', + 120018 => 'c', + 120019 => 'd', + 120020 => 'e', + 120021 => 'f', + 120022 => 'g', + 120023 => 'h', + 120024 => 'i', + 120025 => 'j', + 120026 => 'k', + 120027 => 'l', + 120028 => 'm', + 120029 => 'n', + 120030 => 'o', + 120031 => 'p', + 120032 => 'q', + 120033 => 'r', + 120034 => 's', + 120035 => 't', + 120036 => 'u', + 120037 => 'v', + 120038 => 'w', + 120039 => 'x', + 120040 => 'y', + 120041 => 'z', + 120042 => 'a', + 120043 => 'b', + 120044 => 'c', + 120045 => 'd', + 120046 => 'e', + 120047 => 'f', + 120048 => 'g', + 120049 => 'h', + 120050 => 'i', + 120051 => 'j', + 120052 => 'k', + 120053 => 'l', + 120054 => 'm', + 120055 => 'n', + 120056 => 'o', + 120057 => 'p', + 120058 => 'q', + 120059 => 'r', + 120060 => 's', + 120061 => 't', + 120062 => 'u', + 120063 => 'v', + 120064 => 'w', + 120065 => 'x', + 120066 => 'y', + 120067 => 'z', + 120068 => 'a', + 120069 => 'b', + 120071 => 'd', + 120072 => 'e', + 120073 => 'f', + 120074 => 'g', + 120077 => 'j', + 120078 => 'k', + 120079 => 'l', + 120080 => 'm', + 120081 => 'n', + 120082 => 'o', + 120083 => 'p', + 120084 => 'q', + 120086 => 's', + 120087 => 't', + 120088 => 'u', + 120089 => 'v', + 120090 => 'w', + 120091 => 'x', + 120092 => 'y', + 120094 => 'a', + 120095 => 'b', + 120096 => 'c', + 120097 => 'd', + 120098 => 'e', + 120099 => 'f', + 120100 => 'g', + 120101 => 'h', + 120102 => 'i', + 120103 => 'j', + 120104 => 'k', + 120105 => 'l', + 120106 => 'm', + 120107 => 'n', + 120108 => 'o', + 120109 => 'p', + 120110 => 'q', + 120111 => 'r', + 120112 => 's', + 120113 => 't', + 120114 => 'u', + 120115 => 'v', + 120116 => 'w', + 120117 => 'x', + 120118 => 'y', + 120119 => 'z', + 120120 => 'a', + 120121 => 'b', + 120123 => 'd', + 120124 => 'e', + 120125 => 'f', + 120126 => 'g', + 120128 => 'i', + 120129 => 'j', + 120130 => 'k', + 120131 => 'l', + 120132 => 'm', + 120134 => 'o', + 120138 => 's', + 120139 => 't', + 120140 => 'u', + 120141 => 'v', + 120142 => 'w', + 120143 => 'x', + 120144 => 'y', + 120146 => 'a', + 120147 => 'b', + 120148 => 'c', + 120149 => 'd', + 120150 => 'e', + 120151 => 'f', + 120152 => 'g', + 120153 => 'h', + 120154 => 'i', + 120155 => 'j', + 120156 => 'k', + 120157 => 'l', + 120158 => 'm', + 120159 => 'n', + 120160 => 'o', + 120161 => 'p', + 120162 => 'q', + 120163 => 'r', + 120164 => 's', + 120165 => 't', + 120166 => 'u', + 120167 => 'v', + 120168 => 'w', + 120169 => 'x', + 120170 => 'y', + 120171 => 'z', + 120172 => 'a', + 120173 => 'b', + 120174 => 'c', + 120175 => 'd', + 120176 => 'e', + 120177 => 'f', + 120178 => 'g', + 120179 => 'h', + 120180 => 'i', + 120181 => 'j', + 120182 => 'k', + 120183 => 'l', + 120184 => 'm', + 120185 => 'n', + 120186 => 'o', + 120187 => 'p', + 120188 => 'q', + 120189 => 'r', + 120190 => 's', + 120191 => 't', + 120192 => 'u', + 120193 => 'v', + 120194 => 'w', + 120195 => 'x', + 120196 => 'y', + 120197 => 'z', + 120198 => 'a', + 120199 => 'b', + 120200 => 'c', + 120201 => 'd', + 120202 => 'e', + 120203 => 'f', + 120204 => 'g', + 120205 => 'h', + 120206 => 'i', + 120207 => 'j', + 120208 => 'k', + 120209 => 'l', + 120210 => 'm', + 120211 => 'n', + 120212 => 'o', + 120213 => 'p', + 120214 => 'q', + 120215 => 'r', + 120216 => 's', + 120217 => 't', + 120218 => 'u', + 120219 => 'v', + 120220 => 'w', + 120221 => 'x', + 120222 => 'y', + 120223 => 'z', + 120224 => 'a', + 120225 => 'b', + 120226 => 'c', + 120227 => 'd', + 120228 => 'e', + 120229 => 'f', + 120230 => 'g', + 120231 => 'h', + 120232 => 'i', + 120233 => 'j', + 120234 => 'k', + 120235 => 'l', + 120236 => 'm', + 120237 => 'n', + 120238 => 'o', + 120239 => 'p', + 120240 => 'q', + 120241 => 'r', + 120242 => 's', + 120243 => 't', + 120244 => 'u', + 120245 => 'v', + 120246 => 'w', + 120247 => 'x', + 120248 => 'y', + 120249 => 'z', + 120250 => 'a', + 120251 => 'b', + 120252 => 'c', + 120253 => 'd', + 120254 => 'e', + 120255 => 'f', + 120256 => 'g', + 120257 => 'h', + 120258 => 'i', + 120259 => 'j', + 120260 => 'k', + 120261 => 'l', + 120262 => 'm', + 120263 => 'n', + 120264 => 'o', + 120265 => 'p', + 120266 => 'q', + 120267 => 'r', + 120268 => 's', + 120269 => 't', + 120270 => 'u', + 120271 => 'v', + 120272 => 'w', + 120273 => 'x', + 120274 => 'y', + 120275 => 'z', + 120276 => 'a', + 120277 => 'b', + 120278 => 'c', + 120279 => 'd', + 120280 => 'e', + 120281 => 'f', + 120282 => 'g', + 120283 => 'h', + 120284 => 'i', + 120285 => 'j', + 120286 => 'k', + 120287 => 'l', + 120288 => 'm', + 120289 => 'n', + 120290 => 'o', + 120291 => 'p', + 120292 => 'q', + 120293 => 'r', + 120294 => 's', + 120295 => 't', + 120296 => 'u', + 120297 => 'v', + 120298 => 'w', + 120299 => 'x', + 120300 => 'y', + 120301 => 'z', + 120302 => 'a', + 120303 => 'b', + 120304 => 'c', + 120305 => 'd', + 120306 => 'e', + 120307 => 'f', + 120308 => 'g', + 120309 => 'h', + 120310 => 'i', + 120311 => 'j', + 120312 => 'k', + 120313 => 'l', + 120314 => 'm', + 120315 => 'n', + 120316 => 'o', + 120317 => 'p', + 120318 => 'q', + 120319 => 'r', + 120320 => 's', + 120321 => 't', + 120322 => 'u', + 120323 => 'v', + 120324 => 'w', + 120325 => 'x', + 120326 => 'y', + 120327 => 'z', + 120328 => 'a', + 120329 => 'b', + 120330 => 'c', + 120331 => 'd', + 120332 => 'e', + 120333 => 'f', + 120334 => 'g', + 120335 => 'h', + 120336 => 'i', + 120337 => 'j', + 120338 => 'k', + 120339 => 'l', + 120340 => 'm', + 120341 => 'n', + 120342 => 'o', + 120343 => 'p', + 120344 => 'q', + 120345 => 'r', + 120346 => 's', + 120347 => 't', + 120348 => 'u', + 120349 => 'v', + 120350 => 'w', + 120351 => 'x', + 120352 => 'y', + 120353 => 'z', + 120354 => 'a', + 120355 => 'b', + 120356 => 'c', + 120357 => 'd', + 120358 => 'e', + 120359 => 'f', + 120360 => 'g', + 120361 => 'h', + 120362 => 'i', + 120363 => 'j', + 120364 => 'k', + 120365 => 'l', + 120366 => 'm', + 120367 => 'n', + 120368 => 'o', + 120369 => 'p', + 120370 => 'q', + 120371 => 'r', + 120372 => 's', + 120373 => 't', + 120374 => 'u', + 120375 => 'v', + 120376 => 'w', + 120377 => 'x', + 120378 => 'y', + 120379 => 'z', + 120380 => 'a', + 120381 => 'b', + 120382 => 'c', + 120383 => 'd', + 120384 => 'e', + 120385 => 'f', + 120386 => 'g', + 120387 => 'h', + 120388 => 'i', + 120389 => 'j', + 120390 => 'k', + 120391 => 'l', + 120392 => 'm', + 120393 => 'n', + 120394 => 'o', + 120395 => 'p', + 120396 => 'q', + 120397 => 'r', + 120398 => 's', + 120399 => 't', + 120400 => 'u', + 120401 => 'v', + 120402 => 'w', + 120403 => 'x', + 120404 => 'y', + 120405 => 'z', + 120406 => 'a', + 120407 => 'b', + 120408 => 'c', + 120409 => 'd', + 120410 => 'e', + 120411 => 'f', + 120412 => 'g', + 120413 => 'h', + 120414 => 'i', + 120415 => 'j', + 120416 => 'k', + 120417 => 'l', + 120418 => 'm', + 120419 => 'n', + 120420 => 'o', + 120421 => 'p', + 120422 => 'q', + 120423 => 'r', + 120424 => 's', + 120425 => 't', + 120426 => 'u', + 120427 => 'v', + 120428 => 'w', + 120429 => 'x', + 120430 => 'y', + 120431 => 'z', + 120432 => 'a', + 120433 => 'b', + 120434 => 'c', + 120435 => 'd', + 120436 => 'e', + 120437 => 'f', + 120438 => 'g', + 120439 => 'h', + 120440 => 'i', + 120441 => 'j', + 120442 => 'k', + 120443 => 'l', + 120444 => 'm', + 120445 => 'n', + 120446 => 'o', + 120447 => 'p', + 120448 => 'q', + 120449 => 'r', + 120450 => 's', + 120451 => 't', + 120452 => 'u', + 120453 => 'v', + 120454 => 'w', + 120455 => 'x', + 120456 => 'y', + 120457 => 'z', + 120458 => 'a', + 120459 => 'b', + 120460 => 'c', + 120461 => 'd', + 120462 => 'e', + 120463 => 'f', + 120464 => 'g', + 120465 => 'h', + 120466 => 'i', + 120467 => 'j', + 120468 => 'k', + 120469 => 'l', + 120470 => 'm', + 120471 => 'n', + 120472 => 'o', + 120473 => 'p', + 120474 => 'q', + 120475 => 'r', + 120476 => 's', + 120477 => 't', + 120478 => 'u', + 120479 => 'v', + 120480 => 'w', + 120481 => 'x', + 120482 => 'y', + 120483 => 'z', + 120484 => 'ı', + 120485 => 'ȷ', + 120488 => 'α', + 120489 => 'β', + 120490 => 'γ', + 120491 => 'δ', + 120492 => 'ε', + 120493 => 'ζ', + 120494 => 'η', + 120495 => 'θ', + 120496 => 'ι', + 120497 => 'κ', + 120498 => 'λ', + 120499 => 'μ', + 120500 => 'ν', + 120501 => 'ξ', + 120502 => 'ο', + 120503 => 'π', + 120504 => 'ρ', + 120505 => 'θ', + 120506 => 'σ', + 120507 => 'τ', + 120508 => 'υ', + 120509 => 'φ', + 120510 => 'χ', + 120511 => 'ψ', + 120512 => 'ω', + 120513 => '∇', + 120514 => 'α', + 120515 => 'β', + 120516 => 'γ', + 120517 => 'δ', + 120518 => 'ε', + 120519 => 'ζ', + 120520 => 'η', + 120521 => 'θ', + 120522 => 'ι', + 120523 => 'κ', + 120524 => 'λ', + 120525 => 'μ', + 120526 => 'ν', + 120527 => 'ξ', + 120528 => 'ο', + 120529 => 'π', + 120530 => 'ρ', + 120531 => 'σ', + 120532 => 'σ', + 120533 => 'τ', + 120534 => 'υ', + 120535 => 'φ', + 120536 => 'χ', + 120537 => 'ψ', + 120538 => 'ω', + 120539 => '∂', + 120540 => 'ε', + 120541 => 'θ', + 120542 => 'κ', + 120543 => 'φ', + 120544 => 'ρ', + 120545 => 'π', + 120546 => 'α', + 120547 => 'β', + 120548 => 'γ', + 120549 => 'δ', + 120550 => 'ε', + 120551 => 'ζ', + 120552 => 'η', + 120553 => 'θ', + 120554 => 'ι', + 120555 => 'κ', + 120556 => 'λ', + 120557 => 'μ', + 120558 => 'ν', + 120559 => 'ξ', + 120560 => 'ο', + 120561 => 'π', + 120562 => 'ρ', + 120563 => 'θ', + 120564 => 'σ', + 120565 => 'τ', + 120566 => 'υ', + 120567 => 'φ', + 120568 => 'χ', + 120569 => 'ψ', + 120570 => 'ω', + 120571 => '∇', + 120572 => 'α', + 120573 => 'β', + 120574 => 'γ', + 120575 => 'δ', + 120576 => 'ε', + 120577 => 'ζ', + 120578 => 'η', + 120579 => 'θ', + 120580 => 'ι', + 120581 => 'κ', + 120582 => 'λ', + 120583 => 'μ', + 120584 => 'ν', + 120585 => 'ξ', + 120586 => 'ο', + 120587 => 'π', + 120588 => 'ρ', + 120589 => 'σ', + 120590 => 'σ', + 120591 => 'τ', + 120592 => 'υ', + 120593 => 'φ', + 120594 => 'χ', + 120595 => 'ψ', + 120596 => 'ω', + 120597 => '∂', + 120598 => 'ε', + 120599 => 'θ', + 120600 => 'κ', + 120601 => 'φ', + 120602 => 'ρ', + 120603 => 'π', + 120604 => 'α', + 120605 => 'β', + 120606 => 'γ', + 120607 => 'δ', + 120608 => 'ε', + 120609 => 'ζ', + 120610 => 'η', + 120611 => 'θ', + 120612 => 'ι', + 120613 => 'κ', + 120614 => 'λ', + 120615 => 'μ', + 120616 => 'ν', + 120617 => 'ξ', + 120618 => 'ο', + 120619 => 'π', + 120620 => 'ρ', + 120621 => 'θ', + 120622 => 'σ', + 120623 => 'τ', + 120624 => 'υ', + 120625 => 'φ', + 120626 => 'χ', + 120627 => 'ψ', + 120628 => 'ω', + 120629 => '∇', + 120630 => 'α', + 120631 => 'β', + 120632 => 'γ', + 120633 => 'δ', + 120634 => 'ε', + 120635 => 'ζ', + 120636 => 'η', + 120637 => 'θ', + 120638 => 'ι', + 120639 => 'κ', + 120640 => 'λ', + 120641 => 'μ', + 120642 => 'ν', + 120643 => 'ξ', + 120644 => 'ο', + 120645 => 'π', + 120646 => 'ρ', + 120647 => 'σ', + 120648 => 'σ', + 120649 => 'τ', + 120650 => 'υ', + 120651 => 'φ', + 120652 => 'χ', + 120653 => 'ψ', + 120654 => 'ω', + 120655 => '∂', + 120656 => 'ε', + 120657 => 'θ', + 120658 => 'κ', + 120659 => 'φ', + 120660 => 'ρ', + 120661 => 'π', + 120662 => 'α', + 120663 => 'β', + 120664 => 'γ', + 120665 => 'δ', + 120666 => 'ε', + 120667 => 'ζ', + 120668 => 'η', + 120669 => 'θ', + 120670 => 'ι', + 120671 => 'κ', + 120672 => 'λ', + 120673 => 'μ', + 120674 => 'ν', + 120675 => 'ξ', + 120676 => 'ο', + 120677 => 'π', + 120678 => 'ρ', + 120679 => 'θ', + 120680 => 'σ', + 120681 => 'τ', + 120682 => 'υ', + 120683 => 'φ', + 120684 => 'χ', + 120685 => 'ψ', + 120686 => 'ω', + 120687 => '∇', + 120688 => 'α', + 120689 => 'β', + 120690 => 'γ', + 120691 => 'δ', + 120692 => 'ε', + 120693 => 'ζ', + 120694 => 'η', + 120695 => 'θ', + 120696 => 'ι', + 120697 => 'κ', + 120698 => 'λ', + 120699 => 'μ', + 120700 => 'ν', + 120701 => 'ξ', + 120702 => 'ο', + 120703 => 'π', + 120704 => 'ρ', + 120705 => 'σ', + 120706 => 'σ', + 120707 => 'τ', + 120708 => 'υ', + 120709 => 'φ', + 120710 => 'χ', + 120711 => 'ψ', + 120712 => 'ω', + 120713 => '∂', + 120714 => 'ε', + 120715 => 'θ', + 120716 => 'κ', + 120717 => 'φ', + 120718 => 'ρ', + 120719 => 'π', + 120720 => 'α', + 120721 => 'β', + 120722 => 'γ', + 120723 => 'δ', + 120724 => 'ε', + 120725 => 'ζ', + 120726 => 'η', + 120727 => 'θ', + 120728 => 'ι', + 120729 => 'κ', + 120730 => 'λ', + 120731 => 'μ', + 120732 => 'ν', + 120733 => 'ξ', + 120734 => 'ο', + 120735 => 'π', + 120736 => 'ρ', + 120737 => 'θ', + 120738 => 'σ', + 120739 => 'τ', + 120740 => 'υ', + 120741 => 'φ', + 120742 => 'χ', + 120743 => 'ψ', + 120744 => 'ω', + 120745 => '∇', + 120746 => 'α', + 120747 => 'β', + 120748 => 'γ', + 120749 => 'δ', + 120750 => 'ε', + 120751 => 'ζ', + 120752 => 'η', + 120753 => 'θ', + 120754 => 'ι', + 120755 => 'κ', + 120756 => 'λ', + 120757 => 'μ', + 120758 => 'ν', + 120759 => 'ξ', + 120760 => 'ο', + 120761 => 'π', + 120762 => 'ρ', + 120763 => 'σ', + 120764 => 'σ', + 120765 => 'τ', + 120766 => 'υ', + 120767 => 'φ', + 120768 => 'χ', + 120769 => 'ψ', + 120770 => 'ω', + 120771 => '∂', + 120772 => 'ε', + 120773 => 'θ', + 120774 => 'κ', + 120775 => 'φ', + 120776 => 'ρ', + 120777 => 'π', + 120778 => 'ϝ', + 120779 => 'ϝ', + 120782 => '0', + 120783 => '1', + 120784 => '2', + 120785 => '3', + 120786 => '4', + 120787 => '5', + 120788 => '6', + 120789 => '7', + 120790 => '8', + 120791 => '9', + 120792 => '0', + 120793 => '1', + 120794 => '2', + 120795 => '3', + 120796 => '4', + 120797 => '5', + 120798 => '6', + 120799 => '7', + 120800 => '8', + 120801 => '9', + 120802 => '0', + 120803 => '1', + 120804 => '2', + 120805 => '3', + 120806 => '4', + 120807 => '5', + 120808 => '6', + 120809 => '7', + 120810 => '8', + 120811 => '9', + 120812 => '0', + 120813 => '1', + 120814 => '2', + 120815 => '3', + 120816 => '4', + 120817 => '5', + 120818 => '6', + 120819 => '7', + 120820 => '8', + 120821 => '9', + 120822 => '0', + 120823 => '1', + 120824 => '2', + 120825 => '3', + 120826 => '4', + 120827 => '5', + 120828 => '6', + 120829 => '7', + 120830 => '8', + 120831 => '9', + 125184 => '𞤢', + 125185 => '𞤣', + 125186 => '𞤤', + 125187 => '𞤥', + 125188 => '𞤦', + 125189 => '𞤧', + 125190 => '𞤨', + 125191 => '𞤩', + 125192 => '𞤪', + 125193 => '𞤫', + 125194 => '𞤬', + 125195 => '𞤭', + 125196 => '𞤮', + 125197 => '𞤯', + 125198 => '𞤰', + 125199 => '𞤱', + 125200 => '𞤲', + 125201 => '𞤳', + 125202 => '𞤴', + 125203 => '𞤵', + 125204 => '𞤶', + 125205 => '𞤷', + 125206 => '𞤸', + 125207 => '𞤹', + 125208 => '𞤺', + 125209 => '𞤻', + 125210 => '𞤼', + 125211 => '𞤽', + 125212 => '𞤾', + 125213 => '𞤿', + 125214 => '𞥀', + 125215 => '𞥁', + 125216 => '𞥂', + 125217 => '𞥃', + 126464 => 'ا', + 126465 => 'ب', + 126466 => 'ج', + 126467 => 'د', + 126469 => 'و', + 126470 => 'ز', + 126471 => 'ح', + 126472 => 'ط', + 126473 => 'ي', + 126474 => 'ك', + 126475 => 'ل', + 126476 => 'م', + 126477 => 'ن', + 126478 => 'س', + 126479 => 'ع', + 126480 => 'ف', + 126481 => 'ص', + 126482 => 'ق', + 126483 => 'ر', + 126484 => 'ش', + 126485 => 'ت', + 126486 => 'ث', + 126487 => 'خ', + 126488 => 'ذ', + 126489 => 'ض', + 126490 => 'ظ', + 126491 => 'غ', + 126492 => 'ٮ', + 126493 => 'ں', + 126494 => 'ڡ', + 126495 => 'ٯ', + 126497 => 'ب', + 126498 => 'ج', + 126500 => 'ه', + 126503 => 'ح', + 126505 => 'ي', + 126506 => 'ك', + 126507 => 'ل', + 126508 => 'م', + 126509 => 'ن', + 126510 => 'س', + 126511 => 'ع', + 126512 => 'ف', + 126513 => 'ص', + 126514 => 'ق', + 126516 => 'ش', + 126517 => 'ت', + 126518 => 'ث', + 126519 => 'خ', + 126521 => 'ض', + 126523 => 'غ', + 126530 => 'ج', + 126535 => 'ح', + 126537 => 'ي', + 126539 => 'ل', + 126541 => 'ن', + 126542 => 'س', + 126543 => 'ع', + 126545 => 'ص', + 126546 => 'ق', + 126548 => 'ش', + 126551 => 'خ', + 126553 => 'ض', + 126555 => 'غ', + 126557 => 'ں', + 126559 => 'ٯ', + 126561 => 'ب', + 126562 => 'ج', + 126564 => 'ه', + 126567 => 'ح', + 126568 => 'ط', + 126569 => 'ي', + 126570 => 'ك', + 126572 => 'م', + 126573 => 'ن', + 126574 => 'س', + 126575 => 'ع', + 126576 => 'ف', + 126577 => 'ص', + 126578 => 'ق', + 126580 => 'ش', + 126581 => 'ت', + 126582 => 'ث', + 126583 => 'خ', + 126585 => 'ض', + 126586 => 'ظ', + 126587 => 'غ', + 126588 => 'ٮ', + 126590 => 'ڡ', + 126592 => 'ا', + 126593 => 'ب', + 126594 => 'ج', + 126595 => 'د', + 126596 => 'ه', + 126597 => 'و', + 126598 => 'ز', + 126599 => 'ح', + 126600 => 'ط', + 126601 => 'ي', + 126603 => 'ل', + 126604 => 'م', + 126605 => 'ن', + 126606 => 'س', + 126607 => 'ع', + 126608 => 'ف', + 126609 => 'ص', + 126610 => 'ق', + 126611 => 'ر', + 126612 => 'ش', + 126613 => 'ت', + 126614 => 'ث', + 126615 => 'خ', + 126616 => 'ذ', + 126617 => 'ض', + 126618 => 'ظ', + 126619 => 'غ', + 126625 => 'ب', + 126626 => 'ج', + 126627 => 'د', + 126629 => 'و', + 126630 => 'ز', + 126631 => 'ح', + 126632 => 'ط', + 126633 => 'ي', + 126635 => 'ل', + 126636 => 'م', + 126637 => 'ن', + 126638 => 'س', + 126639 => 'ع', + 126640 => 'ف', + 126641 => 'ص', + 126642 => 'ق', + 126643 => 'ر', + 126644 => 'ش', + 126645 => 'ت', + 126646 => 'ث', + 126647 => 'خ', + 126648 => 'ذ', + 126649 => 'ض', + 126650 => 'ظ', + 126651 => 'غ', + 127274 => '〔s〕', + 127275 => 'c', + 127276 => 'r', + 127277 => 'cd', + 127278 => 'wz', + 127280 => 'a', + 127281 => 'b', + 127282 => 'c', + 127283 => 'd', + 127284 => 'e', + 127285 => 'f', + 127286 => 'g', + 127287 => 'h', + 127288 => 'i', + 127289 => 'j', + 127290 => 'k', + 127291 => 'l', + 127292 => 'm', + 127293 => 'n', + 127294 => 'o', + 127295 => 'p', + 127296 => 'q', + 127297 => 'r', + 127298 => 's', + 127299 => 't', + 127300 => 'u', + 127301 => 'v', + 127302 => 'w', + 127303 => 'x', + 127304 => 'y', + 127305 => 'z', + 127306 => 'hv', + 127307 => 'mv', + 127308 => 'sd', + 127309 => 'ss', + 127310 => 'ppv', + 127311 => 'wc', + 127338 => 'mc', + 127339 => 'md', + 127340 => 'mr', + 127376 => 'dj', + 127488 => 'ほか', + 127489 => 'ココ', + 127490 => 'サ', + 127504 => '手', + 127505 => '字', + 127506 => '双', + 127507 => 'デ', + 127508 => '二', + 127509 => '多', + 127510 => '解', + 127511 => '天', + 127512 => '交', + 127513 => '映', + 127514 => '無', + 127515 => '料', + 127516 => '前', + 127517 => '後', + 127518 => '再', + 127519 => '新', + 127520 => '初', + 127521 => '終', + 127522 => '生', + 127523 => '販', + 127524 => '声', + 127525 => '吹', + 127526 => '演', + 127527 => '投', + 127528 => '捕', + 127529 => '一', + 127530 => '三', + 127531 => '遊', + 127532 => '左', + 127533 => '中', + 127534 => '右', + 127535 => '指', + 127536 => '走', + 127537 => '打', + 127538 => '禁', + 127539 => '空', + 127540 => '合', + 127541 => '満', + 127542 => '有', + 127543 => '月', + 127544 => '申', + 127545 => '割', + 127546 => '営', + 127547 => '配', + 127552 => '〔本〕', + 127553 => '〔三〕', + 127554 => '〔二〕', + 127555 => '〔安〕', + 127556 => '〔点〕', + 127557 => '〔打〕', + 127558 => '〔盗〕', + 127559 => '〔勝〕', + 127560 => '〔敗〕', + 127568 => '得', + 127569 => '可', + 130032 => '0', + 130033 => '1', + 130034 => '2', + 130035 => '3', + 130036 => '4', + 130037 => '5', + 130038 => '6', + 130039 => '7', + 130040 => '8', + 130041 => '9', + 194560 => '丽', + 194561 => '丸', + 194562 => '乁', + 194563 => '𠄢', + 194564 => '你', + 194565 => '侮', + 194566 => '侻', + 194567 => '倂', + 194568 => '偺', + 194569 => '備', + 194570 => '僧', + 194571 => '像', + 194572 => '㒞', + 194573 => '𠘺', + 194574 => '免', + 194575 => '兔', + 194576 => '兤', + 194577 => '具', + 194578 => '𠔜', + 194579 => '㒹', + 194580 => '內', + 194581 => '再', + 194582 => '𠕋', + 194583 => '冗', + 194584 => '冤', + 194585 => '仌', + 194586 => '冬', + 194587 => '况', + 194588 => '𩇟', + 194589 => '凵', + 194590 => '刃', + 194591 => '㓟', + 194592 => '刻', + 194593 => '剆', + 194594 => '割', + 194595 => '剷', + 194596 => '㔕', + 194597 => '勇', + 194598 => '勉', + 194599 => '勤', + 194600 => '勺', + 194601 => '包', + 194602 => '匆', + 194603 => '北', + 194604 => '卉', + 194605 => '卑', + 194606 => '博', + 194607 => '即', + 194608 => '卽', + 194609 => '卿', + 194610 => '卿', + 194611 => '卿', + 194612 => '𠨬', + 194613 => '灰', + 194614 => '及', + 194615 => '叟', + 194616 => '𠭣', + 194617 => '叫', + 194618 => '叱', + 194619 => '吆', + 194620 => '咞', + 194621 => '吸', + 194622 => '呈', + 194623 => '周', + 194624 => '咢', + 194625 => '哶', + 194626 => '唐', + 194627 => '啓', + 194628 => '啣', + 194629 => '善', + 194630 => '善', + 194631 => '喙', + 194632 => '喫', + 194633 => '喳', + 194634 => '嗂', + 194635 => '圖', + 194636 => '嘆', + 194637 => '圗', + 194638 => '噑', + 194639 => '噴', + 194640 => '切', + 194641 => '壮', + 194642 => '城', + 194643 => '埴', + 194644 => '堍', + 194645 => '型', + 194646 => '堲', + 194647 => '報', + 194648 => '墬', + 194649 => '𡓤', + 194650 => '売', + 194651 => '壷', + 194652 => '夆', + 194653 => '多', + 194654 => '夢', + 194655 => '奢', + 194656 => '𡚨', + 194657 => '𡛪', + 194658 => '姬', + 194659 => '娛', + 194660 => '娧', + 194661 => '姘', + 194662 => '婦', + 194663 => '㛮', + 194665 => '嬈', + 194666 => '嬾', + 194667 => '嬾', + 194668 => '𡧈', + 194669 => '寃', + 194670 => '寘', + 194671 => '寧', + 194672 => '寳', + 194673 => '𡬘', + 194674 => '寿', + 194675 => '将', + 194677 => '尢', + 194678 => '㞁', + 194679 => '屠', + 194680 => '屮', + 194681 => '峀', + 194682 => '岍', + 194683 => '𡷤', + 194684 => '嵃', + 194685 => '𡷦', + 194686 => '嵮', + 194687 => '嵫', + 194688 => '嵼', + 194689 => '巡', + 194690 => '巢', + 194691 => '㠯', + 194692 => '巽', + 194693 => '帨', + 194694 => '帽', + 194695 => '幩', + 194696 => '㡢', + 194697 => '𢆃', + 194698 => '㡼', + 194699 => '庰', + 194700 => '庳', + 194701 => '庶', + 194702 => '廊', + 194703 => '𪎒', + 194704 => '廾', + 194705 => '𢌱', + 194706 => '𢌱', + 194707 => '舁', + 194708 => '弢', + 194709 => '弢', + 194710 => '㣇', + 194711 => '𣊸', + 194712 => '𦇚', + 194713 => '形', + 194714 => '彫', + 194715 => '㣣', + 194716 => '徚', + 194717 => '忍', + 194718 => '志', + 194719 => '忹', + 194720 => '悁', + 194721 => '㤺', + 194722 => '㤜', + 194723 => '悔', + 194724 => '𢛔', + 194725 => '惇', + 194726 => '慈', + 194727 => '慌', + 194728 => '慎', + 194729 => '慌', + 194730 => '慺', + 194731 => '憎', + 194732 => '憲', + 194733 => '憤', + 194734 => '憯', + 194735 => '懞', + 194736 => '懲', + 194737 => '懶', + 194738 => '成', + 194739 => '戛', + 194740 => '扝', + 194741 => '抱', + 194742 => '拔', + 194743 => '捐', + 194744 => '𢬌', + 194745 => '挽', + 194746 => '拼', + 194747 => '捨', + 194748 => '掃', + 194749 => '揤', + 194750 => '𢯱', + 194751 => '搢', + 194752 => '揅', + 194753 => '掩', + 194754 => '㨮', + 194755 => '摩', + 194756 => '摾', + 194757 => '撝', + 194758 => '摷', + 194759 => '㩬', + 194760 => '敏', + 194761 => '敬', + 194762 => '𣀊', + 194763 => '旣', + 194764 => '書', + 194765 => '晉', + 194766 => '㬙', + 194767 => '暑', + 194768 => '㬈', + 194769 => '㫤', + 194770 => '冒', + 194771 => '冕', + 194772 => '最', + 194773 => '暜', + 194774 => '肭', + 194775 => '䏙', + 194776 => '朗', + 194777 => '望', + 194778 => '朡', + 194779 => '杞', + 194780 => '杓', + 194781 => '𣏃', + 194782 => '㭉', + 194783 => '柺', + 194784 => '枅', + 194785 => '桒', + 194786 => '梅', + 194787 => '𣑭', + 194788 => '梎', + 194789 => '栟', + 194790 => '椔', + 194791 => '㮝', + 194792 => '楂', + 194793 => '榣', + 194794 => '槪', + 194795 => '檨', + 194796 => '𣚣', + 194797 => '櫛', + 194798 => '㰘', + 194799 => '次', + 194800 => '𣢧', + 194801 => '歔', + 194802 => '㱎', + 194803 => '歲', + 194804 => '殟', + 194805 => '殺', + 194806 => '殻', + 194807 => '𣪍', + 194808 => '𡴋', + 194809 => '𣫺', + 194810 => '汎', + 194811 => '𣲼', + 194812 => '沿', + 194813 => '泍', + 194814 => '汧', + 194815 => '洖', + 194816 => '派', + 194817 => '海', + 194818 => '流', + 194819 => '浩', + 194820 => '浸', + 194821 => '涅', + 194822 => '𣴞', + 194823 => '洴', + 194824 => '港', + 194825 => '湮', + 194826 => '㴳', + 194827 => '滋', + 194828 => '滇', + 194829 => '𣻑', + 194830 => '淹', + 194831 => '潮', + 194832 => '𣽞', + 194833 => '𣾎', + 194834 => '濆', + 194835 => '瀹', + 194836 => '瀞', + 194837 => '瀛', + 194838 => '㶖', + 194839 => '灊', + 194840 => '災', + 194841 => '灷', + 194842 => '炭', + 194843 => '𠔥', + 194844 => '煅', + 194845 => '𤉣', + 194846 => '熜', + 194848 => '爨', + 194849 => '爵', + 194850 => '牐', + 194851 => '𤘈', + 194852 => '犀', + 194853 => '犕', + 194854 => '𤜵', + 194855 => '𤠔', + 194856 => '獺', + 194857 => '王', + 194858 => '㺬', + 194859 => '玥', + 194860 => '㺸', + 194861 => '㺸', + 194862 => '瑇', + 194863 => '瑜', + 194864 => '瑱', + 194865 => '璅', + 194866 => '瓊', + 194867 => '㼛', + 194868 => '甤', + 194869 => '𤰶', + 194870 => '甾', + 194871 => '𤲒', + 194872 => '異', + 194873 => '𢆟', + 194874 => '瘐', + 194875 => '𤾡', + 194876 => '𤾸', + 194877 => '𥁄', + 194878 => '㿼', + 194879 => '䀈', + 194880 => '直', + 194881 => '𥃳', + 194882 => '𥃲', + 194883 => '𥄙', + 194884 => '𥄳', + 194885 => '眞', + 194886 => '真', + 194887 => '真', + 194888 => '睊', + 194889 => '䀹', + 194890 => '瞋', + 194891 => '䁆', + 194892 => '䂖', + 194893 => '𥐝', + 194894 => '硎', + 194895 => '碌', + 194896 => '磌', + 194897 => '䃣', + 194898 => '𥘦', + 194899 => '祖', + 194900 => '𥚚', + 194901 => '𥛅', + 194902 => '福', + 194903 => '秫', + 194904 => '䄯', + 194905 => '穀', + 194906 => '穊', + 194907 => '穏', + 194908 => '𥥼', + 194909 => '𥪧', + 194910 => '𥪧', + 194912 => '䈂', + 194913 => '𥮫', + 194914 => '篆', + 194915 => '築', + 194916 => '䈧', + 194917 => '𥲀', + 194918 => '糒', + 194919 => '䊠', + 194920 => '糨', + 194921 => '糣', + 194922 => '紀', + 194923 => '𥾆', + 194924 => '絣', + 194925 => '䌁', + 194926 => '緇', + 194927 => '縂', + 194928 => '繅', + 194929 => '䌴', + 194930 => '𦈨', + 194931 => '𦉇', + 194932 => '䍙', + 194933 => '𦋙', + 194934 => '罺', + 194935 => '𦌾', + 194936 => '羕', + 194937 => '翺', + 194938 => '者', + 194939 => '𦓚', + 194940 => '𦔣', + 194941 => '聠', + 194942 => '𦖨', + 194943 => '聰', + 194944 => '𣍟', + 194945 => '䏕', + 194946 => '育', + 194947 => '脃', + 194948 => '䐋', + 194949 => '脾', + 194950 => '媵', + 194951 => '𦞧', + 194952 => '𦞵', + 194953 => '𣎓', + 194954 => '𣎜', + 194955 => '舁', + 194956 => '舄', + 194957 => '辞', + 194958 => '䑫', + 194959 => '芑', + 194960 => '芋', + 194961 => '芝', + 194962 => '劳', + 194963 => '花', + 194964 => '芳', + 194965 => '芽', + 194966 => '苦', + 194967 => '𦬼', + 194968 => '若', + 194969 => '茝', + 194970 => '荣', + 194971 => '莭', + 194972 => '茣', + 194973 => '莽', + 194974 => '菧', + 194975 => '著', + 194976 => '荓', + 194977 => '菊', + 194978 => '菌', + 194979 => '菜', + 194980 => '𦰶', + 194981 => '𦵫', + 194982 => '𦳕', + 194983 => '䔫', + 194984 => '蓱', + 194985 => '蓳', + 194986 => '蔖', + 194987 => '𧏊', + 194988 => '蕤', + 194989 => '𦼬', + 194990 => '䕝', + 194991 => '䕡', + 194992 => '𦾱', + 194993 => '𧃒', + 194994 => '䕫', + 194995 => '虐', + 194996 => '虜', + 194997 => '虧', + 194998 => '虩', + 194999 => '蚩', + 195000 => '蚈', + 195001 => '蜎', + 195002 => '蛢', + 195003 => '蝹', + 195004 => '蜨', + 195005 => '蝫', + 195006 => '螆', + 195008 => '蟡', + 195009 => '蠁', + 195010 => '䗹', + 195011 => '衠', + 195012 => '衣', + 195013 => '𧙧', + 195014 => '裗', + 195015 => '裞', + 195016 => '䘵', + 195017 => '裺', + 195018 => '㒻', + 195019 => '𧢮', + 195020 => '𧥦', + 195021 => '䚾', + 195022 => '䛇', + 195023 => '誠', + 195024 => '諭', + 195025 => '變', + 195026 => '豕', + 195027 => '𧲨', + 195028 => '貫', + 195029 => '賁', + 195030 => '贛', + 195031 => '起', + 195032 => '𧼯', + 195033 => '𠠄', + 195034 => '跋', + 195035 => '趼', + 195036 => '跰', + 195037 => '𠣞', + 195038 => '軔', + 195039 => '輸', + 195040 => '𨗒', + 195041 => '𨗭', + 195042 => '邔', + 195043 => '郱', + 195044 => '鄑', + 195045 => '𨜮', + 195046 => '鄛', + 195047 => '鈸', + 195048 => '鋗', + 195049 => '鋘', + 195050 => '鉼', + 195051 => '鏹', + 195052 => '鐕', + 195053 => '𨯺', + 195054 => '開', + 195055 => '䦕', + 195056 => '閷', + 195057 => '𨵷', + 195058 => '䧦', + 195059 => '雃', + 195060 => '嶲', + 195061 => '霣', + 195062 => '𩅅', + 195063 => '𩈚', + 195064 => '䩮', + 195065 => '䩶', + 195066 => '韠', + 195067 => '𩐊', + 195068 => '䪲', + 195069 => '𩒖', + 195070 => '頋', + 195071 => '頋', + 195072 => '頩', + 195073 => '𩖶', + 195074 => '飢', + 195075 => '䬳', + 195076 => '餩', + 195077 => '馧', + 195078 => '駂', + 195079 => '駾', + 195080 => '䯎', + 195081 => '𩬰', + 195082 => '鬒', + 195083 => '鱀', + 195084 => '鳽', + 195085 => '䳎', + 195086 => '䳭', + 195087 => '鵧', + 195088 => '𪃎', + 195089 => '䳸', + 195090 => '𪄅', + 195091 => '𪈎', + 195092 => '𪊑', + 195093 => '麻', + 195094 => '䵖', + 195095 => '黹', + 195096 => '黾', + 195097 => '鼅', + 195098 => '鼏', + 195099 => '鼖', + 195100 => '鼻', + 195101 => '𪘀', +); diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/virama.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/virama.php new file mode 100644 index 0000000000..63c9bbcdcd --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/resources/virama.php @@ -0,0 +1,67 @@ + 9, + 2509 => 9, + 2637 => 9, + 2765 => 9, + 2893 => 9, + 3021 => 9, + 3149 => 9, + 3277 => 9, + 3387 => 9, + 3388 => 9, + 3405 => 9, + 3530 => 9, + 3642 => 9, + 3770 => 9, + 3972 => 9, + 4153 => 9, + 4154 => 9, + 5908 => 9, + 5909 => 9, + 5940 => 9, + 6098 => 9, + 6752 => 9, + 6980 => 9, + 7082 => 9, + 7083 => 9, + 7154 => 9, + 7155 => 9, + 11647 => 9, + 43014 => 9, + 43052 => 9, + 43204 => 9, + 43347 => 9, + 43456 => 9, + 43766 => 9, + 44013 => 9, + 68159 => 9, + 69702 => 9, + 69744 => 9, + 69759 => 9, + 69817 => 9, + 69939 => 9, + 69940 => 9, + 70080 => 9, + 70197 => 9, + 70378 => 9, + 70477 => 9, + 70722 => 9, + 70850 => 9, + 71103 => 9, + 71231 => 9, + 71350 => 9, + 71467 => 9, + 71737 => 9, + 71997 => 9, + 71998 => 9, + 72160 => 9, + 72244 => 9, + 72263 => 9, + 72345 => 9, + 72767 => 9, + 73028 => 9, + 73029 => 9, + 73111 => 9, +); diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/CodePoint.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/CodePoint.php new file mode 100644 index 0000000000..d7203e73ff --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/CodePoint.php @@ -0,0 +1,139 @@ += 0x00 && $codePoint <= 0x7F) { + return chr($codePoint); + } + + $count = 0; + $offset = 0; + + if ($codePoint >= 0x0080 && $codePoint <= 0x07FF) { + $count = 1; + $offset = 0xC0; + } elseif ($codePoint >= 0x0800 && $codePoint <= 0xFFFF) { + $count = 2; + $offset = 0xE0; + } elseif ($codePoint >= 0x10000 && $codePoint <= 0x10FFFF) { + $count = 3; + $offset = 0xF0; + } + + $bytes = chr(($codePoint >> (6 * $count)) + $offset); + + while ($count > 0) { + $temp = $codePoint >> (6 * ($count - 1)); + $bytes .= chr(0x80 | ($temp & 0x3F)); + --$count; + } + + return $bytes; + } + + /** + * Takes a UTF-8 encoded string and converts it into a series of integer code points. Any + * invalid byte sequences will be replaced by a U+FFFD replacement code point. + * + * @see https://encoding.spec.whatwg.org/#utf-8-decoder + * + * @return list + */ + public static function utf8Decode(string $input): array + { + $bytesSeen = 0; + $bytesNeeded = 0; + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + $codePoint = 0; + $codePoints = []; + $length = strlen($input); + + for ($i = 0; $i < $length; ++$i) { + $byte = ord($input[$i]); + + if ($bytesNeeded === 0) { + if ($byte >= 0x00 && $byte <= 0x7F) { + $codePoints[] = $byte; + + continue; + } + + if ($byte >= 0xC2 && $byte <= 0xDF) { + $bytesNeeded = 1; + $codePoint = $byte & 0x1F; + } elseif ($byte >= 0xE0 && $byte <= 0xEF) { + if ($byte === 0xE0) { + $lowerBoundary = 0xA0; + } elseif ($byte === 0xED) { + $upperBoundary = 0x9F; + } + + $bytesNeeded = 2; + $codePoint = $byte & 0xF; + } elseif ($byte >= 0xF0 && $byte <= 0xF4) { + if ($byte === 0xF0) { + $lowerBoundary = 0x90; + } elseif ($byte === 0xF4) { + $upperBoundary = 0x8F; + } + + $bytesNeeded = 3; + $codePoint = $byte & 0x7; + } else { + $codePoints[] = 0xFFFD; + } + + continue; + } + + if ($byte < $lowerBoundary || $byte > $upperBoundary) { + $codePoint = 0; + $bytesNeeded = 0; + $bytesSeen = 0; + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + --$i; + $codePoints[] = 0xFFFD; + + continue; + } + + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + $codePoint = ($codePoint << 6) | ($byte & 0x3F); + + if (++$bytesSeen !== $bytesNeeded) { + continue; + } + + $codePoints[] = $codePoint; + $codePoint = 0; + $bytesNeeded = 0; + $bytesSeen = 0; + } + + // String unexpectedly ended, so append a U+FFFD code point. + if ($bytesNeeded !== 0) { + $codePoints[] = 0xFFFD; + } + + return $codePoints; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/CodePointStatus.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/CodePointStatus.php new file mode 100755 index 0000000000..e48a511866 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/CodePointStatus.php @@ -0,0 +1,106 @@ + + */ + private static $mapped; + + /** + * @var array + */ + private static $ignored; + + /** + * @var array + */ + private static $deviation; + + /** + * @var array + */ + private static $disallowed; + + /** + * @var array + */ + private static $disallowed_STD3_mapped; + + /** + * @var array + */ + private static $disallowed_STD3_valid; + + /** + * @var bool + */ + private static $dataLoaded = false; + + /** + * @codeCoverageIgnore + */ + private function __construct() + { + } + + /** + * @return array{status: string, mapping?: string} + */ + public static function lookup(int $codePoint, bool $useSTD3ASCIIRules): array + { + if (!self::$dataLoaded) { + self::$dataLoaded = true; + self::$mapped = require self::RESOURCE_DIR . 'mapped.php'; + self::$ignored = require self::RESOURCE_DIR . 'ignored.php'; + self::$deviation = require self::RESOURCE_DIR . 'deviation.php'; + self::$disallowed = require self::RESOURCE_DIR . 'disallowed.php'; + self::$disallowed_STD3_mapped = require self::RESOURCE_DIR . 'disallowed_STD3_mapped.php'; + self::$disallowed_STD3_valid = require self::RESOURCE_DIR . 'disallowed_STD3_valid.php'; + } + + if (isset(self::$mapped[$codePoint])) { + return ['status' => 'mapped', 'mapping' => self::$mapped[$codePoint]]; + } + + if (isset(self::$ignored[$codePoint])) { + return ['status' => 'ignored']; + } + + if (isset(self::$deviation[$codePoint])) { + return ['status' => 'deviation', 'mapping' => self::$deviation[$codePoint]]; + } + + if (isset(self::$disallowed[$codePoint]) || DisallowedRanges::inRange($codePoint)) { + return ['status' => 'disallowed']; + } + + $isDisallowedMapped = isset(self::$disallowed_STD3_mapped[$codePoint]); + + if ($isDisallowedMapped || isset(self::$disallowed_STD3_valid[$codePoint])) { + $status = 'disallowed'; + + if (!$useSTD3ASCIIRules) { + $status = $isDisallowedMapped ? 'mapped' : 'valid'; + } + + if ($isDisallowedMapped) { + return ['status' => $status, 'mapping' => self::$disallowed_STD3_mapped[$codePoint]]; + } + + return ['status' => $status]; + } + + return ['status' => 'valid']; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/DomainInfo.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/DomainInfo.php new file mode 100644 index 0000000000..c873086b86 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/DomainInfo.php @@ -0,0 +1,82 @@ +bidiDomain = false; + $this->errors = 0; + $this->transitionalDifferent = false; + $this->validBidiDomain = true; + } + + public function addError(int $errors): void + { + $this->errors |= $errors; + } + + public function getErrors(): int + { + return $this->errors; + } + + /** + * A Bidi domain name is a domain name containing at least one character with Bidi_Class R, AL, or AN. + * + * @see https://www.unicode.org/reports/tr46/#Notation + * @see https://www.unicode.org/reports/tr9/#Bidirectional_Character_Types + */ + public function isBidiDomain(): bool + { + return $this->bidiDomain; + } + + public function isTransitionalDifferent(): bool + { + return $this->transitionalDifferent; + } + + public function isValidBidiDomain(): bool + { + return $this->validBidiDomain; + } + + public function setBidiDomain(): void + { + $this->bidiDomain = true; + } + + public function setInvalidBidiDomain(): void + { + $this->validBidiDomain = false; + } + + public function setTransitionalDifferent(): void + { + $this->transitionalDifferent = true; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/Idna.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/Idna.php new file mode 100644 index 0000000000..9d5153eb11 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/Idna.php @@ -0,0 +1,268 @@ + true, + 'CheckBidi' => true, + 'CheckJoiners' => true, + 'UseSTD3ASCIIRules' => true, + 'Transitional_Processing' => false, + ]; + private const DEFAULT_ASCII_OPTIONS = self::DEFAULT_UNICODE_OPTIONS + [ + 'VerifyDnsLength' => true, + ]; + + public const ERROR_EMPTY_LABEL = 1; + public const ERROR_LABEL_TOO_LONG = 2; + public const ERROR_DOMAIN_NAME_TOO_LONG = 4; + public const ERROR_LEADING_HYPHEN = 8; + public const ERROR_TRAILING_HYPHEN = 0x10; + public const ERROR_HYPHEN_3_4 = 0x20; + public const ERROR_LEADING_COMBINING_MARK = 0x40; + public const ERROR_DISALLOWED = 0x80; + public const ERROR_PUNYCODE = 0x100; + public const ERROR_LABEL_HAS_DOT = 0x200; + public const ERROR_INVALID_ACE_LABEL = 0x400; + public const ERROR_BIDI = 0x800; + public const ERROR_CONTEXTJ = 0x1000; + public const ERROR_CONTEXTO_PUNCTUATION = 0x2000; + public const ERROR_CONTEXTO_DIGITS = 0x4000; + + private const MAX_DOMAIN_SIZE = 253; + private const MAX_LABEL_SIZE = 63; + + /** + * @codeCoverageIgnore + */ + private function __construct() + { + } + + /** + * @see https://www.unicode.org/reports/tr46/#ProcessingStepMap + * + * @param array $options + */ + private static function mapCodePoints(string $domain, array $options, DomainInfo $info): string + { + $str = ''; + $useSTD3ASCIIRules = $options['UseSTD3ASCIIRules']; + $transitional = $options['Transitional_Processing']; + $errors = 0; + $transitionalDifferent = false; + + foreach (CodePoint::utf8Decode($domain) as $codePoint) { + $data = CodePointStatus::lookup($codePoint, $useSTD3ASCIIRules); + + switch ($data['status']) { + case 'disallowed': + $errors |= Idna::ERROR_DISALLOWED; + + // no break. + + case 'valid': + $str .= CodePoint::encode($codePoint); + + break; + + case 'ignored': + // Do nothing. + break; + + case 'mapped': + $str .= $data['mapping']; + + break; + + case 'deviation': + $transitionalDifferent = true; + $str .= ($transitional ? $data['mapping'] : CodePoint::encode($codePoint)); + + break; + } + } + + $info->addError($errors); + + if ($transitionalDifferent) { + $info->setTransitionalDifferent(); + } + + return $str; + } + + /** + * @see https://www.unicode.org/reports/tr46/#Processing + * + * @param array $options + * + * @return list + */ + private static function process(string $domain, array $options, DomainInfo $info): array + { + // If VerifyDnsLength is not set, we are doing ToUnicode otherwise we are doing ToASCII and + // we need to respect the VerifyDnsLength option. + $checkForEmptyLabels = !isset($options['VerifyDnsLength']) || $options['VerifyDnsLength']; + + if ($checkForEmptyLabels && $domain === '') { + $info->addError(self::ERROR_EMPTY_LABEL); + + return ['']; + } + + // Step 1. Map each code point in the domain name string + $domain = self::mapCodePoints($domain, $options, $info); + + // Step 2. Normalize the domain name string to Unicode Normalization Form C. + if (!Normalizer::isNormalized($domain, Normalizer::FORM_C)) { + $originalDomain = $domain; + $domain = Normalizer::normalize($domain, Normalizer::FORM_C); + + // This shouldn't be possible since the input string is run through the UTF-8 decoder + // when mapping the code points above, but lets account for it anyway. + if ($domain === false) { + $info->addError(self::ERROR_INVALID_ACE_LABEL); + $domain = $originalDomain; + } + + unset($originalDomain); + } + + // Step 3. Break the string into labels at U+002E (.) FULL STOP. + $labels = explode('.', $domain); + $lastLabelIndex = count($labels) - 1; + $validator = new LabelValidator($info); + + // Step 4. Convert and validate each label in the domain name string. + foreach ($labels as $i => $label) { + $validationOptions = $options; + + if (strncmp($label, 'xn--', 4) === 0) { + try { + $label = Punycode::decode(substr($label, 4)); + } catch (PunycodeException $e) { + $info->addError(self::ERROR_PUNYCODE); + + continue; + } + + $validationOptions['Transitional_Processing'] = false; + $labels[$i] = $label; + } + + $validator->validate($label, $validationOptions, $i > 0 && $i === $lastLabelIndex); + } + + if ($info->isBidiDomain() && !$info->isValidBidiDomain()) { + $info->addError(self::ERROR_BIDI); + } + + // Any input domain name string that does not record an error has been successfully + // processed according to this specification. Conversely, if an input domain_name string + // causes an error, then the processing of the input domain_name string fails. Determining + // what to do with error input is up to the caller, and not in the scope of this document. + return $labels; + } + + /** + * @see https://www.unicode.org/reports/tr46/#ToASCII + * + * @param array $options + */ + public static function toAscii(string $domain, array $options = []): IdnaResult + { + $options = array_merge(self::DEFAULT_ASCII_OPTIONS, $options); + $info = new DomainInfo(); + $labels = self::process($domain, $options, $info); + + foreach ($labels as $i => $label) { + // Only convert labels to punycode that contain non-ASCII code points + if (preg_match('/[^\x00-\x7F]/', $label) === 1) { + try { + $label = 'xn--' . Punycode::encode($label); + } catch (PunycodeException $e) { + $info->addError(self::ERROR_PUNYCODE); + } + + $labels[$i] = $label; + } + } + + if ($options['VerifyDnsLength']) { + self::validateDomainAndLabelLength($labels, $info); + } + + return new IdnaResult(implode('.', $labels), $info); + } + + /** + * @see https://www.unicode.org/reports/tr46/#ToUnicode + * + * @param array $options + */ + public static function toUnicode(string $domain, array $options = []): IdnaResult + { + // VerifyDnsLength is not a valid option for toUnicode, so remove it if it exists. + unset($options['VerifyDnsLength']); + $options = array_merge(self::DEFAULT_UNICODE_OPTIONS, $options); + $info = new DomainInfo(); + $labels = self::process($domain, $options, $info); + + return new IdnaResult(implode('.', $labels), $info); + } + + /** + * @param list $labels + */ + private static function validateDomainAndLabelLength(array $labels, DomainInfo $info): void + { + $maxDomainSize = self::MAX_DOMAIN_SIZE; + $length = count($labels); + $totalLength = $length - 1; + + // If the last label is empty and it is not the first label, then it is the root label. + // Increase the max size by 1, making it 254, to account for the root label's "." + // delimiter. This also means we don't need to check the last label's length for being too + // long. + if ($length > 1 && $labels[$length - 1] === '') { + ++$maxDomainSize; + --$length; + } + + for ($i = 0; $i < $length; ++$i) { + $bytes = strlen($labels[$i]); + $totalLength += $bytes; + + if ($bytes > self::MAX_LABEL_SIZE) { + $info->addError(self::ERROR_LABEL_TOO_LONG); + } + } + + if ($totalLength > $maxDomainSize) { + $info->addError(self::ERROR_DOMAIN_NAME_TOO_LONG); + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/IdnaResult.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/IdnaResult.php new file mode 100644 index 0000000000..71a3c81b6d --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/IdnaResult.php @@ -0,0 +1,55 @@ +domain = $domain; + $this->errors = $info->getErrors(); + $this->transitionalDifferent = $info->isTransitionalDifferent(); + } + + public function getDomain(): string + { + return $this->domain; + } + + public function getErrors(): int + { + return $this->errors; + } + + public function hasError(int $error): bool + { + return ($this->errors & $error) !== 0; + } + + public function hasErrors(): bool + { + return $this->errors !== 0; + } + + public function isTransitionalDifferent(): bool + { + return $this->transitionalDifferent; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/LabelValidator.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/LabelValidator.php new file mode 100644 index 0000000000..edacacd7dc --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/src/LabelValidator.php @@ -0,0 +1,237 @@ + + */ + protected static $virama = []; + + public function __construct(DomainInfo $info) + { + $this->info = $info; + + if (self::$virama === []) { + self::$virama = require __DIR__ . DS . '..' . DS . 'resources' . DS . 'virama.php'; + } + } + + /** + * @param list $codePoints + */ + protected function isValidContextJ(string $label, array $codePoints): bool + { + $offset = 0; + + foreach ($codePoints as $i => $codePoint) { + if ($codePoint !== 0x200C && $codePoint !== 0x200D) { + continue; + } + + $prev = $i - 1; + + if (!isset($codePoints[$prev])) { + return false; + } + + // If Canonical_Combining_Class(Before(cp)) .eq. Virama Then True; + if (isset(self::$virama[$codePoints[$prev]])) { + continue; + } + + // If RegExpMatch((Joining_Type:{L,D})(Joining_Type:T)*\u200C(Joining_Type:T)*(Joining_Type:{R,D})) Then + // True; + // Generated RegExp = ([Joining_Type:{L,D}][Joining_Type:T]*\u200C[Joining_Type:T]*)[Joining_Type:{R,D}] + if ( + $codePoint === 0x200C + && preg_match(Regex::ZWNJ, $label, $matches, PREG_OFFSET_CAPTURE, $offset) === 1 + ) { + $offset += strlen($matches[1][0]); + + continue; + } + + return false; + } + + return true; + } + + /** + * @see https://www.unicode.org/reports/tr46/#Validity_Criteria + * + * @param array $options + */ + public function validate(string $label, array $options, bool $canBeEmpty): void + { + if ($label === '') { + if ( + !$canBeEmpty + && (!isset($options['VerifyDnsLength']) || $options['VerifyDnsLength']) + ) { + $this->info->addError(Idna::ERROR_EMPTY_LABEL); + } + + return; + } + + $codePoints = CodePoint::utf8Decode($label); + + // Step 1. The label must be in Unicode Normalization Form C. + if (!Normalizer::isNormalized($label, Normalizer::FORM_C)) { + $this->info->addError(Idna::ERROR_INVALID_ACE_LABEL); + } + + if ($options['CheckHyphens']) { + // Step 2. If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character + // in both the thrid and fourth positions. + if (isset($codePoints[3]) && $codePoints[2] === 0x2D && $codePoints[3] === 0x2D) { + $this->info->addError(Idna::ERROR_HYPHEN_3_4); + } + + // Step 3. If CheckHyphens, the label must neither begin nor end with a U+002D + // HYPHEN-MINUS character. + if (strncmp($label, '-', 1) === 0) { + $this->info->addError(Idna::ERROR_LEADING_HYPHEN); + } + + if (substr_compare($label, '-', -1) === 0) { + $this->info->addError(Idna::ERROR_TRAILING_HYPHEN); + } + } + + // Step 4. The label must not contain a U+002E (.) FULL STOP. + if (strpos($label, '.') !== false) { + $this->info->addError(Idna::ERROR_LABEL_HAS_DOT); + } + + // Step 5. The label must not begin with a combining mark, that is: General_Category=Mark. + if (preg_match(Regex::COMBINING_MARK, $label, $matches) === 1) { + $this->info->addError(Idna::ERROR_LEADING_COMBINING_MARK); + } + + // Step 6. Each code point in the label must only have certain status values according to + // Section 5, IDNA Mapping Table: + $transitional = $options['Transitional_Processing']; + $useSTD3ASCIIRules = $options['UseSTD3ASCIIRules']; + + foreach ($codePoints as $codePoint) { + $data = CodePointStatus::lookup($codePoint, $useSTD3ASCIIRules); + $status = $data['status']; + + if ($status === 'valid' || (!$transitional && $status === 'deviation')) { + continue; + } + + $this->info->addError(Idna::ERROR_DISALLOWED); + + break; + } + + // Step 7. If CheckJoiners, the label must satisify the ContextJ rules from Appendix A, in + // The Unicode Code Points and Internationalized Domain Names for Applications (IDNA) + // [IDNA2008]. + if ($options['CheckJoiners'] && !$this->isValidContextJ($label, $codePoints)) { + $this->info->addError(Idna::ERROR_CONTEXTJ); + } + + // Step 8. If CheckBidi, and if the domain name is a Bidi domain name, then the label must + // satisfy all six of the numbered conditions in [IDNA2008] RFC 5893, Section 2. + if ( + $options['CheckBidi'] + && (!$this->info->isBidiDomain() || $this->info->isValidBidiDomain()) + ) { + $this->validateBidi($label); + } + } + + /** + * @see https://tools.ietf.org/html/rfc5893#section-2 + */ + protected function validateBidi(string $label): void + { + if (preg_match(Regex::RTL_LABEL, $label) === 1) { + $this->info->setBidiDomain(); + + // Step 1. The first character must be a character with Bidi property L, R, or AL. + // If it has the R or AL property, it is an RTL label + if (preg_match(Regex::BIDI_STEP_1_RTL, $label) !== 1) { + $this->info->setInvalidBidiDomain(); + + return; + } + + // Step 2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES, + // CS, ET, ON, BN, or NSM are allowed. + if (preg_match(Regex::BIDI_STEP_2, $label) === 1) { + $this->info->setInvalidBidiDomain(); + + return; + } + + // Step 3. In an RTL label, the end of the label must be a character with Bidi property + // R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM. + if (preg_match(Regex::BIDI_STEP_3, $label) !== 1) { + $this->info->setInvalidBidiDomain(); + + return; + } + + // Step 4. In an RTL label, if an EN is present, no AN may be present, and vice versa. + if ( + preg_match(Regex::BIDI_STEP_4_AN, $label) === 1 + && preg_match(Regex::BIDI_STEP_4_EN, $label) === 1 + ) { + $this->info->setInvalidBidiDomain(); + + return; + } + + return; + } + + // We are a LTR label + // Step 1. The first character must be a character with Bidi property L, R, or AL. + // If it has the L property, it is an LTR label. + if (preg_match(Regex::BIDI_STEP_1_LTR, $label) !== 1) { + $this->info->setInvalidBidiDomain(); + + return; + } + + // Step 5. In an LTR label, only characters with the Bidi properties L, EN, + // ES, CS, ET, ON, BN, or NSM are allowed. + if (preg_match(Regex::BIDI_STEP_5, $label) === 1) { + $this->info->setInvalidBidiDomain(); + + return; + } + + // Step 6.In an LTR label, the end of the label must be a character with Bidi property L or + // EN, followed by zero or more characters with Bidi property NSM. + if (preg_match(Regex::BIDI_STEP_6, $label) !== 1) { + $this->info->setInvalidBidiDomain(); + + return; + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/tests/IdnaV2Test.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/tests/IdnaV2Test.php new file mode 100755 index 0000000000..63c79d6ae7 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/tests/IdnaV2Test.php @@ -0,0 +1,258 @@ + true, + 'CheckBidi' => true, + 'CheckJoiners' => true, + 'UseSTD3ASCIIRules' => true, + 'Transitional_Processing' => false, + 'VerifyDnsLength' => true, + ]; + + /** + * @var array + */ + protected static $errorMap; + + public static function setUpBeforeClass(): void + { + if (isset(self::$errorMap)) { + return; + } + + $reflection = new ReflectionClass(Idna::class); + $errors = array_filter($reflection->getConstants(), static function (string $name): bool { + return strncmp($name, 'ERROR_', 6) === 0; + }, ARRAY_FILTER_USE_KEY); + self::$errorMap = array_flip($errors); + } + + /** + * @return array> + */ + public function getData(): array + { + return $this->loadTestData(Idna::UNICODE_VERSION); + } + + /** + * @dataProvider getData + */ + public function testToUnicode( + string $source, + string $toUnicode, + string $toUnicodeStatus, + string $toAsciiN, + string $toAsciiNStatus, + string $toAsciiT, + string $toAsciiTStatus + ): void { + [ + $toUnicode, + $toUnicodeStatus, + $toAsciiN, + $toAsciiNStatus, + $toAsciiT, + $toAsciiTStatus, + ] = $this->translate($source, $toUnicode, $toUnicodeStatus, $toAsciiN, $toAsciiNStatus, $toAsciiT, $toAsciiTStatus); + + $result = Idna::toUnicode($source, self::DEFAULT_OPTIONS); + self::assertSame($toUnicode, $result->getDomain()); + + if ($toUnicodeStatus === []) { + self::assertFalse($result->hasErrors(), sprintf( + 'Expected no errors, but found %s.', + $this->containedErrors($result->getErrors()) + )); + } else { + self::assertTrue($result->hasErrors(), sprintf( + 'Expected to find %s, but found %s', + $this->expectedErrors($toUnicodeStatus), + $this->containedErrors($result->getErrors()) + )); + } + } + + /** + * @dataProvider getData + */ + public function testToAsciiNonTransitional( + string $source, + string $toUnicode, + string $toUnicodeStatus, + string $toAsciiN, + string $toAsciiNStatus, + string $toAsciiT, + string $toAsciiTStatus + ): void { + [ + $toUnicode, + $toUnicodeStatus, + $toAsciiN, + $toAsciiNStatus, + $toAsciiT, + $toAsciiTStatus, + ] = $this->translate($source, $toUnicode, $toUnicodeStatus, $toAsciiN, $toAsciiNStatus, $toAsciiT, $toAsciiTStatus); + $result = Idna::toAscii($source, self::DEFAULT_OPTIONS); + + if ($toAsciiNStatus === []) { + self::assertSame($toAsciiN, $result->getDomain()); + self::assertFalse($result->hasErrors(), sprintf( + 'Expected no errors, but found %s.', + $this->containedErrors($result->getErrors()) + )); + } else { + self::assertTrue($result->hasErrors(), sprintf( + 'Expected %s, but found no errors.', + $this->expectedErrors($toAsciiTStatus) + )); + } + } + + /** + * @dataProvider getData + */ + public function testToAsciiTransitional( + string $source, + string $toUnicode, + string $toUnicodeStatus, + string $toAsciiN, + string $toAsciiNStatus, + string $toAsciiT, + string $toAsciiTStatus + ): void { + [ + $toUnicode, + $toUnicodeStatus, + $toAsciiN, + $toAsciiNStatus, + $toAsciiT, + $toAsciiTStatus, + ] = $this->translate($source, $toUnicode, $toUnicodeStatus, $toAsciiN, $toAsciiNStatus, $toAsciiT, $toAsciiTStatus); + $options = self::DEFAULT_OPTIONS; + $options['Transitional_Processing'] = true; + $result = Idna::toAscii($source, $options); + + // There is currently a bug in the test data, where it is expected that the following 2 + // source strings result in an empty string. However, due to the way the test files are setup + // it currently isn't possible to represent an empty string as an expected value. So, we + // skip these 2 problem tests. I have notified the Unicode Consortium about this and they + // have passed the information along to the spec editors. + if ($source === "\u{200C}" || $source === "\u{200D}") { + $toAsciiT = ''; + } + + if ($toAsciiTStatus === []) { + self::assertSame($toAsciiT, $result->getDomain()); + self::assertFalse($result->hasErrors(), sprintf( + 'Expected no errors, but found %s.', + $this->containedErrors($result->getErrors()) + )); + } else { + self::assertTrue($result->hasErrors(), sprintf( + 'Expected %s, but found no errors.', + $this->expectedErrors($toAsciiTStatus) + )); + } + } + + public function assertErrors(array $expectedErrors, int $errors): void + { + foreach ($expectedErrors as $errorCode) { + if (is_array($errorCode)) { + self::assertTrue($this->matchErrors($errorCode, $errors)); + + continue; + } + + self::assertTrue( + ($errors & $errorCode) !== 0, + sprintf( + 'Expected %s (%d), but only found %s.', + self::$errorMap[$errorCode], + $errorCode, + $this->containedErrors($errors) + ) + ); + } + } + + public function expectedErrors(array $expectedErrors): string + { + $message = ''; + + foreach ($expectedErrors as $i => $errorCode) { + if (is_array($errorCode)) { + $message .= '('; + + foreach ($errorCode as $j => $error) { + if ($j > 0) { + $message .= ' OR '; + } + + $message .= sprintf('%s (%d)', self::$errorMap[$error], $error); + } + + $message .= ')'; + } else { + if ($i > 0) { + $message .= ' | '; + } + + $message .= sprintf('%s (%d)', self::$errorMap[$errorCode], $errorCode); + } + } + + return $message; + } + + public function containedErrors(int $errors): string + { + if ($errors === 0) { + return 'no errors'; + } + + $out = ''; + $count = 0; + + foreach (self::$errorMap as $code => $name) { + if (($errors & $code) !== 0) { + if ($count++ > 0) { + $out .= ' | '; + } + + $out .= sprintf('%s (%d)', $name, $code); + } + } + + return $out; + } + + public function matchErrors(array $potentialErrors, int $errors): bool + { + foreach ($potentialErrors as $error) { + if (($errors & $error) !== 0) { + return true; + } + } + + return false; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/idna/tests/IdnaV2TestCase.php b/packages/playground/data-liberation/vendor-patched/rowbot/idna/tests/IdnaV2TestCase.php new file mode 100644 index 0000000000..714b39e22c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/idna/tests/IdnaV2TestCase.php @@ -0,0 +1,216 @@ + Idna::ERROR_DISALLOWED, + 'P4' => [ + Idna::ERROR_EMPTY_LABEL, + Idna::ERROR_DOMAIN_NAME_TOO_LONG, + Idna::ERROR_LABEL_TOO_LONG, + Idna::ERROR_PUNYCODE, + ], + 'V1' => Idna::ERROR_INVALID_ACE_LABEL, + 'V2' => Idna::ERROR_HYPHEN_3_4, + 'V3' => [Idna::ERROR_LEADING_HYPHEN, Idna::ERROR_TRAILING_HYPHEN], + 'V4' => Idna::ERROR_LABEL_HAS_DOT, + 'V5' => Idna::ERROR_LEADING_COMBINING_MARK, + 'V6' => Idna::ERROR_DISALLOWED, + // V7 and V8 are handled by C* and B* respectively. + 'A3' => Idna::ERROR_PUNYCODE, + 'A4_1' => Idna::ERROR_DOMAIN_NAME_TOO_LONG, + 'A4_2' => [Idna::ERROR_EMPTY_LABEL, Idna::ERROR_LABEL_TOO_LONG], + 'B1' => Idna::ERROR_BIDI, + 'B2' => Idna::ERROR_BIDI, + 'B3' => Idna::ERROR_BIDI, + 'B4' => Idna::ERROR_BIDI, + 'B5' => Idna::ERROR_BIDI, + 'B6' => Idna::ERROR_BIDI, + 'C1' => Idna::ERROR_CONTEXTJ, + 'C2' => Idna::ERROR_CONTEXTJ, + // ContextO isn't tested here. + // 'C3' => Idna::ERROR_CONTEXTO_PUNCTUATION, + // 'C4' => Idna::ERROR_CONTEXTO_PUNCTUATION, + // 'C5' => Idna::ERROR_CONTEXTO_PUNCTUATION, + // 'C6' => Idna::ERROR_CONTEXTO_PUNCTUATION, + // 'C7' => Idna::ERROR_CONTEXTO_PUNCTUATION, + // 'C8' => Idna::ERROR_CONTEXTO_DIGITS, + // 'C9' => Idna::ERROR_CONTEXTO_DIGITS, + 'X4_2' => Idna::ERROR_EMPTY_LABEL, + 'X3' => Idna::ERROR_EMPTY_LABEL, + ]; + + private const BASE_URI = 'https://www.unicode.org/Public/idna/'; + private const TEST_FILE = 'IdnaTestV2.txt'; + private const CACHE_TTL = 86400 * 7; // 7 DAYS + private const TEST_DATA_DIR = __DIR__ . DIRECTORY_SEPARATOR . 'data'; + + /** + * @return array> + */ + public function loadTestData(string $version): array + { + $cache = new FilesystemAdapter( + 'unicode-idna-test-data', + self::CACHE_TTL, + self::TEST_DATA_DIR + ); + $testData = $cache->getItem($version); + + if ($testData->isHit()) { + return $testData->get(); + } + + $client = new Client([ + 'base_uri' => self::BASE_URI + ]); + $response = $client->request('GET', $version . '/' . self::TEST_FILE); + + if ($response->getStatusCode() >= 400) { + throw new RuntimeException(sprintf( + 'Got status code %d trying to retrieve %s for Unicode version %s.', + $response->getStatusCode(), + self::TEST_FILE, + $version + )); + } + + $data = $this->processResponse($response); + $testData->set($data); + $cache->save($testData); + + return $data; + } + + /** + * @return array> + */ + private function processResponse(ResponseInterface $response): array + { + $output = []; + + foreach (explode("\n", (string) $response->getBody()) as $line) { + // Ignore empty lines and comments. + if ($line === '' || $line[0] === '#') { + continue; + } + + [$data] = explode('#', $line); + $columns = array_map('\trim', explode(';', $data)); + assert(count($columns) === 7); + $output[] = $columns; + } + + return $output; + } + + /** + * @param array> $inherit + * + * @return array> + */ + private function resolveErrorCodes(string $statusCodes, array $inherit, array $ignore): array + { + if ($statusCodes === '') { + return $inherit; + } + + if ($statusCodes === '[]') { + return []; + } + + $matchCount = preg_match_all('/[PVUABCX][0-9](?:_[0-9])?/', $statusCodes, $matches); + + if (preg_last_error() !== PREG_NO_ERROR) { + throw new RuntimeException(); + } + + if ($matchCount === 0) { + throw new RuntimeException(); + } + + $errors = []; + + foreach ($matches[0] as $match) { + if ($match[0] === 'U' || in_array($match, $ignore, true)) { + continue; + } + + if (!isset(self::ERROR_CODE_MAP[$match])) { + throw new RuntimeException(sprintf('Unhandled error code %s.', $match)); + } + + $errors[] = self::ERROR_CODE_MAP[$match]; + } + + return $errors; + } + + /** + * @return array{ + * 0: string, + * 1: array>, + * 2: string, + * 3: array>, + * 4: string, + * 5: array> + * } + */ + public function translate( + string $source, + string $toUnicode, + string $toUnicodeStatus, + string $toAsciiN, + string $toAsciiNStatus, + string $toAsciiT, + string $toAsciiTStatus, + array $ignore = [] + ): array { + if ($toUnicode === '') { + $toUnicode = $source; + } + + if ($toAsciiN === '') { + $toAsciiN = $toUnicode; + } + + if ($toAsciiT === '') { + $toAsciiT = $toAsciiN; + } + + $toUnicodeStatus = $this->resolveErrorCodes($toUnicodeStatus, [], $ignore); + $toAsciiNStatus = $this->resolveErrorCodes($toAsciiNStatus, $toUnicodeStatus, $ignore); + $toAsciiTStatus = $this->resolveErrorCodes($toAsciiTStatus, $toAsciiNStatus, $ignore); + + return [ + $toUnicode, + $toUnicodeStatus, + $toAsciiN, + $toAsciiNStatus, + $toAsciiT, + $toAsciiTStatus, + ]; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.gitattributes b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.gitattributes new file mode 100755 index 0000000000..dfe0770424 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.github/workflows/tests.yml b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.github/workflows/tests.yml new file mode 100644 index 0000000000..4fe8421858 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.github/workflows/tests.yml @@ -0,0 +1,176 @@ +name: Tests + +on: + pull_request: + push: + paths-ignore: + - '**.md' + +jobs: + tests: + name: Tests + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - 7.1 + - 7.2 + - 7.3 + - 7.4 + - 8.0 + - 8.1 + - 8.2 + - 8.3 + operating-system: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install + + - name: Run PHPStan + run: vendor/bin/phpstan analyse + + - name: Run PHPCS + run: vendor/bin/phpcs + + - name: Tests + run: vendor/bin/phpunit + + tests-unstable: + name: Test Unstable + runs-on: ${{ matrix.operating-system }} + continue-on-error: true + + strategy: + fail-fast: false + matrix: + php-version: + - nightly + operating-system: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --ignore-platform-reqs + + - name: Tests + run: vendor/bin/phpunit + + tests-code-coverage: + name: Code Coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: pcov + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install + + - name: Tests with code coverage + run: | + mkdir -p ./build/coverage/ + vendor/bin/phpunit --coverage-clover ./build/coverage/clover.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./build/coverage/clover.xml + + tests-jit: + name: Test JIT + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - 8.0 + operating-system: [ubuntu-latest] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: none + ini-values: opcache.enable_cli=1, opcache.jit=1255, opcache.jit_buffer_size=64M + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install + + - name: Tests + run: vendor/bin/phpunit diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.gitignore b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.gitignore new file mode 100755 index 0000000000..7d6e0d5aa2 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.gitignore @@ -0,0 +1,6 @@ +composer.phar +composer.lock +/vendor/ +/.vscode/ +/build/ +.phpunit.result.cache diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.phpcs.xml b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.phpcs.xml new file mode 100644 index 0000000000..254d476513 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/.phpcs.xml @@ -0,0 +1,9 @@ + + + src + + + + + + diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/CHANGELOG.md b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/CHANGELOG.md new file mode 100755 index 0000000000..4404bea1be --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +## [Unreleased] + +## [1.0.4] - 2024-05-02 + +### Fixed + +- Fix implicit nullable deprecation warning for PHP 8.4 + +## [1.0.3] - 2022-01-10 + +### Changed + +- Update PHPstan to 1.0 +- Switched to GitHub Actions + +## [1.0.2] - 2020-07-15 + +### Added + +- Minor performance improvements + +## [1.0.1] - 2020-06-16 + +### Fixed + +- When calling `Punycode::decode()`, the case flags array would fail to populate when given an empty array. + +## [1.0.0] - 2020-06-09 + +- Initial release diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/LICENSE b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/LICENSE new file mode 100755 index 0000000000..06a800ebea --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Trevor Rowbotham + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/README.md b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/README.md new file mode 100644 index 0000000000..5f06c24c1a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/README.md @@ -0,0 +1,83 @@ +# Punycode + +[![Software License](https://img.shields.io/github/license/TRowbotham/punycode?style=flat-square)](https://github.com/TRowbotham/punycode/blob/master/LICENSE) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/TRowbotham/punycode/Tests?style=flat-square)](https://travis-ci.com/github/TRowbotham/punycode) +[![Code Coverage](https://img.shields.io/codecov/c/github/TRowbotham/punycode/master?style=flat-square)](https://codecov.io/gh/TRowbotham/punycode) +[![PHP Version](https://img.shields.io/packagist/v/rowbot/punycode?style=flat-square)](https://packagist.org/packages/rowbot/punycode) +[![Total Downloads](https://img.shields.io/packagist/dt/rowbot/punycode?style=flat-square)](https://packagist.org/packages/rowbot/punycode) + +An implementation of RFC 3492 Punycode in PHP, based on the sample implementation in [Appendix C](https://tools.ietf.org/html/rfc3492#appendix-C). +This is NOT a substitue for `idn_to_ascii` and `idn_to_utf8`. + +## Requirements + +- PHP 7.1+ + +## Installation + +```bash +composer require rowbot/punycode +``` + +## API + +### Punycode::decode(string $input, int $outLength = null, array &$caseFlags = []) + +The `Punycode::decode()` method takes an ASCII encoded string and decodes it to a UTF-8 encoded +string. Optionally, the second parameter can be specified to place a limit on the size of the +returned string. + +#### Parameters + +- `$input` - An ASCII encoded punycode string to convert to a UTF-8 encoded string. +- `$outLength` - A positive integer representing the maximum length, in code points, of the resulting + output string. Defaults to 2,147,483,647. +- `$caseFlags` - An array, which will have the case flag of each character inserted into it. + +#### Throws + +- `\Rowbot\Punycode\Exception\OutputSizeExceededException` - If the size of the output string + exceeds the maximum size specified. +- `\Rowbot\Punycode\Exception\OverflowException` - If integer overflow occurs. +- `\Rowbot\Punycode\Exception\InvalidInputException` - If input contains non-ASCII bytes or mapping + a code point to a digit fails. + +```php +use Rowbot\Punycode\Punycode; + +try { + echo Punycode::decode('Hello-Another-Way--fc4qua05auwb3674vfr0b'); // Hello-Another-Way-それぞれの場所 +} catch (\Rowbot\Punycode\Exception\PunycodeException $e) { + echo 'An error occured!'; +} +``` + +### Punycode::encode(string $input, int $outLength = null, array $caseFlags = []) + +The `Punycode::encode()` method takes a UTF-8 encoded string and converts it into an ASCII encoded +punycode string. Optionally, the second parameter can be specified to place a limit on the size of +the returned string. + +#### Parameters + +- `$input` - A UTF-8 encoded string to convert to punycode. +- `$outLength` - A positive integer representing the maximum length, in code points, of the resulting + output string. Defaults to 2,147,483,647. +- `$caseFlags` - An array of bools where true indicates that the character should be uppercase and + false indicates that it should be lowercase. This only affects ASCII characters `[a-zA-Z]`. + +#### Throws + +- `\Rowbot\Punycode\Exception\OutputSizeExceededException` - If the size of the output string + exceeds the maximum size specified. +- `\Rowbot\Punycode\Exception\OverflowException` - If integer overflow occurs. + +```php +use Rowbot\Punycode\Punycode; + +try { + echo Punycode::encode('Hello-Another-Way-それぞれの場所'); // Hello-Another-Way--fc4qua05auwb3674vfr0b +} catch (\Rowbot\Punycode\Exception\PunycodeException $e) { + echo 'An error occured!'; +} +``` diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/composer.json b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/composer.json new file mode 100755 index 0000000000..33704084c5 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/composer.json @@ -0,0 +1,42 @@ +{ + "name": "rowbot/punycode", + "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA).", + "keywords": [ + "punycode", + "rfc3492", + "rfc-3492" + ], + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Trevor Rowbotham", + "role": "Developer", + "homepage": "https://trowbotham.com" + } + ], + "config": { + "sort-packages": true + }, + "autoload": { + "psr-4": { + "Rowbot\\Punycode\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Rowbot\\Punycode\\Test\\": "tests/" + } + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "ext-mbstring": "*", + "phpstan/phpstan": "^1.2", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.5.1" + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/phpstan.neon b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/phpstan.neon new file mode 100644 index 0000000000..f6970f6526 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/phpstan.neon @@ -0,0 +1,8 @@ +includes: + - vendor/phpstan/phpstan-strict-rules/rules.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon + +parameters: + level: max + paths: + - src diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/phpunit.xml b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/phpunit.xml new file mode 100644 index 0000000000..e0ce8df5ba --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/phpunit.xml @@ -0,0 +1,20 @@ + + + + tests + + + + + src + + + diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/src/Exception/InvalidInputException.php b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/src/Exception/InvalidInputException.php new file mode 100644 index 0000000000..29d3a3302f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/src/Exception/InvalidInputException.php @@ -0,0 +1,9 @@ + + */ + private static $basicToDigit = [ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, + + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + ]; + + /** + * @codeCoverageIgnore + */ + private function __construct() + { + } + + /** + * @see https://tools.ietf.org/html/rfc3492#section-6.1 + */ + private static function adaptBias(int $delta, int $numPoints, bool $firstTime): int + { + // xxx >> 1 is a faster way of doing intdiv(xxx, 2) + $delta = $firstTime ? intdiv($delta, self::DAMP) : $delta >> 1; + $delta += intdiv($delta, $numPoints); + $k = 0; + + while ($delta > ((self::BASE - self::TMIN) * self::TMAX) >> 1) { + $delta = intdiv($delta, self::BASE - self::TMIN); + $k += self::BASE; + } + + return $k + intdiv((self::BASE - self::TMIN + 1) * $delta, $delta + self::SKEW); + } + + /** + * @see https://tools.ietf.org/html/rfc3492#section-6.2 + * + * @param array $caseFlags + */ + public static function decode(string $input, ?int $outLength = null, array &$caseFlags = []): string + { + $n = self::INITIAL_N; + $out = 0; + $i = 0; + $maxOut = $outLength ?? self::MAX_INT; + $bias = self::INITIAL_BIAS; + $lastDelimIndex = strrpos($input, self::DELIMITER); + $b = $lastDelimIndex === false ? 0 : $lastDelimIndex; + $inputLength = strlen($input); + $output = []; + $wantsCaseFlags = func_num_args() > 2; + + if ($b > $maxOut) { + throw new OutputSizeExceededException(); + } + + $bytes = array_map('ord', str_split($input)); + + for ($j = 0; $j < $b; ++$j) { + if ($bytes[$j] > 0x7F) { + throw new InvalidInputException(); + } + + if ($wantsCaseFlags) { + $caseFlags[$out] = self::flagged($bytes[$j]); + } + + $output[$out++] = $input[$j]; + } + + if ($b > 0) { + $b += 1; + } + + for ($in = $b; $in < $inputLength; ++$out) { + $oldi = $i; + $w = 1; + + for ($k = self::BASE; /* no condition */; $k += self::BASE) { + if ($in >= $inputLength) { + throw new InvalidInputException(); + } + + $digit = self::$basicToDigit[$bytes[$in++] & 0xFF]; + + if ($digit < 0) { + throw new InvalidInputException(); + } + + if ($digit > intdiv(self::MAX_INT - $i, $w)) { + throw new OverflowException(); + } + + $i += $digit * $w; + + if ($k <= $bias) { + $t = self::TMIN; + } elseif ($k >= $bias + self::TMAX) { + $t = self::TMAX; + } else { + $t = $k - $bias; + } + + if ($digit < $t) { + break; + } + + $baseMinusT = self::BASE - $t; + + if ($w > intdiv(self::MAX_INT, $baseMinusT)) { + throw new OverflowException(); + } + + $w *= $baseMinusT; + } + + $outPlusOne = $out + 1; + $bias = self::adaptBias($i - $oldi, $outPlusOne, $oldi === 0); + + if (intdiv($i, $outPlusOne) > self::MAX_INT - $n) { + throw new OverflowException(); + } + + $n += intdiv($i, $outPlusOne); + $i %= $outPlusOne; + + if ($out >= $maxOut) { + throw new OutputSizeExceededException(); + } + + if ($wantsCaseFlags) { + array_splice($caseFlags, $i, 0, [self::flagged($bytes[$n - 1])]); + } + + array_splice($output, $i++, 0, [self::encodeCodePoint($n)]); + } + + return implode('', $output); + } + + /** + * @see https://tools.ietf.org/html/rfc3492#section-6.3 + * + * @param array $caseFlags + */ + public static function encode(string $input, ?int $outLength = null, array $caseFlags = []): string + { + $n = self::INITIAL_N; + $delta = 0; + $out = 0; + $maxOut = $outLength ?? self::MAX_INT; + $bias = self::INITIAL_BIAS; + $inputLength = 0; + $output = ''; + $codePoints = self::utf8Decode($input); + + foreach ($codePoints as $j => $codePoint) { + ++$inputLength; + + if ($codePoint < 0x80) { + if ($maxOut - $out < 2) { + throw new OutputSizeExceededException(); + } + + $output .= isset($caseFlags[$j]) + ? self::encodeBasic($codePoint, $caseFlags[$j]) + : chr($codePoint); + ++$out; + } + } + + $h = $out; + $b = $out; + + if ($b > 0) { + $output .= self::DELIMITER; + ++$out; + } + + while ($h < $inputLength) { + $m = self::MAX_INT; + + foreach ($codePoints as $codePoint) { + if ($codePoint >= $n && $codePoint < $m) { + $m = $codePoint; + } + } + + if ($m - $n > intdiv(self::MAX_INT - $delta, $h + 1)) { + throw new OverflowException(); + } + + $delta += ($m - $n) * ($h + 1); + $n = $m; + + foreach ($codePoints as $j => $codePoint) { + if ($codePoint < $n && ++$delta === 0) { + throw new OverflowException(); + } elseif ($codePoint === $n) { + $q = $delta; + + for ($k = self::BASE; /* no condition */; $k += self::BASE) { + if ($out >= $maxOut) { + throw new OutputSizeExceededException(); + } + + if ($k <= $bias) { + $t = self::TMIN; + } elseif ($k >= $bias + self::TMAX) { + $t = self::TMAX; + } else { + $t = $k - $bias; + } + + if ($q < $t) { + break; + } + + $qMinusT = $q - $t; + $baseMinusT = self::BASE - $t; + $output .= self::encodeDigit($t + ($qMinusT) % ($baseMinusT), false); + ++$out; + $q = intdiv($qMinusT, $baseMinusT); + } + + $output .= self::encodeDigit($q, $caseFlags[$j] ?? false); + ++$out; + $bias = self::adaptBias($delta, $h + 1, $h === $b); + $delta = 0; + ++$h; + } + } + + ++$delta; + ++$n; + } + + return $output; + } + + private static function encodeBasic(int $codePoint, bool $flag): string + { + $codePoint -= ($codePoint - 97 < 26 ? 1 : 0) << 5; + + return chr($codePoint + ((!$flag && ($codePoint - 65 < 26) ? 1 : 0) << 5)); + } + + /** + * Takes a Unicode code point and encodes it. The return behavior is undefined if the given + * code point is outside the range 0..10FFFF. + * + * @see https://encoding.spec.whatwg.org/#utf-8-encoder + */ + private static function encodeCodePoint(int $codePoint): string + { + if ($codePoint >= 0x00 && $codePoint <= 0x7F) { + return chr($codePoint); + } + + $count = 0; + $offset = 0; + + if ($codePoint >= 0x0080 && $codePoint <= 0x07FF) { + $count = 1; + $offset = 0xC0; + } elseif ($codePoint >= 0x0800 && $codePoint <= 0xFFFF) { + $count = 2; + $offset = 0xE0; + } elseif ($codePoint >= 0x10000 && $codePoint <= 0x10FFFF) { + $count = 3; + $offset = 0xF0; + } + + $bytes = chr(($codePoint >> (6 * $count)) + $offset); + + while ($count > 0) { + $temp = $codePoint >> (6 * ($count - 1)); + $bytes .= chr(0x80 | ($temp & 0x3F)); + --$count; + } + + return $bytes; + } + + private static function encodeDigit(int $d, bool $flag): string + { + return chr($d + 22 + 75 * ($d < 26 ? 1 : 0) - (($flag ? 1 : 0) << 5)); + } + + private static function flagged(int $codePoint): bool + { + return $codePoint - 65 < 26; + } + + /** + * Takes a UTF-8 encoded string and converts it into a series of integer code points. Any + * invalid byte sequences will be replaced by a U+FFFD replacement code point. + * + * @see https://encoding.spec.whatwg.org/#utf-8-decoder + * + * @return array + */ + private static function utf8Decode(string $input): array + { + $bytesSeen = 0; + $bytesNeeded = 0; + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + $codePoint = 0; + $codePoints = []; + $length = strlen($input); + + for ($i = 0; $i < $length; ++$i) { + $byte = ord($input[$i]); + + if ($bytesNeeded === 0) { + if ($byte <= 0x7F) { + $codePoints[] = $byte; + + continue; + } + + if ($byte >= 0xC2 && $byte <= 0xDF) { + $bytesNeeded = 1; + $codePoint = $byte & 0x1F; + } elseif ($byte >= 0xE0 && $byte <= 0xEF) { + if ($byte === 0xE0) { + $lowerBoundary = 0xA0; + } elseif ($byte === 0xED) { + $upperBoundary = 0x9F; + } + + $bytesNeeded = 2; + $codePoint = $byte & 0xF; + } elseif ($byte >= 0xF0 && $byte <= 0xF4) { + if ($byte === 0xF0) { + $lowerBoundary = 0x90; + } elseif ($byte === 0xF4) { + $upperBoundary = 0x8F; + } + + $bytesNeeded = 3; + $codePoint = $byte & 0x7; + } else { + $codePoints[] = 0xFFFD; + } + + continue; + } + + if ($byte < $lowerBoundary || $byte > $upperBoundary) { + $codePoint = 0; + $bytesNeeded = 0; + $bytesSeen = 0; + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + --$i; + $codePoints[] = 0xFFFD; + + continue; + } + + $lowerBoundary = 0x80; + $upperBoundary = 0xBF; + $codePoint = ($codePoint << 6) | ($byte & 0x3F); + + if (++$bytesSeen !== $bytesNeeded) { + continue; + } + + $codePoints[] = $codePoint; + $codePoint = 0; + $bytesNeeded = 0; + $bytesSeen = 0; + } + + // String unexpectedly ended, so append a U+FFFD code point. + if ($bytesNeeded !== 0) { + $codePoints[] = 0xFFFD; + } + + return $codePoints; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/punycode/tests/PunycodeTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/tests/PunycodeTest.php new file mode 100644 index 0000000000..b37ebcf1f7 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/punycode/tests/PunycodeTest.php @@ -0,0 +1,211 @@ +> + */ + public static function punycodeProvider(): array + { + return [ + [ + 'ليهمابتكلموشعربي؟', + 'egbpdaj6bu4bxfgehfvwxn', + 'Arabic (Egyptian)' + ], + [ + '他们为什么不说中文', + 'ihqwcrb4cv8a8dqg056pqjye', + 'Chinese (simplified)' + ], + [ + '他們爲什麽不說中文', + 'ihqwctvzc91f659drss3x8bo0yb', + 'Chinese (traditional)' + ], + [ + 'Pročprostěnemluvíčesky', + 'Proprostnemluvesky-uyb24dma41a', + 'Czech' + ], + [ + 'למההםפשוטלאמדבריםעברית', + '4dbcagdahymbxekheh6e0a7fei0b', + 'Hebrew' + ], + [ + 'यहलोगहिन्दीक्योंनहींबोलसकतेहैं', + 'i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd', + 'Hindi (Devanagari)' + ], + [ + 'なぜみんな日本語を話してくれないのか', + 'n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa', + 'Japanese (kanji and hiragana)' + ], + [ + '세계의모든사람들이한국어를이해한다면얼마나좋을까', + '989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c', + 'Korean (Hangul syllables)' + ], + [ + 'почемужеонинеговорятпорусски', + 'b1abfaaepdrnnbgefbadotcwatmq2g4l', + 'Russian (Cyrillic)' + ], + [ + 'PorquénopuedensimplementehablarenEspañol', + 'PorqunopuedensimplementehablarenEspaol-fmd56a', + 'Spanish' + ], + [ + 'TạisaohọkhôngthểchỉnóitiếngViệt', + 'TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g', + 'Vietnamese' + ], + [ + '3年B組金八先生', + '3B-ww4c5e180e575a65lsy2b', + '' + ], + [ + '安室奈美恵-with-SUPER-MONKEYS', + '-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n', + '' + ], + [ + 'Hello-Another-Way-それぞれの場所', + 'Hello-Another-Way--fc4qua05auwb3674vfr0b', + '' + ], + [ + 'ひとつ屋根の下2', + '2-u9tlzr9756bt3uc0v', + '' + ], + [ + 'MajiでKoiする5秒前', + 'MajiKoi5-783gue6qz075azm5e', + '' + ], + [ + 'パフィーdeルンバ', + 'de-jg4avhby1noc0d', + '' + ], + [ + 'そのスピードで', + 'd9juau41awczczp', + '' + ], + [ + '-> $1.00 <-', + '-> $1.00 <--', + '' + ], + ]; + } + + /** + * @dataProvider punycodeProvider + */ + public function testEncode(string $decoded, string $encoded, string $comment): void + { + self::assertSame($encoded, Punycode::encode($decoded), $comment); + } + + /** + * @dataProvider punycodeProvider + */ + public function testDecode(string $decoded, string $encoded, string $comment): void + { + self::assertSame($decoded, Punycode::decode($encoded), $comment); + } + + public function encodeMaxLengthDataProvider(): array + { + return array_filter($this->punycodeProvider(), static function (array $data): bool { + return mb_strlen($data[1], 'utf-8') > 10; + }); + } + + /** + * @dataProvider encodeMaxLengthDataProvider + */ + public function testEncodeMaxLength(string $decoded, string $encoded, string $comment): void + { + $this->expectException(OutputSizeExceededException::class); + Punycode::encode($decoded, 10); + } + + /** + * @return array> + */ + public function decodeMaxLengthDataProvider(): array + { + return array_filter($this->punycodeProvider(), static function (array $data): bool { + return mb_strlen($data[0], 'utf-8') > 10; + }); + } + + /** + * @dataProvider decodeMaxLengthDataProvider + */ + public function testDecodeMaxLength(string $decoded, string $encoded, string $comment): void + { + $this->expectException(OutputSizeExceededException::class); + Punycode::decode($encoded, 10); + } + + /** + * @return array> + */ + public function nonAsciiDataProvider(): array + { + return array_filter($this->punycodeProvider(), static function (array $data): bool { + return preg_match('/[^\x00-\x7F]/', $data[0]) === 1; + }) + [ + ["abc\u{FFFD}", '', 'Mixed ASCII and non-ASCII'] + ]; + } + + /** + * @dataProvider nonAsciiDataProvider + */ + public function testDecodeOnlyAcceptsAscii(string $decoded, string $encoded, string $comment): void + { + $this->expectException(InvalidInputException::class); + Punycode::decode($decoded); + } + + public function testCaseFlags(): void + { + $c = [true, true, true]; + $e = Punycode::encode('abc', null, $c); + self::assertSame('ABC-', $e); + + $c = [false, false, false]; + $d = Punycode::decode($e, null, $c); + self::assertSame([true, true, true], $c); + self::assertSame('ABC', $d); + + $c = []; + $d = Punycode::decode($e, null, $c); + self::assertSame([true, true, true], $c); + self::assertSame('ABC', $d); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/.github/workflows/coverage.yml b/packages/playground/data-liberation/vendor-patched/rowbot/url/.github/workflows/coverage.yml new file mode 100644 index 0000000000..f43d6a74f2 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/.github/workflows/coverage.yml @@ -0,0 +1,57 @@ +name: Code Coverage +on: + push: + paths-ignore: + - '**.md' + + pull_request: + paths-ignore: + - '**.md' + +jobs: + coverage: + name: PHP ${{ matrix.php }} on ${{ matrix.os }} ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + container: shivammathur/node:latest-${{ matrix.arch }} + defaults: + run: + shell: bash + strategy: + fail-fast: true + matrix: + os: + - ubuntu-latest + arch: + - amd64 + - i386 + php: + - 8.1 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mbstring, opcache, pcre, gmp + coverage: pcov + env: + fail-fast: true + + - name: Install dependencies + run: composer update --no-interaction + + - name: Generate Coverage + run: | + mkdir -p ./build/coverage/ + vendor/bin/phpunit --coverage-clover ./build/coverage/clover.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + if: ${{ !env.ACT }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./build/coverage/clover.xml + name: PHP ${{ matrix.php }} on ${{ matrix.os }} ${{ matrix.arch }} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/.github/workflows/tests.yml b/packages/playground/data-liberation/vendor-patched/rowbot/url/.github/workflows/tests.yml new file mode 100644 index 0000000000..c219ba6090 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/.github/workflows/tests.yml @@ -0,0 +1,70 @@ +name: Test URL +on: + push: + paths-ignore: + - '**.md' + + pull_request: + paths-ignore: + - '**.md' + +jobs: + test: + name: PHP ${{ matrix.php }} ${{ matrix.php-jit == 'on' && 'with' || 'without' }} JIT on ${{ matrix.os }} ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + container: shivammathur/node:latest-${{ matrix.arch }} + continue-on-error: ${{ matrix.php == 'nightly' }} + defaults: + run: + shell: bash + strategy: + fail-fast: true + matrix: + os: + - ubuntu-latest + arch: + - amd64 + - i386 + php: + - 8.1 + - 8.2 + - 8.3 + php-jit: + - on + - off + include: + - php: nightly + os: ubuntu-latest + arch: amd64 + php-jit: on + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mbstring, opcache, pcre, gmp + ini-values: zend.assertions=1, opcache.enable_cli=1, opcache.memory_consumption=256, opcache.jit=${{ matrix.php-jit == 'on' && 1255 || 'disable' }}, opcache.jit_buffer_size=${{ matrix.php-jit == 'on' && '256M' || '0' }} + coverage: none + env: + fail-fast: true + + - name: Install dependencies + run: | + if [[ ${{ matrix.php == 'nightly' }} ]]; then + composer update --no-interaction --ignore-platform-reqs + else + composer update --no-interaction + fi + + - name: Run PHPCS + run: vendor/bin/phpcs + + - name: Run PHPStan + run: vendor/bin/phpstan + + - name: Run Tests + run: vendor/bin/phpunit diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/.gitignore b/packages/playground/data-liberation/vendor-patched/rowbot/url/.gitignore new file mode 100644 index 0000000000..a8f6aa1cd8 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/.gitignore @@ -0,0 +1,6 @@ +/vendor/ +/composer.lock +.vscode +/build/ +/tests/WhatWg/data/ +/.phpunit.cache/ diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/.phpcs.xml b/packages/playground/data-liberation/vendor-patched/rowbot/url/.phpcs.xml new file mode 100644 index 0000000000..3a17c01e63 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/.phpcs.xml @@ -0,0 +1,199 @@ + + + src + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/CHANGELOG.md b/packages/playground/data-liberation/vendor-patched/rowbot/url/CHANGELOG.md new file mode 100644 index 0000000000..060100a230 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/CHANGELOG.md @@ -0,0 +1,302 @@ +# Changelog + +## [Unreleased] + +## [4.0.0] - 2024-06-20 + +### Added + +- Performance improvements +- Validation error logging +- `\Rowbot\URL\URL` and `\Rowbot\URL\URLSearchParams` now implement `\Stringable` +- `\Rowbot\URL\URLSearchParams` constructor now has a native typehint of `array|object|string` +- `\Rowbot\URL\URLSearchParams` now has a `size` getter per [whatwg/url#734](https://github.com/whatwg/url/pull/734) + - `\Rowbot\URL\URLSearchParams` now also implements `\Countable` +- Added `\Rowbot\URL\URL::canParse()`, which returns a boolean if parsing was successful, per [whatwg/url#713](https://github.com/whatwg/url/issues/713) and [whatwg/url#763](https://github.com/whatwg/url/pull/763) +- Add value parameter to `\Rowbot\URL\URLSearchParams::has()` and `\Rowbot\URL\URLSearchParams::delete()` per [whatwg/url#335](https://github.com/whatwg/url/issues/335) and [whatwg/url#735](https://github.com/whatwg/url/pull/735) +- Added `\Rowbot\URL\URL::parse()`, which will return the parsed URL or null on failure, avoiding needing a `try` statement per [whatwg/url#372](https://github.com/whatwg/url/issues/372) and [whatwg/url#825](https://github.com/whatwg/url/pull/825) + +### Changed + +- Bump minimum PHP version to 8.1 +- Lone surrogate code points are no longer treated differently from other invalid code points +- `\Rowbot\URL\String\Exception\UConverterException` has been renamed to `\Rowbot\URL\String\Exception\EncodingException` +- Moved 32-bit tests to GitHub Actions from Appveyor +- `\Rowbot\URL\URL`'s $url and $base parameters now also accept `\Stringable` +- `\Rowbot\URL\URLSearchParams::current()` now returns `null` when the iterator is invalid instead of `['', '']`, which better matches the expected behavior +- Ensure opaque paths can round trip from the API [whatwg/url#651](https://github.com/whatwg/url/issues/651) [whatwg/url#728](https://github.com/whatwg/url/pull/728) +- Blob URL's with an inner non-http(s) URL now returns an opaque origin per [whatwg/url#770](https://github.com/whatwg/url/issues/770) and [whatwg/url#771](https://github.com/whatwg/url/pull/771) + +### Removed + +- Removed `\Rowbot\URL\Exception\JsonException` in favor of `\JsonException` + +### Internals + +- Removed class `\Rowbot\URL\String\IDLString` +- Added method `\Rowbot\URL\String\Utf8String::scrub()` +- Added method `\Rowbot\URL\String\Utf8String::fromUnsafe()` +- Moved method `\Rowbot\URL\String\AbstractUSVString::transcode()` to `\Rowbot\URL\String\Utf8String` +- Removed method `\Rowbot\URL\String\Exception\RegexException::getNameFromLastCode()` +- All objects with a `__toString()` method now implement `\Stringable` +- Added native union typehints where possible +- `\Rowbot\URL\Origin` is now an interface + - Added class `\Rowbot\URL\Component\TupleOrigin` which implements `\Rowbot\URL\Origin` + - Added class `\Rowbot\URL\Component\OpaqueOrigin` which implements `\Rowbot\URL\Origin` +- `\Rowbot\URL\Component\PathListInterface` renamed to `\Rowbot\URL\PathInterface` + - Added class `\Rowbot\URL\Component\OpaquePath` which implements `\Rowbot\URL\PathInterface` +- `\Rowbot\URL\Component\Path` was renamed to `\Rowbot\URL\Component\PathSegment` +- `\Rowbot\URL\State\CannotBeABaseUrlPathState` was renamed to `\Rowbot\URL\State\OpaquePathState` + - Removed property `\Rowbot\URL\URLRecord::$cannotBeABaseUrl` +- Adopted the specs new percent encoding model + - Removed const `\Rowbot\URL\String\CodePoint::C0_CONTROL_PERCENT_ENCODE_SET` + - Removed const `\Rowbot\URL\String\CodePoint::FRAGMENT_PERCENT_ENCODE_SET` + - Removed const `\Rowbot\URL\String\CodePoint::PATH_PERCENT_ENCODE_SET` + - Removed const `\Rowbot\URL\String\CodePoint::USERINFO_PERCENT_ENCODE_SET` + - Removed method `\Rowbot\URL\String\CodePoint::utf8PercentEncode()` + - Added enum `\Rowbot\URL\String\EncodeSet` + - Added class `\Rowbot\URL\String\PercentEncoder` + - `\Rowbot\URL\Component\Host\HostParser` methods are no longer static +- `\Rowbot\URL\String\StringListInterface` extends `\IteratorAggregate` instead of `\Iterator` +- `\Rowbot\URL\Component\QueryList` now implements `\Countable` +- Added enum `\Rowbot\URL\State\StatusCode` + - `\Rowbot\URL\State\State` now has a return type of `\Rowbot\URL\State\StatusCode` instead of `int` + - Removed const `\Rowbot\URL\State\State::RETURN_OK` + - Removed const `\Rowbot\URL\State\State::RETURN_CONTINUE` + - Removed const `\Rowbot\URL\State\State::RETURN_BREAK` + - Removed const `\Rowbot\URL\State\State::RETURN_FAILURE` + +## [3.1.7] - 2022-08-26 + +### Fixed + +- Detection of windows drive letters + +## [3.1.6] - 2022-08-16 + +### Changed + +- Forbid C0 control code points and U+007F DEL code point in non-opaque domain names per [whatwg/url#685](https://github.com/whatwg/url/pull/685) + +## Fixed + +- Fix parsing IPv4-mapped IPv6 addresses due to wrong logical condition + +## [3.1.5] - 2022-01-10 + +### Changed + +- Upgraded to PHPStan 1.0 + +### Fixed + +- Initialization of `URLSearchParams` using an object now behaves the same as when using and iterator or string + +## [3.1.4] - 2021-07-27 + +### Added + +- Support for PHP 8.1 + +### Changed + +- Make the `URL::$hostname` setter do nothing if the host name contains a ":" per [whatwg/url#601](https://github.com/whatwg/url/issues/601) [whatwg/url#604](https://github.com/whatwg/url/pull/604) +- Reject non-IPv4 hostnames that end in numbers per [whatwg/url#560](https://github.com/whatwg/url/issues/560) [whatwg/url#619](https://github.com/whatwg/url/pull/619) + +### Fixed + +- Prevent the pathname setter from erasing the path of path-only URLs [whatwg/url#581](https://github.com/whatwg/url/issues/581) [whatwg/url#582](https://github.com/whatwg/url/pull/582) + +## [3.1.3] - 2021-05-11 + +### Fixed + +- File URL reparse bug with percent encoded windows drive letter [whatwg/url#589](https://github.com/whatwg/url/pull/589) + +### Changed + +- Switched to GitHub Actions for CI +- Minimum PHPUnit version is now 7.5 + +## [3.1.2] - 2021-02-11 + +### Added + +- Performance improvements +- Test on PHP 8 release in CI + +## [3.1.1] - 2020-10-17 + +### Added + +- Support for `brick/math` ^0.9 + +### Changed + +- Serializing non-special URLs is now idempotent per [whatwg/url#505](https://github.com/whatwg/url/pull/505) +- Changes to file URL path normalization per [whatwg/url#544](https://github.com/whatwg/url/pull/544). + +## [3.1.0] - 2020-07-15 + +### Added + +- Added test coverage on Windows 32-bit. +- Dependency on `rowbot/idna` to support international domain names without `ext-intl`. + +### Changed + +- `URLSearchParams::sort()` now correctly sorts by code units instead of trying to fake it using code points. +- Null bytes are now percent encoded in fragments instead of being ignored per [whatwg/url#440](https://github.com/whatwg/url/issues/440). +- Domain names of URLs with special schemes can no longer be an empty string per [whatwg/url#492](https://github.com/whatwg/url/pull/492) and [whatwg/url#497](https://github.com/whatwg/url/pull/497). +- Host names now also forbid the use of the `<`, `>`, and `^` code points per [whatwg/url#458](https://github.com/whatwg/url/issues/458). + +### Fixed + +- Incorrect output encoding used when encoding was overriden to be "replacement", "utf-16", "utf-16le", or "utf-16be". +- `ext-json` is now correctly listed as a dependency. + +### Removed + +- Dependency on `ext-intl` and `lib-ICU`. + +## [3.0.1] - 2020-02-29 + +### Added + +- Improved portability and 32-bit PHP support. +- Dependency on `brick/math` to support 32-bit versions of PHP. + +### Removed + +- Removed `ext-gmp` as a dependency. + +## [3.0.0] - 2020-02-11 + +The majority of this library was rewritten in this update, but the public API has remain unchanged. This should make for a relatively painless upgrade. + +### Added + +- Installation requirement of ICU >= 4.6 to assist with [#6](https://github.com/TRowbotham/URL-Parser/issues/6) +- Support for PHPUnit ^8.0 and ^9.0. +- More test coverage. +- Support for symfony/cache ^5.0. + +### Changed + +- "gopher" was removed from the list of special schemes per [whatwg/url#453](https://github.com/whatwg/url/pull/453) and [whatwg/url#454](https://github.com/whatwg/url/pull/454). +- Coding style has been updated for PSR-12. +- Non-iterable objects passed to the `URLSearchParams` constructor now have their properties iterated over using `\ReflectionObject::getProperties()` rather than directly. There should not be any change in behavior. +- Removed artificial limitation when passing a sequence of sequences to the `URLSearchParams` constructor that required the non-array sub-sequences to implement the `\ArrayAccess` interface. + - Note that sub-sequences must still be countable and only contain exactly 2 items. + - This broadens the type of sequences that can be supplied from `iterable` to `iterable>`, where `stringable` is a scalar value or an object with the `__toString()` method. +- `URLSearchParams` now throws a `\Rowbot\URL\Exception\TypeError` instead of a `\InvalidArgument` exception to match browsers when passed an iterable that does not solely contain other iterables that are countable. +- JSON encoding the `URL` object will now throw a `\Rowbot\URL\Exception\JsonException` when JSON encoding fails. + +### Fixed + +- Documentaion errors. +- Restores expected string conversion behavior on systems using an ICU version >= 60.1 ([#7](https://github.com/TRowbotham/URL-Parser/issues/6)). +- `Origin::getEffectiveDomain()` was incorrectly returning the string `"null"` instead of the actual value `null` when the origin was an opaque origin. + +### Removed + +- `phpstan/phpstan` and associated packages are no longer a dev dependency and is now only run in CI. +- No longer depends on `ext-ctype`, which was an unlisted dependency. +- `php-coveralls/php-coveralls` is no longer a dev dependency. The project has moved to Codecov instead. +- `squizlabs/php_codesniffer` is no longer a dev dependency and is only run in CI now. + +## [2.0.3] - 2019-08-13 + +### Fixed + +- "%2e%2E" and "%2E%2e" were not properly detected as percent encoded double dot path segments. + +## [2.0.2] - 2019-04-28 + +### Added + +- PHP 7.4 compatibility +- Sped up IPv6 address serialization by removing some unncessary type casting. + +## [2.0.1] - 2019-04-15 + +### Fixed + +- Loading cached test data failed when using newer versions of the symfony/cache component. + +## [2.0.0] - 2018-12-08 + +### Added + +- Tests now automatically pull the latest data directly from the Web Platform Tests repository - thanks [@nyamsprod](https://github.com/nyamsprod) + +### Changed + +- The minimum required PHP version is now 7.1. +- Testing environment updated for PHP 7.1 - thanks [@nyamsprod](https://github.com/nyamsprod) +- Native typehints are now used. This which means that an `\TypeError` is now thrown instead of an `\InvalidArgumentException` when a value with an incorrect type is passed. +- `\Rowbot\URL\Exception\TypeError` and `\Rowbot\URL\Exception\InvalidParserState` now inherit from `\Rowbot\URL\Exception\URLException` + +## [1.1.1] - 2018-08-15 + +### Added + +- Sped up `URLSearchParams::has()` when the string does not exist in the list. +- TravisCI automation for tests and code coverage. + +### Fixed + +- The query string sorting algorithm now correctly sorts by code units instead of code points. + +## [1.1.0] - 2018-06-21 + +### Added + +- Updated documentation +- Updated tests + +### Changed + +- Only scalar values (bool, float, int, string) and objects with a `__toString()` method are considered as valid input now for methods and properties that only accept strings. This matches what PHP's string type hint would allow, allowing for an easier upgrade path when adding native type hints in the future. + - A `null` value is no longer considered valid input and will cause an `\InvalidArgumentException` to be thrown. Previously, this was converted to the string `"null"`. + - A `resource` value such as that returned by a call to `fopen()` is no longer considered valid input and will cause an `\InvalidArgumentException` to be thrown. This was previously casted to a string resulting in something like `"Resource id #1"`. + - Previously, the values `true` and `false` were converted to the strings `"true"` and `"false"`. This is no longer the case. They now are now simply cast to a string resulting in the values `"1"` and `""` respectively. +- Passing an `iterable` that does not solely contain other `iterables` to the `URLSearchParams` constructor now causes it to throw an `\InvalidArgumentException`. +- Trying to access an invalid property on the `URL` object will now throw an `\InvalidArgumentException` to help catch typos. + +## [1.0.3] - 2018-06-19 + +### Added + +- Sped up serialization of IPv6 addresses +- Slightly better handling of non-UTF-8 encoded text when parsing query strings. +- A bunch of missing `use` import statements +- Updated tests and test data. + +### Changed + +- URLs with [special schemes](https://url.spec.whatwg.org/#special-scheme) now percent encode `'` characters in the query string. +- The application/x-www-form-urlencoded parser now only handles UTF-8 encoded text. + +## [1.0.2] - 2018-02-28 + +### Changed + +- Trying to set `URL::searchParams` directly will now throw a `\Rowbot\URL\Exception\TypeError` +- Passing invalid input will throw an `\InvalidArgumentException` +- Malformed byte sequences will now get fixed up with `\u{FFFD}` replacement characters. +- `URLSearchParams` now implements `\Iterator` instead of `\IteratorAggregate` to match test expectations. + +### Fixed + +- The last few failing tests are now passing with the exception of 3 errors as a result of PHP bug [72506](https://bugs.php.net/bug.php?id=72506) + +## [1.0.1] - 2018-02-22 + +### Added + +- MIT License + +## [1.0.0] - 2018-02-22 + +- Initial Release! diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/LICENSE b/packages/playground/data-liberation/vendor-patched/rowbot/url/LICENSE new file mode 100644 index 0000000000..34196ce795 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Trevor Rowbotham + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/README.md b/packages/playground/data-liberation/vendor-patched/rowbot/url/README.md new file mode 100644 index 0000000000..9136d43370 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/README.md @@ -0,0 +1,255 @@ +# URL-Parser + +[![GitHub](https://img.shields.io/github/license/TRowbotham/URL-Parser.svg?style=flat-square)](https://github.com/TRowbotham/URL-Parser/blob/master/LICENSE) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/TRowbotham/URL-Parser/tests.yml?branch=master&style=flat-square)](https://github.com/TRowbotham/URL-Parser/actions) +[![Codecov](https://img.shields.io/codecov/c/github/TRowbotham/URL-Parser/master?logo=Codecov&style=flat-square&token=WDWFg8wmjW)](https://codecov.io/gh/TRowbotham/URL-Parser) +[![Packagist](https://img.shields.io/packagist/v/rowbot/url.svg?style=flat-square)](https://packagist.org/packages/rowbot/url) +[![Packagist](https://img.shields.io/packagist/dt/rowbot/url.svg?style=flat-square)](https://packagist.org/packages/rowbot/url) + +A [WHATWG URL](https://url.spec.whatwg.org/) spec compliant URL parser for working with URLs and their query strings. + +This API offers 2 objects that you can use to help you work with URLs; [URL](#url) and [URLSearchParams](#urlsearchparams). + +## Demo + +Checkout an interactive demo [here](https://url-demo.trowbotham.com/). + +## Requirements + +- PHP >= 8.1 +- `ext-mbstring` +- `brick/math` +- `rowbot/idna` + +## Installation + +```bash +composer require rowbot/url +``` + +## URL + +The URL object is the primary object for working with a URL. + +### The URL constructor + +`URL(string|\Stringable $url[, null|string|\Stringable $base = null, array $options = []])` + +The `$options` argument accepts an array with a key `logger` whose value is an object implementing +`\Psr\Log\LoggerInterface`. See [Logging](#logging) for more information. + +#### URL constructor throws + +- `\Rowbot\URL\Exception\TypeError` + - When the URL parser determines that the given input is not a valid URL. + +```php +use Rowbot\URL\URL; + +// Construct a new URL object. +$url = new URL('https://example.com/'); + +// Construct a new URL object using a relative URL, by also providing the constructor with the base URL. +$url = new URL('path/to/file.php?query=string', 'http://example.com'); +echo $url->href; // Outputs: "http://example.com/path/to/file.php?query=string" + +// You can also pass an existing URL object to either the $url or $base arguments. +$url = new URL('https://example.org:123'); +$url1 = new URL('foo/bar/', $url); +echo $url1->href; // Outputs: "https://example.org:123/foo/bar/" + +// Catch the error when URL parsing fails. +try { + $url = new URL('http://2001::1]'); +} catch (\Rowbot\URL\Exception\TypeError $e) { + echo 'Invalid URL'; +} +``` + +### URL Members + +Note: As a convience, both the `__get()` and `__set()` methods will throw an `\InvalidArgumentException` if you try to get or set an invalid property. + +#### `string URL::href` + +The `href` getter returns the serialization of the URL. The `href` setter will parse the entire string +updating all the components of the URL with the new values. Providing an invalid URL will cause the +setter to throw a `\Rowbot\URL\TypeError`. + +#### `readonly string URL::origin` + +The `origin` member is readonly. Its output is in the form of `scheme://host:port`. If a URL does not +have a port, then that will be excluded from the output. + +#### `string URL::protocol` + +The `protocol` getter, also known as a scheme, returns the protocol of the URL, such as http, ftp, or ssh. +The `protocol` setter is used to change the URLs protocol. + +#### `string URL::username` + +The `username` getter returns the username portion of the URL, or an empty string if the URL does not contain a username. The `username` setter changes the URLs username. + +#### `string URL::password` + +The `password` getter returns the password portion of the URL, or an empty string if the URL does not contain a password. The `password` setter changes the URLs password. + +#### `string URL::host` + +The `host` getter returns the combination of `hostname` and `port`. The output would look like `hostname:port`. If the URL does not have a port, then the port is not present in the output. The `host` setter allows you to change both the `hostname` and `port` at the same time. + +#### `string URL::hostname` + +The `hostname` getter returns the hostname of the URL. For example, the hostname of `https://example.com:31` would be `example.com`. The `hostname` setter will change the hostname portion of the URL. + +#### `string URL::port` + +The `port` getter returns an integer as a string representing the URLs port. If the URL does not have a port, the empty string will be returned instead. The `port` setter updates the URLs port. + +#### `string URL::pathname` + +The `pathname` getter returns the URLs path. The `pathname` setter updates the URLs path. + +#### `string URL::search` + +The `search` getter returns the URLs query string. The `search` setter updates the URLs URLSearchParams list. + +#### `readonly URLSearchParams URL::searchParams` + +Returns the URLSearchParams object associated with this URL allowing you to modify the query parameters without having to clobber the entire query string. This will always return the same object. + +#### `string URL::hash` + +The `hash` getter, also known as a URLs fragment, returns the portion of the URL that follows the "#" character. The `hash` setter updates the portion of the URL that follows the "#". + +#### `bool URL::canParse(string|\Stringable $url[, null|string|\Stringable $base = null])` + +A static method that allows the user to quickly check if a URL is parsable, without needing to construct a new URL object and wrapping it with a try/catch statement. + +#### `string URL::toJSON()` + +Returns a JSON encoded string of the URL. Note that this method escapes forward slashes, which is not the default for PHPs `json_encode()`, but matches the default behavior of JavaScripts `JSON.stringify()`. If you wish to control the serialization, then pass the URL obect to the `json_encode()` function. + +#### `string URL::jsonSerialize()` + +The URL object implements the `JsonSerializable` interface allowing you to pass the object as a whole to the json_encode() function. + +#### `string URL::toString()` + +Returns the serialization of the URL. + +#### `string URL::__toString()` + +See [URL::toString()](#string-urltostring) + +## URLSearchParams + +The URLSearchParams object allows you to work with query strings when you don't need a full URL. The URLSearchParams object implements the `Iterator` interface so that you may iterate over the list of search parameters. The iterator will return an array containing exactly 2 items. The first item is the parameter name and the second item is the parameter value. + +### The URLSearchParams constructor + +`URLSearchParams([iterable|(\Traversable&\Countable)>|object|string|\Stringable $init])` + +#### URLSearchParams constructor throws + +- `\Rowbot\URL\Exception\TypeError` + - When an iterable is passed and one if its values is not iterable. + - When an iterable is passed and one of its values is not countable, such as an object that implements `\Iterator`, but not `\Countable`. + - When an iterable is passed and one of its sequences does not contain exactly 2 items, such as an array that contains only 1 string. + +```php +use Rowbot\URL\URLSearchParams; + +// Construct an empty list of search params. +$params = new URLSearchParams(); + +// Construct a new list from a query string. Remember that a leading "?" will be stripped. +$params = new URLSearchParams('?foo=bar'); + +// Construct a new list using an array of arrays containing strings. Alternatively, you could pass an +// object that implements the Traversable interface and whose iterator returns an array of arrays, +// with each array containing exactly 2 items. +$params = new URLSearchParams([ + ['foo', 'bar'], + ['foo', 'bar'] // Duplicates are allowed! + ['one', 'two'] +]); + +// Iterate over a URLSearchParams object. +foreach ($params as $index => $param) { + if ($index > 0) { + echo '&'; + } + + echo $param[0] . '=' . $param[1]; +} + +// Above loop prints "foo=bar&foo=bar&one=two". + +// Construct a new list using an object +$obj = new \stdClass(); +$obj->foo = 'bar'; +$params = new URLSearchParams($obj); + +// Copy an existing URLSearchParams object into a new one. +$params1 = new URLSearchParams($params); +``` + +### URLSearchParams Members + +#### `void URLSearchParams::append(string $name, string $value)` + +Appends a new name-value pair to the list. + +#### `void URLSearchParams::delete(string $name[, string $value])` + +Deletes all name-value pairs whose name is `$name` from the list. If the optional `$value` is provided, +then only pairs with the same name and value are removed. + +#### `string|null URLSearchParams::get(string $name)` + +Returns the value of the first name-value pair whose name is `$name` in the list or null if there are no name-value pairs whose name is `$name` in the list. + +#### `string[] URLSearchParams::getAll(string $name)` + +Returns a list of values of all name-value pairs whose name is `$name`, in list order, or the empty list if there are no name-value pairs whose name is `$name` in the list. + +#### `bool URLSearchParams::has(string $name[, string $value])` + +Returns true if there is a name-value pair in the list, and false otherwise. + +#### `void URLSearchParams::set(string $name, string $value)` + +If the list contains name-value pairs whose name is `$name`, the first name-value pair in the list whose name is `$name` will have its value changed to `$value` and all others following it in the list will be removed. If the list does not contain a name-value pair whose name is `$name` then the new name-value pair will be appended to the list. + +#### `void URLSearchParams::sort()` + +Sorts the list of search params by comparing code units. The relative order of name-value pairs with the same name are preserved. + +#### `string URLSearchParams::toString()` + +Returns the serialization of the list of name-value pairs. + +### `int URLSearchParams::$size` + +Returns the number of query parameters in the list. + +#### `string URLSearchParams::__toString()` + +See [URLSearchParams::toString()](#string-urlsearchparamstostring) + +## Logging + +The given logger logs validation errors. Entries with a level of `warning` are fatal errors that cause the parser to +fail. Entries with a level of `notice` are not fatal. All validation errors have an `input` key and either a `column` +or `column_range` offset key. Column offsets start at 1. + +### Logging context + +| Key | Type | Description | +| ---------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `input` | `string` | The input string that the parser is operating on at the time of error. | +| `column` | `positive-int` | The column offset of the error. | +| `column_range` | `array{0: positive-int, 1: positive-int}` | Index 0 is the starting column offset, and index 1 is the end column offset. The range is inclusive. | +| `idn_errors` | `list` | A list of strings that represent IDN error constant names. | +| `unicode_domain` | `string` | The domain name as a Unicode string. | diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/composer.json b/packages/playground/data-liberation/vendor-patched/rowbot/url/composer.json new file mode 100644 index 0000000000..ab6974fed1 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/composer.json @@ -0,0 +1,60 @@ +{ + "name": "rowbot/url", + "description": "A WHATWG URL spec compliant URL parser for working with URLs and their query strings.", + "keywords": [ + "url", + "url-parser", + "uri", + "querystring", + "query-string", + "url-parsing", + "whatwg" + ], + "license": "MIT", + "authors": [ + { + "name": "Trevor Rowbotham", + "homepage": "https://trowbotham.com", + "role": "Developer" + } + ], + "require": { + "php": ">=8.1", + "ext-mbstring": "*", + "brick/math": "^0.8.13 || ^0.9", + "psr/log": "^3.0", + "rowbot/idna": "^0.1.5" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.0", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^10.0 || ^11.0", + "slevomat/coding-standard": "^8.0", + "squizlabs/php_codesniffer": "^3.7.1", + "symfony/cache": "^5.0 || ^6.0" + }, + "autoload": { + "psr-4": { + "Rowbot\\URL\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Rowbot\\URL\\Tests\\": "tests/" + } + }, + "scripts": { + "phpunit": "phpunit --coverage-text", + "test": [ + "@phpunit" + ] + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/phpstan.neon b/packages/playground/data-liberation/vendor-patched/rowbot/url/phpstan.neon new file mode 100644 index 0000000000..d25b910ca6 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/phpstan.neon @@ -0,0 +1,23 @@ +includes: + - vendor/phpstan/phpstan-strict-rules/rules.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon + +parameters: + level: max + treatPhpDocTypesAsCertain: false + + paths: + - src + + ignoreErrors: + - + message: '#Call to function is_object\(\) with object will always evaluate to true\.#' + path: src/URLSearchParams.php + count: 1 + + - + message: '#Cannot use array destructuring on Countable&iterable\.#' + path: src/URLSearchParams.php + count: 1 + + # Note to self: You can't escape single quotes in neon. Another possible alternative would be to replace single quotes with \x27. diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/phpunit.xml b/packages/playground/data-liberation/vendor-patched/rowbot/url/phpunit.xml new file mode 100644 index 0000000000..d17d5f3bb7 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/phpunit.xml @@ -0,0 +1,19 @@ + + + + + ./src + + + + + ./tests + + + diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/APIParserErrorType.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/APIParserErrorType.php new file mode 100644 index 0000000000..f0f316cd48 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/APIParserErrorType.php @@ -0,0 +1,12 @@ +url = $urlRecord; + $this->error = $error; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/BasicURLParser.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/BasicURLParser.php new file mode 100644 index 0000000000..2f4006649d --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/BasicURLParser.php @@ -0,0 +1,139 @@ +logger = $logger; + } + + /** + * The parser can parse both absolute and relative URLs. If a relative URL is given, a base URL must also be given + * so that an absolute URL can be resolved. It can also parse individual parts of a URL when the default starting + * state is overridden, however, a previously parsed URL record object must be provided in this case. + * + * @see https://url.spec.whatwg.org/#concept-basic-url-parser + * + * @param \Rowbot\URL\String\USVStringInterface $input A UTF-8 encoded string consisting of only scalar + * values, excluding surrogates. + * @param \Rowbot\URL\URLRecord|null $base (optional) This represents the base URL, which in + * most cases, is the document's URL, it may also be + * a node's base URI or whatever base URL you wish to + * resolve relative URLs against. Default is null. + * @param string|null $encodingOverride (optional) Overrides the default ouput encoding, + * which is UTF-8. This option exists solely for the + * use of the HTML specification and should never be + * changed. + * @param \Rowbot\URL\URLRecord|null $url (optional) This represents an existing URL record + * object that should be modified based on the input + * URL and optional base URL. Default is null. + * @param \Rowbot\URL\ParserState|null $stateOverride (optional) An object implementing the + * \Rowbot\URL\ParserState interface that overrides + * the default start state, which is the Scheme Start + * State. Default is null. + * @return \Rowbot\URL\URLRecord|false + * @param ?\Rowbot\URL\ParserState::* $stateOverride + */ + public function parse( + USVStringInterface $input, + ?URLRecord $base = null, + ?string $encodingOverride = null, + ?URLRecord $url = null, + ?string $stateOverride = null + ) { + $count = 0; + + if ($url === null) { + $url = new URLRecord(); + $originalInput = $input; + $input = $input->replaceRegex('/^[\x00-\x20]+|[\x00-\x20]+$/u', '', -1, $count); + + if ($count !== 0) { + // Validation error. + ($nullsafeVariable1 = $this->logger) ? $nullsafeVariable1->notice('invalid-URL-unit', [ + 'input' => (string) $originalInput, + 'column_range' => (static function () use ($originalInput): array { + $originalInput->matches('/^[\x00-\x20]+|[\x00-\x20]+$/u', $matches, PREG_OFFSET_CAPTURE); + + if ($matches[0][1] === 0) { + return [1, strlen($matches[0][0])]; + } + + return [$originalInput->length() - strlen($matches[0][0]) + 1, $originalInput->length()]; + })(), + ]) : null; + } + } + + $originalInput = $input; + $input = $input->replaceRegex('/[\x09\x0A\x0D]+/u', '', -1, $count); + + if ($count !== 0) { + // Validation error. + ($nullsafeVariable2 = $this->logger) ? $nullsafeVariable2->notice('invalid-URL-unit', [ + 'input' => (string) $originalInput, + 'column_range' => (static function () use ($originalInput): array { + $originalInput->matches('/[\x09\x0A\x0D]+/u', $matches, PREG_OFFSET_CAPTURE); + $start = mb_strlen(substr((string) $originalInput, 0, $matches[0][1]), 'utf-8'); + + return [$start + 1, $start + strlen($matches[0][0])]; + })(), + ]) : null; + } + + $iter = $input->getIterator(); + $iter->rewind(); + // length + imaginary eof character + $length = $input->length() + 1; + $buffer = new StringBuffer(); + $context = new ParserContext( + $input, + $iter, + $buffer, + $url, + $base, + $stateOverride, + $encodingOverride, + $this->logger + ); + + do { + $handler = ParserState::createHandlerFor($context->state); + $status = $handler->handle($context, $iter->current()); + + if ($status === StatusCode::CONTINUE) { + $status = StatusCode::OK; + + continue; + } + + $iter->next(); + } while ($status === StatusCode::OK && $iter->key() < $length); + + switch ($status) { + case StatusCode::FAILURE: + return false; + default: + return $url; + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/AbstractPath.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/AbstractPath.php new file mode 100644 index 0000000000..d3195609ef --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/AbstractPath.php @@ -0,0 +1,72 @@ + + */ + protected $list; + + /** + * @param list<\Rowbot\URL\Component\PathSegment> $paths + */ + public function __construct(array $paths = []) + { + $this->list = $paths; + } + + public function count(): int + { + return count($this->list); + } + + public function first(): PathSegment + { + if (!isset($this->list[0])) { + throw new UndefinedIndexException(); + } + return $this->list[0]; + } + + public function isEmpty(): bool + { + return $this->list === []; + } + + public function potentiallyStripTrailingSpaces(URLRecord $url): void + { + if (!$this->isOpaque()) { + return; + } + + if ($url->fragment !== null) { + return; + } + + if ($url->query !== null) { + return; + } + + $this->list[0]->stripTrailingSpaces(); + } + + public function __clone() + { + $list = []; + + foreach ($this->list as $path) { + $list[] = clone $path; + } + + $this->list = $list; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/AbstractHost.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/AbstractHost.php new file mode 100644 index 0000000000..c9e38dfe30 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/AbstractHost.php @@ -0,0 +1,23 @@ +?@[\\\\\]^|'; + private const FORBIDDEN_DOMAIN_CODEPOINTS = self::FORBIDDEN_HOST_CODEPOINTS . '\x01-\x1F%\x7F'; + + private const UNICODE_IDNA_OPTIONS = [ + 'CheckHyphens' => false, + 'CheckBidi' => true, + 'CheckJoiners' => true, + 'UseSTD3ASCIIRules' => false, + 'Transitional_Processing' => false, + ]; + + /** + * Parses a host string. The string could represent a domain, IPv4 or IPv6 address, or an opaque host. + * + * @param bool $isOpaque (optional) Whether or not the URL has a special scheme. + * + * @return \Rowbot\URL\Component\Host\HostInterface|false The returned Host can never be a null host. + */ + public function parse( + ParserContext $context, + USVStringInterface $input, + bool $isOpaque = false + ) { + if ($input->startsWith('[')) { + if (!$input->endsWith(']')) { + // Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->warning('IPv6-unclosed', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 2, + ]) : null; + + return false; + } + + return IPv6AddressParser::parse($context, $input->substr(1, -1)); + } + + if ($isOpaque) { + return $this->parseOpaqueHost($context, $input); + } + + assert(!$input->isEmpty()); + $domain = rawurldecode((string) $input); + $asciiDomain = $this->domainToAscii($context, $domain, false); + + if ($asciiDomain === false) { + return false; + } + + if ($asciiDomain->matches('/[' . self::FORBIDDEN_DOMAIN_CODEPOINTS . ']/u', $matches, PREG_OFFSET_CAPTURE)) { + // Validation error. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->warning('domain-invalid-code-point', [ + 'input' => (string) $asciiDomain, + 'column' => mb_strlen(mb_strcut((string) $asciiDomain, 0, $matches[0][1], 'utf-8'), 'utf-8') + 1, + 'unicode_domain' => Idna::toUnicode((string) $asciiDomain, self::UNICODE_IDNA_OPTIONS)->getDomain(), + ]) : null; + + return false; + } + + if (IPv4AddressParser::endsInIPv4Number($asciiDomain)) { + return IPv4AddressParser::parse($context, $asciiDomain); + } + + return $asciiDomain; + } + + /** + * @see https://url.spec.whatwg.org/#concept-domain-to-ascii + * @return \Rowbot\URL\Component\Host\StringHost|false + */ + private function domainToAscii(ParserContext $context, string $domain, bool $beStrict) + { + // 1. Let result be the result of running Unicode ToASCII with domain_name set to domain, UseSTD3ASCIIRules set + // to beStrict, CheckHyphens set to false, CheckBidi set to true, CheckJoiners set to true, + // Transitional_Processing set to false, and VerifyDnsLength set to beStrict. + $result = Idna::toAscii($domain, [ + 'CheckHyphens' => false, + 'CheckBidi' => true, + 'CheckJoiners' => true, + 'UseSTD3ASCIIRules' => $beStrict, + 'Transitional_Processing' => false, + 'VerifyDnsLength' => $beStrict, + ]); + $convertedDomain = $result->getDomain(); + + // 2. If result is a failure value, validation error, return failure. + // 3. If result is the empty string, validation error, return failure. + if ($convertedDomain === '' || $result->hasErrors()) { + // Validation error. + ($nullsafeVariable3 = $context->logger) ? $nullsafeVariable3->warning('domain-to-ASCII', [ + 'input' => $domain, + 'column_range' => [1, mb_strlen($domain, 'utf-8')], + 'idn_errors' => $this->enumerateIdnaErrors($result->getErrors()), + 'unicode_domain' => Idna::toUnicode($domain, self::UNICODE_IDNA_OPTIONS)->getDomain(), + ]) : null; + + return false; + } + + // 4. Return result. + return new StringHost($convertedDomain); + } + + /** + * Parses an opaque host. + * + * @see https://url.spec.whatwg.org/#concept-opaque-host-parser + * @return \Rowbot\URL\Component\Host\HostInterface|false + */ + private function parseOpaqueHost(ParserContext $context, USVStringInterface $input) + { + if ($input->matches('/[' . self::FORBIDDEN_HOST_CODEPOINTS . ']/u', $matches, PREG_OFFSET_CAPTURE)) { + // Validation error. + ($nullsafeVariable4 = $context->logger) ? $nullsafeVariable4->warning('host-invalid-code-point', [ + 'input' => (string) $input, + 'column' => mb_strlen(mb_strcut((string) $input, 0, $matches[0][1], 'utf-8'), 'utf-8') + 1, + ]) : null; + + return false; + } + + foreach ($input as $i => $codePoint) { + if ($codePoint !== '%' && !CodePoint::isUrlCodePoint($codePoint)) { + // Validation error. + ($nullsafeVariable5 = $context->logger) ? $nullsafeVariable5->notice('invalid-URL-unit', [ + 'input' => (string) $input, + 'column' => $i, + ]) : null; + } elseif ($codePoint === '%' && !$input->substr($i + 1)->startsWithTwoAsciiHexDigits()) { + // Validation error. + ($nullsafeVariable6 = $context->logger) ? $nullsafeVariable6->notice('invalid-URL-unit', [ + 'input' => (string) $input, + 'column' => $i, + ]) : null; + } + } + + $percentEncoder = new PercentEncoder(); + $output = $percentEncoder->percentEncodeAfterEncoding('utf-8', (string) $input, EncodeSet::C0_CONTROL); + + return new StringHost($output); + } + + /** + * @return list + */ + private function enumerateIdnaErrors(int $bitmask): array + { + $reflection = new ReflectionClass(Idna::class); + $errorConstants = array_filter( + $reflection->getConstants(ReflectionClassConstant::IS_PUBLIC), + static function (string $name) : bool { + return strncmp($name, 'ERROR_', strlen('ERROR_')) === 0; + }, + ARRAY_FILTER_USE_KEY + ); + $errors = []; + + foreach ($errorConstants as $name => $value) { + if (($value & $bitmask) !== 0) { + $errors[] = $name; + } + } + + return $errors; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv4Address.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv4Address.php new file mode 100644 index 0000000000..51e4f6ed62 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv4Address.php @@ -0,0 +1,40 @@ +address = $address; + } + + /** + * @param \Rowbot\URL\Component\Host\HostInterface $other + */ + public function equals($other): bool + { + return $other instanceof self && $this->address === $other->address; + } + + public function getSerializer(): HostSerializerInterface + { + return new IPv4AddressSerializer($this->address); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv4AddressParser.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv4AddressParser.php new file mode 100644 index 0000000000..6ce0c8b5f8 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv4AddressParser.php @@ -0,0 +1,275 @@ +split('.'); + $count = $parts->count(); + + // 2. If the last item in parts is the empty string, then: + if ($parts->last()->isEmpty()) { + // 2.1. Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('IPv4-part-empty', [ + 'input' => (string) $input, + 'column' => $input->length() + 1, + ]) : null; + + // 2.2. If parts’s size is greater than 1, then remove the last item from parts. + if ($count > 1) { + $parts->pop(); + --$count; + } + } + + // 3. If parts’s size is greater than 4, validation error, return failure. + if ($count > 4) { + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->warning('IPv4-too-many-parts', [ + 'input' => (string) $input, + 'column' => array_reduce( + array_slice(array_merge(iterator_to_array($parts)), 0, 4), + static function (int $carry, USVStringInterface $part) : int { + return $carry + $part->length(); + }, + 4 + ), + ]) : null; + + return false; + } + + // 4. Let numbers be an empty list. + $numbers = []; + + // 5. For each part of parts: + foreach ($parts as $i => $part) { + // 5.1. Let result be the result of parsing part. + $result = self::parseIPv4Number($part); + + // 5.2. If result is failure, validation error, return failure. + if ($result === false) { + ($nullsafeVariable3 = $context->logger) ? $nullsafeVariable3->warning('IPv4-non-numeric-part', [ + 'input' => (string) $input, + 'column_range' => self::getColumnRange($i, $parts), + ]) : null; + + return false; + } + + // 5.3. If result[1] is true, then set validationError to true. + if ($result[1] === true) { + // Validation error. + ($nullsafeVariable4 = $context->logger) ? $nullsafeVariable4->notice('IPv4-non-decimal-part', [ + 'input' => (string) $input, + 'column_range' => self::getColumnRange($i, $parts), + ]) : null; + } + + // 5.4. Append result[0] to numbers. + $numbers[] = $result[0]; + } + + $size = count($numbers); + $sizeMinusOne = $size - 1; + $failure = false; + $errorQueue = []; + + // 6. If any item in numbers is greater than 255, validation error. + foreach ($numbers as $i => $number) { + if ($number->isGreaterThan(255)) { + // 7. If any but the last item in numbers is greater than 255, then return failure. + if ($i < $sizeMinusOne) { + $errorQueue[$i] = LogLevel::WARNING; + $failure = true; + + break; + } + + $errorQueue[$i] = LogLevel::NOTICE; + + break; + } + } + + $limit = NumberFactory::createNumber(256, 10)->pow(5 - $size); + + // 8. If the last item in numbers is greater than or equal to 256 ** (5 − numbers’s size), validation error, + // return failure. + if ($numbers[$sizeMinusOne]->isGreaterThanOrEqualTo($limit)) { + $errorQueue[$sizeMinusOne] = LogLevel::WARNING; + $failure = true; + } + + foreach ($errorQueue as $partIndex => $level) { + ($nullsafeVariable5 = $context->logger) ? $nullsafeVariable5->log($level, 'IPv4-out-of-range-part', [ + 'input' => (string) $input, + 'column_range' => self::getColumnRange($partIndex, $parts), + ]) : null; + } + + if ($failure) { + return false; + } + + /** + * 9. Let ipv4 be the last item in numbers. + * 10. Remove the last item from numbers. + * + * @var \Rowbot\URL\Component\Host\Math\NumberInterface $ipv4 + */ + $ipv4 = array_pop($numbers); + + // 11. Let counter be 0. + $counter = 0; + + // 12. For each n of numbers: + foreach ($numbers as $number) { + // 12.1. Increment ipv4 by n × 256 ** (3 − counter). + $ipv4 = $ipv4->plus($number->multipliedBy(256 ** (3 - $counter))); + + // 12.2. Increment counter by 1. + ++$counter; + } + + // 13. Return ipv4. + return new IPv4Address((string) $ipv4); + } + + public static function endsInIPv4Number(USVStringInterface $input): bool + { + // 1. Let parts be the result of strictly splitting input on U+002E (.). + $parts = $input->split('.'); + + // 2. If the last item in parts is the empty string, then: + if ($parts->last()->isEmpty()) { + // 2.1. If parts’s size is 1, then return false. + if ($parts->count() === 1) { + return false; + } + + // 3.1. Remove the last item from parts. + $parts->pop(); + } + + // 3. Let last be the last item in parts. + $last = $parts->last(); + + // 4. If parsing last as an IPv4 number does not return failure, then return true. + if (self::parseIPv4Number($last) !== false) { + return true; + } + + // 5. If last is non-empty and contains only ASCII digits, then return true. + // 6. Return false. + return !$last->isEmpty() && strspn((string) $last, CodePoint::ASCII_DIGIT_MASK) === $last->length(); + } + + /** + * @see https://url.spec.whatwg.org/#ipv4-number-parser + * + * @return array{0: \Rowbot\URL\Component\Host\Math\NumberInterface, 1: bool}|false + */ + private static function parseIPv4Number(USVStringInterface $input) + { + // 1. If input is the empty string, then return failure. + if ($input->isEmpty()) { + return false; + } + + // 2. Let validationError be false. + $validationError = false; + + // 3. Let R be 10. + $radix = 10; + + if ($input->length() > 1) { + // 4. If input contains at least two code points and the first two code points are either "0x" or "0X", + // then: + if ($input->startsWith('0x') || $input->startsWith('0X')) { + // 4.1. Set validationError to true. + $validationError = true; + + // 4.2. Remove the first two code points from input. + $input = $input->substr(2); + + // 4.3. Set R to 16. + $radix = 16; + + // 5. Otherwise, if input contains at least two code points and the first code point is U+0030 (0), then: + } elseif ($input->startsWith('0')) { + // 5.1. Set validationError to true. + $validationError = true; + + // 5.2. Remove the first code point from input. + $input = $input->substr(1); + + // 5.3. Set R to 8. + $radix = 8; + } + } + + // 6. If input is the empty string, then return 0. + if ($input->isEmpty()) { + return [NumberFactory::createNumber(0, 10), $validationError]; + } + + $s = (string) $input; + $length = strlen($s); + + // 7. If input contains a code point that is not a radix-R digit, then return failure. + if ( + ($radix === 10 && strspn($s, CodePoint::ASCII_DIGIT_MASK) !== $length) + || ($radix === 16 && strspn($s, CodePoint::HEX_DIGIT_MASK) !== $length) + || ($radix === 8 && strspn($s, CodePoint::OCTAL_DIGIT_MASK) !== $length) + ) { + return false; + } + + // 8. Let output be the mathematical integer value that is represented by input in radix-R notation, using ASCII + // hex digits for digits with values 0 through 15. + // 9. Return (output, validationError). + return [NumberFactory::createNumber($s, $radix), $validationError]; + } + + /** + * @return array{0: int, 1: int} + */ + private static function getColumnRange(int $i, StringListInterface $parts): array + { + $partsArray = array_merge(iterator_to_array($parts)); + $offset = $i; + + for ($j = 0; $j < $i; ++$j) { + $offset += $partsArray[$j]->length(); + } + + $targetPartLength = $partsArray[$i]->length(); + + return [$offset + 1, $offset + $partsArray[$i]->length() + ($targetPartLength > 0 ? 0 : 1)]; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv6Address.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv6Address.php new file mode 100644 index 0000000000..cada301d6e --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv6Address.php @@ -0,0 +1,40 @@ + + */ + private $address; + + /** + * @param non-empty-list $address + */ + public function __construct(array $address) + { + $this->address = $address; + } + + /** + * @param \Rowbot\URL\Component\Host\HostInterface $other + */ + public function equals($other): bool + { + return $other instanceof self && $this->address === $other->address; + } + + public function getSerializer(): HostSerializerInterface + { + return new IPv6AddressSerializer($this->address); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv6AddressParser.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv6AddressParser.php new file mode 100644 index 0000000000..517f552546 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/IPv6AddressParser.php @@ -0,0 +1,372 @@ +getIterator(); + $iter->rewind(); + + // 5. If c is U+003A (:), then: + if ($iter->current() === ':') { + // 5.1. If remaining does not start with U+003A (:), validation error, return failure. + if ($iter->peek() !== ':') { + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->warning('IPv6-invalid-compression', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + + // 5.2. Increase pointer by 2. + $iter->seek(2); + + // 5.3. Increase pieceIndex by 1 and then set compress to pieceIndex. + $compress = ++$pieceIndex; + } + + // 6. While c is not the EOF code point: + while ($iter->valid()) { + // 6.1. If pieceIndex is 8, validation error, return failure. + if ($pieceIndex === 8) { + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->warning('IPv6-too-many-pieces', [ + 'input' => (string) $input, + 'column' => $iter->key(), + ]) : null; + + return false; + } + + // 6.2. If c is U+003A (:), then: + if ($iter->current() === ':') { + // 6.2.1. If compress is non-null, validation error, return failure. + if ($compress !== null) { + ($nullsafeVariable3 = $context->logger) ? $nullsafeVariable3->warning('IPv6-multiple-compression', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + + // 6.2.2. Increase pointer and pieceIndex by 1, set compress to pieceIndex, and then continue. + $iter->next(); + $compress = ++$pieceIndex; + + continue; + } + + // 6.3. Let value and length be 0. + $value = 0; + $length = 0; + $current = $iter->current(); + + // 6.4. While length is less than 4 and c is an ASCII hex digit, set value to value × 0x10 + c interpreted + // as hexadecimal number, and increase pointer and length by 1. + while ($length < 4 && strpbrk($current, CodePoint::HEX_DIGIT_MASK) === $current) { + $value = ($value * 0x10) + intval($current, 16); + $iter->next(); + ++$length; + $current = $iter->current(); + } + + // 6.5. If c is U+002E (.), then: + if ($iter->current() === '.') { + // 6.5.1. If length is 0, validation error, return failure. + if ($length === 0) { + ($nullsafeVariable4 = $context->logger) ? $nullsafeVariable4->warning('IPv4-in-IPv6-invalid-code-point', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + + // 6.5.2. Decrease pointer by length. + $iter->seek(-$length); + + // 6.5.3. If pieceIndex is greater than 6, validation error, return failure. + if ($pieceIndex > 6) { + ($nullsafeVariable5 = $context->logger) ? $nullsafeVariable5->warning('IPv4-in-IPv6-too-many-pieces', [ + 'input' => (string) $input, + 'column' => $iter->key(), + ]) : null; + + return false; + } + + $result = self::parseIPv4Address($context, $input, $iter, $address, $pieceIndex); + + if ($result === false) { + return false; + } + + [$address, $pieceIndex] = $result; + + // 6.5.7. Break. + break; + } + + // 6.6. Otherwise, if c is U+003A (:): + if ($iter->current() === ':') { + // 6.6.1. Increase pointer by 1. + $iter->next(); + + // 6.2.2. If c is the EOF code point, validation error, return failure. + if (!$iter->valid()) { + ($nullsafeVariable6 = $context->logger) ? $nullsafeVariable6->warning('IPv6-invalid-code-point', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + + // 6.7. Otherwise, if c is not the EOF code point, validation error, return failure. + } elseif ($iter->valid()) { + ($nullsafeVariable7 = $context->logger) ? $nullsafeVariable7->warning('IPv6-invalid-code-point', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + + // 6.8. Set address[pieceIndex] to value. + // 6.9. Increase pieceIndex by 1. + $address[$pieceIndex++] = $value; + } + + // 7. If compress is non-null, then: + if ($compress !== null) { + // 7.1. Let swaps be pieceIndex − compress. + $swaps = $pieceIndex - $compress; + + // 7.2. Set pieceIndex to 7. + $pieceIndex = 7; + + // 7.3. While pieceIndex is not 0 and swaps is greater than 0, swap address[pieceIndex] with + // address[compress + swaps − 1], and then decrease both pieceIndex and swaps by 1. + while ($pieceIndex !== 0 && $swaps > 0) { + $temp = $address[$pieceIndex]; + $address[$pieceIndex] = $address[$compress + $swaps - 1]; + $address[$compress + $swaps - 1] = $temp; + --$pieceIndex; + --$swaps; + } + + // Otherwise, if compress is null and pieceIndex is not 8, validation error, return failure. + } elseif ($pieceIndex !== 8) { + ($nullsafeVariable8 = $context->logger) ? $nullsafeVariable8->warning('IPv6-too-few-pieces', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + $arrayIsListFunction = function (array $array) : bool { + if (function_exists('array_is_list')) { + return array_is_list($array); + } + if ($array === []) { + return true; + } + $current_key = 0; + foreach ($array as $key => $noop) { + if ($key !== $current_key) { + return false; + } + ++$current_key; + } + return true; + }; + + assert($arrayIsListFunction($address)); + + // 9. Return address. + return new IPv6Address($address); + } + + /** + * @param non-empty-list $address + * + * @return array{0: non-empty-list, 1: int}|false + */ + private static function parseIPv4Address( + ParserContext $context, + USVStringInterface $input, + StringIteratorInterface $iter, + array $address, + int $pieceIndex + ) { + // 6.5.4. Let numbersSeen be 0. + $numbersSeen = 0; + + // 6.5.5. While c is not the EOF code point: + do { + // 6.5.5.1. Let ipv4Piece be null. + $ipv4Piece = null; + + // 6.5.5.2. If numbersSeen is greater than 0, then: + if ($numbersSeen > 0) { + // 6.5.5.2.2 Otherwise, validation error, return failure. + if ($iter->current() !== '.' || $numbersSeen >= 4) { + // Validation error. + ($nullsafeVariable9 = $context->logger) ? $nullsafeVariable9->warning('IPv4-in-IPv6-invalid-code-point', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + + // 6.5.5.2.1 If c is a U+002E (.) and numbersSeen is less than 4, then increase pointer by 1. + $iter->next(); + } + + $current = $iter->current(); + + // 6.5.5.3. If c is not an ASCII digit, validation error, return failure. + if (strpbrk($current, CodePoint::ASCII_DIGIT_MASK) !== $current) { + // Validation error. + ($nullsafeVariable10 = $context->logger) ? $nullsafeVariable10->warning('IPv4-in-IPv6-invalid-code-point', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + + // 6.5.5.4. While c is an ASCII digit: + do { + // 6.5.5.4.1. Let number be c interpreted as decimal number. + $number = (int) $current; + + // 6.5.5.4.2. If ipv4Piece is null, then set ipv4Piece to number. + if ($ipv4Piece === null) { + $ipv4Piece = $number; + + // Otherwise, if ipv4Piece is 0, validation error, return failure. + } elseif ($ipv4Piece === 0) { + // Validation error. + ($nullsafeVariable11 = $context->logger) ? $nullsafeVariable11->warning('IPv4-in-IPv6-invalid-code-point', [ + 'input' => (string) $input, + 'column' => $iter->key(), + ]) : null; + + return false; + + // Otherwise, set ipv4Piece to ipv4Piece × 10 + number. + } else { + $ipv4Piece = ($ipv4Piece * 10) + $number; + } + + // 6.5.5.4.3. If ipv4Piece is greater than 255, validation error, return failure. + if ($ipv4Piece > 255) { + // Validation error. + ($nullsafeVariable12 = $context->logger) ? $nullsafeVariable12->warning('IPv4-in-IPv6-out-of-range-part', [ + 'input' => (string) $input, + 'column_range' => (static function () use ($input, $iter, $numbersSeen): array { + $str = (string) $input; + $delimiter = $numbersSeen === 0 ? ':' : '.'; + $startIndex = strrpos($str, $delimiter, -($input->length() - $iter->key())); + $endIndex = strpos($str, '.', $iter->key()); + + if ($startIndex === false) { + $startIndex = $iter->key(); + } + + if ($endIndex === false) { + $endIndex = $input->length(); + } + + return [$startIndex + 2, $endIndex]; + })(), + ]) : null; + + return false; + } + + // 6.5.5.4.4. Increase pointer by 1. + $iter->next(); + $current = $iter->current(); + } while (strpbrk($current, CodePoint::ASCII_DIGIT_MASK) === $current); + + // 6.5.5.5. Set address[pieceIndex] to address[pieceIndex] × 0x100 + ipv4Piece. + $piece = $address[$pieceIndex]; + $address[$pieceIndex] = ($piece * 0x100) + $ipv4Piece; + + // 6.5.5.6. Increase numbersSeen by 1. + ++$numbersSeen; + + // 6.5.5.7. If numbersSeen is 2 or 4, then increase pieceIndex by 1. + if ($numbersSeen === 2 || $numbersSeen === 4) { + ++$pieceIndex; + } + } while ($iter->valid()); + + // 6.5.6. If numbersSeen is not 4, validation error, return failure. + if ($numbersSeen !== 4) { + // Validation error. + ($nullsafeVariable13 = $context->logger) ? $nullsafeVariable13->warning('IPv4-in-IPv6-too-few-parts', [ + 'input' => (string) $input, + 'column' => $iter->key() + 1, + ]) : null; + + return false; + } + $arrayIsListFunction = function (array $array) : bool { + if (function_exists('array_is_list')) { + return array_is_list($array); + } + if ($array === []) { + return true; + } + $current_key = 0; + foreach ($array as $key => $noop) { + if ($key !== $current_key) { + return false; + } + ++$current_key; + } + return true; + }; + + assert($arrayIsListFunction($address)); + + return [$address, $pieceIndex]; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/BrickMathAdapter.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/BrickMathAdapter.php new file mode 100644 index 0000000000..7d087a7380 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/BrickMathAdapter.php @@ -0,0 +1,108 @@ +number = BigInteger::fromBase($number, $base); + + return; + } + + $this->number = BigInteger::of($number); + } + + public function intdiv(int $number): NumberInterface + { + return new self($this->number->dividedBy($number, RoundingMode::FLOOR)); + } + + /** + * @param \Rowbot\URL\Component\Host\Math\NumberInterface $number + */ + public function isEqualTo($number): bool + { + if (!$number instanceof self) { + throw new MathException('Must be given an instance of itself.'); + } + + return $this->number->isEqualTo($number->number); + } + + public function isGreaterThan(int $number): bool + { + return $this->number->isGreaterThan($number); + } + + /** + * @param \Rowbot\URL\Component\Host\Math\NumberInterface $number + */ + public function isGreaterThanOrEqualTo($number): bool + { + if (!$number instanceof self) { + throw new MathException('Must be given an instance of itself.'); + } + + return $this->number->isGreaterThanOrEqualTo($number->number); + } + + public function mod(int $number): NumberInterface + { + return new self($this->number->mod($number)); + } + + public function multipliedBy(int $number): NumberInterface + { + return new self($this->number->multipliedBy($number)); + } + + /** + * @param \Rowbot\URL\Component\Host\Math\NumberInterface $number + */ + public function plus($number): NumberInterface + { + if (!$number instanceof self) { + throw new MathException('Must be given an instance of itself.'); + } + + return new self($this->number->plus($number->number)); + } + + public function pow(int $number): NumberInterface + { + return new self($this->number->power($number)); + } + + /** + * @return numeric-string + */ + public function __toString(): string + { + $str = (string) $this->number; + assert(is_numeric($str)); + + return $str; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/Exception/MathException.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/Exception/MathException.php new file mode 100644 index 0000000000..66ec16369c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/Exception/MathException.php @@ -0,0 +1,11 @@ +number = intval($number, $base); + } + + public function intdiv(int $number): NumberInterface + { + return new self((int) floor($this->number / $number)); + } + + /** + * @param \Rowbot\URL\Component\Host\Math\NumberInterface $number + */ + public function isEqualTo($number): bool + { + if (!$number instanceof self) { + throw new MathException('Must be given an instance of itself.'); + } + + return $this->number === $number->number; + } + + public function isGreaterThan(int $number): bool + { + return $this->number > $number; + } + + /** + * @param \Rowbot\URL\Component\Host\Math\NumberInterface $number + */ + public function isGreaterThanOrEqualTo($number): bool + { + if (!$number instanceof self) { + throw new MathException('Must be given an instance of itself.'); + } + + return $this->number >= $number->number; + } + + public function mod(int $number): NumberInterface + { + return new self($this->number % $number); + } + + public function multipliedBy(int $number): NumberInterface + { + return new self($this->number * $number); + } + + /** + * @param \Rowbot\URL\Component\Host\Math\NumberInterface $number + */ + public function plus($number): NumberInterface + { + if (!$number instanceof self) { + throw new MathException('Must be given an instance of itself.'); + } + + return new self($this->number + $number->number); + } + + public function pow(int $number): NumberInterface + { + return new self($this->number ** $number); + } + + /** + * @return numeric-string + */ + public function __toString(): string + { + return (string) $this->number; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/NumberFactory.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/NumberFactory.php new file mode 100644 index 0000000000..776ceb8e62 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Math/NumberFactory.php @@ -0,0 +1,30 @@ +address = $address; + } + + public function toFormattedString(): string + { + return $this->toString(); + } + + public function toString(): string + { + // 1. Let output be the empty string. + $output = ''; + + // 2. Let n be the value of address. + $number = NumberFactory::createNumber($this->address, 10); + + // 3. For each i in the range 1 to 4, inclusive: + for ($i = 0; $i < 4; ++$i) { + // 3.1. Prepend n % 256, serialized, to output. + $output = $number->mod(256) . $output; + + // 3.2. If i is not 4, then prepend U+002E (.) to output. + if ($i < 3) { + $output = '.' . $output; + } + + // 3.3. Set n to floor(n / 256). + $number = $number->intdiv(256); + } + + // 4. Return output. + return $output; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Serializer/IPv6AddressSerializer.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Serializer/IPv6AddressSerializer.php new file mode 100644 index 0000000000..ec8f9644f4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Serializer/IPv6AddressSerializer.php @@ -0,0 +1,121 @@ + + */ + private $address; + + /** + * @param non-empty-list $address + */ + public function __construct(array $address) + { + $this->address = $address; + } + + public function toFormattedString(): string + { + return '[' . $this->toString() . ']'; + } + + public function toString(): string + { + // 1. Let output be the empty string. + $output = ''; + + // 2. Let compress be an index to the first IPv6 piece in the first longest sequences of address’s IPv6 pieces + // that are 0. + // 3. If there is no sequence of address’s IPv6 pieces that are 0 that is longer than 1, then set compress to + // null. + // 4. Let ignore0 be false. + [$compress, $longestSequence] = $this->getCompressLocation(); + $pieceIndex = 0; + + // 5. For each pieceIndex in the range 0 to 7, inclusive: + do { + // 5.3. If compress is pieceIndex, then: + if ($compress === $pieceIndex) { + // 5.3.1. Let separator be "::" if pieceIndex is 0, and U+003A (:) otherwise. + // 5.3.2. Append separator to output. + $output .= $pieceIndex === 0 ? '::' : ':'; + + // 5.3.3. Set ignore0 to true and continue. + // + // Advance the pointer to $compress + $longestSequence + // to skip over all 16-bit pieces that are 0 that immediately + // follow the piece at $compress. + $pieceIndex = $compress + $longestSequence; + + continue; + } + + // 5.4. Append address[pieceIndex], represented as the shortest possible lowercase hexadecimal number, to + // output. + // + // Is it safe to assume this always returns lowercase letters? + $output .= dechex($this->address[$pieceIndex]); + + // 5.5. If pieceIndex is not 7, then append U+003A (:) to output. + if ($pieceIndex < self::MAX_SIZE - 1) { + $output .= ':'; + } + + ++$pieceIndex; + } while ($pieceIndex < self::MAX_SIZE); + + // 6. Return output. + return $output; + } + + /** + * Finds the longest sequence, with a length greater than 1, of 16-bit pieces that are 0 and + * sets $compress to the first 16-bit piece in that sequence, otherwise $compress will remain + * null. + * + * @return array{0: int|null, 1: int} The first item is the compress pointer, which indicates where in the address + * it can start compression, or null if the address isn't compressable. The + * second item is the length of the longest sequence of zeroes. + */ + private function getCompressLocation(): array + { + $longestSequence = 1; + $compress = null; + $i = 0; + + do { + if ($this->address[$i] !== 0) { + continue; + } + + $sequenceLength = 0; + + do { + ++$sequenceLength; + ++$i; + } while ($i < self::MAX_SIZE && $this->address[$i] === 0); + + // We are only interested in sequences with a length greater than one. We also only want + // to note the first of those sequences since there may be multiple sequences of zero + // that have the same length. + if ($sequenceLength > $longestSequence) { + $longestSequence = $sequenceLength; + $compress = $i - $sequenceLength; + } + } while (++$i < self::MAX_SIZE); + + return [$compress, $longestSequence]; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Serializer/StringHostSerializer.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Serializer/StringHostSerializer.php new file mode 100644 index 0000000000..61ef556000 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/Serializer/StringHostSerializer.php @@ -0,0 +1,28 @@ +string = $string; + } + + public function toFormattedString(): string + { + return $this->string; + } + + public function toString(): string + { + return $this->string; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/StringHost.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/StringHost.php new file mode 100644 index 0000000000..4a8dd01773 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Host/StringHost.php @@ -0,0 +1,40 @@ +string === $other->string; + } + + public function getSerializer(): HostSerializerInterface + { + return new StringHostSerializer($this->string); + } + + public function isLocalHost(): bool + { + return $this->string === 'localhost'; + } + + public function isNull(): bool + { + return false; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/OpaqueOrigin.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/OpaqueOrigin.php new file mode 100644 index 0000000000..8eec95b8c5 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/OpaqueOrigin.php @@ -0,0 +1,47 @@ +list); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/PathInterface.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/PathInterface.php new file mode 100644 index 0000000000..85f84fe3c4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/PathInterface.php @@ -0,0 +1,29 @@ +list[] = $path; + } + + public function shorten(Scheme $scheme): void + { + // 3. If url’s scheme is "file", path’s size is 1, and path[0] is a normalized Windows drive letter, then + // return. + if ($scheme->isFile() && count($this->list) === 1 && $this->list[0]->isNormalizedWindowsDriveLetter()) { + return; + } + + // 4. Remove path’s last item, if any. + array_pop($this->list); + } + + /** + * @see https://url.spec.whatwg.org/#url-path-serializer + */ + public function __toString(): string + { + if (!isset($this->list[0])) { + return ''; + } + + return '/' . implode('/', $this->list); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/PathSegment.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/PathSegment.php new file mode 100644 index 0000000000..eda1c1c414 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/PathSegment.php @@ -0,0 +1,33 @@ +string) === 2 + && strpbrk($this->string[0], CodePoint::ASCII_ALPHA_MASK) === $this->string[0] + && $this->string[1] === ':'; + } + + public function stripTrailingSpaces(): void + { + $this->string = rtrim($this->string, "\x20"); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/QueryList.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/QueryList.php new file mode 100644 index 0000000000..92619fb780 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/QueryList.php @@ -0,0 +1,341 @@ + + */ +class QueryList implements Countable, IteratorAggregate +{ + private const LEAD_OFFSET = 0xD800 - (0x10000 >> 10); + + /** + * @var array + */ + private $cache; + + /** + * @var array + */ + private $list; + + /** + * @param array $list + */ + public function __construct(array $list = []) + { + $this->list = $list; + $this->cache = []; + } + + /** + * Decodes a application/x-www-form-urlencoded string and returns the decoded pairs as a list. + * + * Note: A legacy server-oriented implementation might have to support encodings other than + * UTF-8 as well as have special logic for tuples of which the name is `_charset_`. Such logic + * is not described here as only UTF-8' is conforming. + * + * @see https://url.spec.whatwg.org/#concept-urlencoded-parser + */ + public static function fromString(string $input): self + { + // Let sequences be the result of splitting input on 0x26 (&). + $sequences = explode('&', $input); + + // Let output be an initially empty list of name-value tuples where both name and value + // hold a string. + $output = new self(); + + foreach ($sequences as $bytes) { + if ($bytes === '') { + continue; + } + + // If bytes contains a 0x3D (=), then let name be the bytes from the start of bytes up + // to but excluding its first 0x3D (=), and let value be the bytes, if any, after the + // first 0x3D (=) up to the end of bytes. If 0x3D (=) is the first byte, then name will + // be the empty byte sequence. If it is the last, then value will be the empty byte + // sequence. Otherwise, let name have the value of bytes and let value be the empty byte + // sequence. + $name = $bytes; + $value = ''; + + if (strpos($bytes, '=') !== false) { + [$name, $value] = explode('=', $bytes, 2); + } + + // Replace any 0x2B (+) in name and value with 0x20 (SP). + [$name, $value] = str_replace('+', "\x20", [$name, $value]); + + // Let nameString and valueString be the result of running UTF-8 + // decode without BOM on the percent decoding of name and value, + // respectively. + $output->append( + Utf8String::transcode(rawurldecode($name), 'utf-8', 'utf-8'), + Utf8String::transcode(rawurldecode($value), 'utf-8', 'utf-8') + ); + } + + return $output; + } + + /** + * Appends a new name-value pair to the list. + */ + public function append(string $name, string $value): void + { + $this->list[] = ['name' => $name, 'value' => $value]; + $this->cache[$name] = true; + } + + public function count(): int + { + return count($this->list); + } + + /** + * Determines if a name-value pair with name $name exists in the collection. + */ + public function contains(string $name, ?string $value = null): bool + { + $hasTuple = isset($this->cache[$name]); + + if ($value === null || !$hasTuple) { + return $hasTuple; + } + + foreach ($this->list as $tuple) { + if ($name === $tuple['name'] && $value === $tuple['value']) { + return true; + } + } + + return false; + } + + /** + * Returns a filtered array based on the given callback. + * + * @return array> + */ + public function filter(callable $callback): array + { + return array_filter($this->list, $callback === null ? function ($value, $key) : bool { + return !empty($value); + } : $callback, $callback === null ? ARRAY_FILTER_USE_BOTH : 0); + } + + /** + * Returns the first name-value pair in the list whose name is $name. + */ + public function first(string $name): ?string + { + foreach ($this->list as $pair) { + if ($pair['name'] === $name) { + return $pair['value']; + } + } + + return null; + } + + /** + * @return \ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->list); + } + + /** + * @return array{name: string, value: string}|null + */ + public function getTupleAt(int $index): ?array + { + return $this->list[$index] ?? null; + } + + /** + * Removes all name-value pairs with name $name from the list. + */ + public function remove(string $name, ?string $value): void + { + $seen = 0; + $removed = 0; + + for ($i = count($this->list) - 1; $i >= 0; --$i) { + if ($this->list[$i]['name'] === $name) { + ++$seen; + + if ($value !== null && $this->list[$i]['value'] !== $value) { + continue; + } + + array_splice($this->list, $i, 1); + ++$removed; + } + } + + if ($seen === $removed) { + unset($this->cache[$name]); + } + } + + /** + * Sets the value of the first name-value pair with $name to $value and + * removes all other occurances that have name $name. + */ + public function set(string $name, string $value): void + { + $prevIndex = null; + + for ($i = count($this->list) - 1; $i >= 0; --$i) { + if ($this->list[$i]['name'] === $name) { + if ($prevIndex !== null) { + array_splice($this->list, $prevIndex, 1); + } + + $prevIndex = $i; + } + } + + if ($prevIndex === null) { + return; + } + + $this->list[$prevIndex]['value'] = $value; + } + + /** + * Sorts the collection by code units and preserves the relative positioning + * of name-value pairs. + */ + public function sort(): void + { + $temp = []; + + foreach ($this->list as $pair) { + $codeUnits = $this->convertToCodeUnits($pair['name']); + $temp[] = ['original' => $pair, 'codeUnits' => $codeUnits, 'length' => count($codeUnits)]; + } + + // Sorting priority overview: + // + // Each string is compared code unit by code unit against each other. + // + // 1) If the two strings have different lengths, and the strings are equal up to the end of + // the shortest string, then the shorter of the two strings will be moved up in the + // array. (e.g. The string "aa" will come before the string "aaa".) + // 2) If the value of the code units differ, the character with the lower code unit will be + // moved up in the array. (e.g. "🌈" will come before "ffi". Although "🌈" has a code + // point value of 127,752 that is greater than the "ffi" code point value of 64,259, "🌈" + // is split in to 2 code units and it's first code unit has a value of 55,356, which is + // less than the "ffi" single code unit value of 64,259.) + // 3) If the two strings are considered equal, then they are sorted by the relative + // position in which they appeared in the array. (e.g. The string "b=c&a=c&b=a&a=a" + // becomes "a=c&a=a&b=c&b=a".) + usort($temp, static function (array $a, array $b): int { + $aCodeUnits = $a['codeUnits']; + $bCodeUnits = $b['codeUnits']; + $lengthComparison = $a['length'] <=> $b['length']; + + if ($lengthComparison === 0) { + return $aCodeUnits <=> $bCodeUnits; + } + + $shortestLength = $lengthComparison < 0 ? $a['length'] : $b['length']; + + for ($i = 0; $i < $shortestLength; ++$i) { + $comparison = $aCodeUnits[$i] <=> $bCodeUnits[$i]; + + if ($comparison !== 0) { + return $comparison; + } + } + + return $lengthComparison; + }); + + $this->list = array_column($temp, 'original'); + } + + /** + * Encodes the list of tuples as a valid application/x-www-form-urlencoded string. + * + * @see https://url.spec.whatwg.org/#concept-urlencoded-serializer + */ + public function toUrlencodedString(?string $encodingOverride = null): string + { + $encoding = EncodingHelper::getOutputEncoding($encodingOverride) ?? 'utf-8'; + $output = ''; + $percentEncoder = new PercentEncoder(); + + foreach ($this->list as $key => $tuple) { + $name = $percentEncoder->percentEncodeAfterEncoding( + $encoding, + $tuple['name'], + EncodeSet::X_WWW_URLENCODED, + true + ); + $value = $percentEncoder->percentEncodeAfterEncoding( + $encoding, + $tuple['value'], + EncodeSet::X_WWW_URLENCODED, + true + ); + + if ($key > 0) { + $output .= '&'; + } + + $output .= $name . '=' . $value; + } + + return $output; + } + + /** + * @see https://www.unicode.org/faq/utf_bom.html?source=post_page---------------------------#utf16-4 + * + * @return list> + */ + private function convertToCodeUnits(string $input): array + { + $codeUnits = []; + + foreach (mb_str_split($input, 1, 'utf-8') as $strCodePoint) { + $codePoint = mb_ord($strCodePoint, 'utf-8'); + + // Code points less than 0x10000 are part of the Basic Multilingual Plane and are + // represented by a single code unit that is equal to its code point. Use 0 as the low + // surrogate as the <=> operator compares array size first and values second. + $codeUnits[] = $codePoint < 0x10000 + ? [$codePoint, 0] + : [self::LEAD_OFFSET + ($codePoint >> 10), 0xDC00 + ($codePoint & 0x3FF)]; + } + + return $codeUnits; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Scheme.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Scheme.php new file mode 100644 index 0000000000..0478d6572f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/Scheme.php @@ -0,0 +1,69 @@ + 21, + 'file' => null, + 'http' => 80, + 'https' => 443, + 'ws' => 80, + 'wss' => 443, + ]; + /** + * @var string + */ + private $scheme; + public function __construct(string $scheme = '') + { + $this->scheme = $scheme; + } + public function isBlob(): bool + { + return $this->scheme === 'blob'; + } + public function isFile(): bool + { + return $this->scheme === 'file'; + } + public function isWebsocket(): bool + { + return $this->scheme === 'wss' || $this->scheme === 'ws'; + } + /** + * Returns whether or not the string is a special scheme. + * + * @see https://url.spec.whatwg.org/#is-special + */ + public function isSpecial(): bool + { + return array_key_exists($this->scheme, self::SPECIAL_SCHEMES); + } + public function isDefaultPort(?int $port): bool + { + if (!isset(self::SPECIAL_SCHEMES[$this->scheme])) { + return false; + } + + return self::SPECIAL_SCHEMES[$this->scheme] === $port; + } + public function equals(self $other): bool + { + return $this->scheme === $other->scheme; + } + public function __toString(): string + { + return $this->scheme; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/TupleOrigin.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/TupleOrigin.php new file mode 100644 index 0000000000..6e0724e2a0 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Component/TupleOrigin.php @@ -0,0 +1,106 @@ +domain = $domain; + $this->host = $host; + $this->port = $port; + $this->scheme = $scheme; + } + + public function getEffectiveDomain(): ?string + { + if ($this->domain !== null) { + return $this->domain; + } + + return $this->host->getSerializer()->toFormattedString(); + } + + public function isOpaque(): bool + { + return false; + } + + /** + * @param \Rowbot\URL\Origin $other + */ + public function isSameOrigin($other): bool + { + return $other instanceof self + && $this->scheme === $other->scheme + && $this->host->equals($other->host) + && $this->port === $other->port; + } + + /** + * @param \Rowbot\URL\Origin $other + */ + public function isSameOriginDomain($other): bool + { + // If A and B are both tuple origins... + if ($other instanceof self) { + // If A and B's schemes are identical, and their domains are + // identical and non-null, then return true. Otherwise, if A and B + // are same origin and their domains are identical and null, then + // return true. + if ($this->scheme === $other->scheme && $this->domain !== null && $this->domain === $other->domain) { + return true; + } + + if ($this->isSameOrigin($other) && $this->domain === null && $other->domain === null) { + return true; + } + } + + return false; + } + + /** + * @see https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin + */ + public function __toString(): string + { + $result = $this->scheme; + $result .= '://'; + $result .= $this->host->getSerializer()->toFormattedString(); + + if ($this->port !== null) { + $result .= ':' . $this->port; + } + + return $result; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Exception/TypeError.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Exception/TypeError.php new file mode 100644 index 0000000000..9e807d9857 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Exception/TypeError.php @@ -0,0 +1,9 @@ +input = $input; + $this->iter = $iter; + $this->buffer = $buffer; + $this->url = $url; + $this->base = $base; + $this->encoding = EncodingHelper::getOutputEncoding($encodingOverride) ?? 'utf-8'; + $this->state = $stateOverride ?? ParserState::SCHEME_START; + $this->stateOverride = $stateOverride; + $this->logger = $logger; + } + + /** + * Returns the output encoding of the URL string. + */ + public function getOutputEncoding(): string + { + return $this->encoding; + } + + /** + * Returns whether the parser's starting state was overriden. This occurs when using one of the + * URL object's setters. + */ + public function isStateOverridden(): bool + { + return $this->stateOverride !== null; + } + + /** + * Returns whether the parser's starting state was the Hostname state. + */ + public function isOverrideStateHostname(): bool + { + return $this->stateOverride === ParserState::HOSTNAME; + } + + /** + * Changes the encoding of the resulting URL string. This only affects the query string portion + * and it is only for use in the HTML specification. This should never be changed from the + * default UTF-8 encoding. + */ + public function setOutputEncoding(string $encoding): void + { + $this->encoding = EncodingHelper::getOutputEncoding($encoding) ?? 'utf-8'; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/ParserState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/ParserState.php new file mode 100644 index 0000000000..c9a8934e79 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/ParserState.php @@ -0,0 +1,100 @@ +isBracketOpen = false; + } + + public function handle(ParserContext $context, string $codePoint) + { + // 1. If state override is given and url’s scheme is "file", then decrease pointer by 1 and set state to file + // host state. + if ($context->isStateOverridden() && $context->url->scheme->isFile()) { + $context->iter->prev(); + $context->state = ParserState::FILE_HOST; + + return StatusCode::OK; + } + + do { + // 2. Otherwise, if c is U+003A (:) and insideBrackets is false, then: + if ($codePoint === ':' && !$this->isBracketOpen) { + // 2.1. If buffer is the empty string, validation error, return failure. + if ($context->buffer->isEmpty()) { + // Validation error. Return failure. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->warning('host-missing', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + return StatusCode::FAILURE; + } + + // 2.2. If state override is given and state override is hostname state, then return. + if ($context->isOverrideStateHostname()) { + return StatusCode::BREAK; + } + + // 2.3. Let host be the result of host parsing buffer with url is not special. + $parser = new HostParser(); + $host = $parser->parse($context, $context->buffer->toUtf8String(), !$context->url->scheme->isSpecial()); + + // 2.4. If host is failure, then return failure. + if ($host === false) { + return StatusCode::FAILURE; + } + + // 5. Set url’s host to host, buffer to the empty string, and state to port state. + $context->url->host = $host; + $context->buffer->clear(); + $context->state = ParserState::PORT; + + return StatusCode::OK; + } + + // 3. Otherwise, if one of the following is true: + // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#) + // - url is special and c is U+005C (\) + if ( + ( + $codePoint === CodePoint::EOF + || $codePoint === '/' + || $codePoint === '?' + || $codePoint === '#' + ) + || ($context->url->scheme->isSpecial() && $codePoint === '\\') + ) { + // then decrease pointer by 1, and then: + $context->iter->prev(); + + // 3.1. If url is special and buffer is the empty string, validation error, return failure. + if ($context->url->scheme->isSpecial() && $context->buffer->isEmpty()) { + // Validation error. Return failure. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->warning('host-missing', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 2, // Add 2 since we called ->prev() above + ]) : null; + + return StatusCode::FAILURE; + } + + // 3.2. Otherwise, if state override is given, buffer is the empty string, and either url includes + // credentials or url’s port is non-null, return. + if ( + $context->isStateOverridden() + && $context->buffer->isEmpty() + && ($context->url->includesCredentials() || $context->url->port !== null) + ) { + return StatusCode::BREAK; + } + + // 3.3. Let host be the result of host parsing buffer with url is not special. + $parser = new HostParser(); + $host = $parser->parse($context, $context->buffer->toUtf8String(), !$context->url->scheme->isSpecial()); + + // 3.4. If host is failure, then return failure. + if ($host === false) { + return StatusCode::FAILURE; + } + + // 3.5. Set url’s host to host, buffer to the empty string, and state to path start state. + $context->url->host = $host; + $context->buffer->clear(); + $context->state = ParserState::PATH_START; + + // 3.6. If state override is given, then return. + if ($context->isStateOverridden()) { + return StatusCode::BREAK; + } + + return StatusCode::OK; + } + + // 4. Otherwise: + // 4.1. If c is U+005B ([), then set insideBrackets to true. + if ($codePoint === '[') { + $this->isBracketOpen = true; + + // 4.2. If c is U+005D (]), then set insideBrackets to false. + } elseif ($codePoint === ']') { + $this->isBracketOpen = false; + } + + // 4.3. Append c to buffer. + $context->buffer->append($codePoint); + $context->iter->next(); + $codePoint = $context->iter->current(); + } while (true); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/AuthorityState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/AuthorityState.php new file mode 100644 index 0000000000..f35bcecdfb --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/AuthorityState.php @@ -0,0 +1,135 @@ +atTokenSeen = false; + $this->passwordTokenSeen = false; + } + + public function handle(ParserContext $context, string $codePoint) + { + do { + // 1. If c is U+0040 (@), then: + if ($codePoint === '@') { + // 1.1. Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-credentials', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + // 1.2. If atSignSeen is true, then prepend "%40" to buffer. + if ($this->atTokenSeen) { + $context->buffer->prepend('%40'); + } + + // 1.3. Set atSignSeen to true. + $this->atTokenSeen = true; + $username = ''; + $password = ''; + + // 1.4. For each codePoint in buffer: + foreach ($context->buffer as $bufferCodePoint) { + // 1.4.1. If codePoint is U+003A (:) and passwordTokenSeen is false, then set passwordTokenSeen to true + // and continue. + if ($bufferCodePoint === ':' && !$this->passwordTokenSeen) { + $this->passwordTokenSeen = true; + + continue; + } + + // 1.4.3. If passwordTokenSeen is true, then append encodedCodePoints to url’s password. + // 1.4.4. Otherwise, append encodedCodePoints to url’s username. + if ($this->passwordTokenSeen) { + $password .= $bufferCodePoint; + } else { + $username .= $bufferCodePoint; + } + } + + // 1.4.2. Let encodedCodePoints be the result of running UTF-8 percent-encode codePoint using the + // userinfo percent-encode set. + $percentEncoder = new PercentEncoder(); + $context->url->username .= $percentEncoder->percentEncodeAfterEncoding( + 'utf-8', + $username, + EncodeSet::USERINFO + ); + $context->url->password .= $percentEncoder->percentEncodeAfterEncoding( + 'utf-8', + $password, + EncodeSet::USERINFO + ); + + // 1.5. Set buffer to the empty string. + $context->buffer->clear(); + + $context->iter->next(); + $codePoint = $context->iter->current(); + + continue; + } + + // 2. Otherwise, if one of the following is true: + // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#) + // - url is special and c is U+005C (\) + if ( + ( + $codePoint === CodePoint::EOF + || $codePoint === '/' + || $codePoint === '?' + || $codePoint === '#' + ) + || ($context->url->scheme->isSpecial() && $codePoint === '\\') + ) { + // 2.1. If atSignSeen is true and buffer is the empty string, validation error, return failure. + if ($this->atTokenSeen && $context->buffer->isEmpty()) { + // Validation error. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->warning('host-missing', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + return StatusCode::FAILURE; + } + + // 2.2. Decrease pointer by the number of code points in buffer plus one, set buffer to the empty string, + // and set state to host state. + $context->iter->seek(-($context->buffer->length() + 1)); + $context->buffer->clear(); + $context->state = ParserState::HOST; + + return StatusCode::OK; + } + + // 3. Otherwise, append c to buffer. + $context->buffer->append($codePoint); + $context->iter->next(); + $codePoint = $context->iter->current(); + } while (true); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileHostState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileHostState.php new file mode 100644 index 0000000000..8fa80f1afc --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileHostState.php @@ -0,0 +1,100 @@ +iter->prev(); + + // 1.1. If state override is not given and buffer is a Windows drive letter, validation error, set state to + // path state. + if (!$context->isStateOverridden() && $context->buffer->isWindowsDriveLetter()) { + // Validation error + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('file-invalid-Windows-drive-letter-host', [ + 'input' => (string) $context->input, + 'column_range' => [$context->iter->key(), $context->iter->key() + $context->buffer->length()], + ]) : null; + $context->state = ParserState::PATH; + + return StatusCode::OK; + } + + // This is a (platform-independent) Windows drive letter quirk. $context->buffer is not reset + // here and instead used in the path state. + // + // 1.2. Otherwise, if buffer is the empty string, then: + if ($context->buffer->isEmpty()) { + // 1.2.1. Set url’s host to the empty string. + $context->url->host = new StringHost(); + + // 1.2.2. If state override is given, then return. + if ($context->isStateOverridden()) { + return StatusCode::BREAK; + } + + // 1.2.3. Set state to path start state. + $context->state = ParserState::PATH_START; + + return StatusCode::OK; + } + + // 1.3. Otherwise, run these steps: + // 1.3.1. Let host be the result of host parsing buffer with url is not special. + $parser = new HostParser(); + $host = $parser->parse($context, $context->buffer->toUtf8String(), !$context->url->scheme->isSpecial()); + + // 1.3.2. If host is failure, then return failure. + if ($host === false) { + return StatusCode::FAILURE; + } + + // 1.3.3. If host is "localhost", then set host to the empty string. + if ($host->isLocalHost()) { + $host = new StringHost(); + } + + // 1.3.4. Set url’s host to host. + $context->url->host = $host; + + // 1.3.5. If state override is given, then return. + if ($context->isStateOverridden()) { + return StatusCode::BREAK; + } + + // 1.3.6. Set buffer to the empty string and state to path start state. + $context->buffer->clear(); + $context->state = ParserState::PATH_START; + + return StatusCode::OK; + } + + // 2. Otherwise, append c to buffer. + $context->buffer->append($codePoint); + $context->iter->next(); + $codePoint = $context->iter->current(); + } while (true); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileSlashState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileSlashState.php new file mode 100644 index 0000000000..53415f05b4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileSlashState.php @@ -0,0 +1,59 @@ +logger) ? $nullsafeVariable1->notice('invalid-reverse-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + // 1.2. Set state to file host state. + $context->state = ParserState::FILE_HOST; + + return StatusCode::OK; + } + + // 2. Otherwise: + // 2.1. If base is non-null and base’s scheme is "file", then: + if ($context->base !== null && $context->base->scheme->isFile()) { + // 2.1.1. Set url’s host to base’s host. + $context->url->host = clone $context->base->host; + $path = $context->base->path->first(); + + // 2.1.2. If the substring from pointer in input does not start with a Windows drive letter and base’s + // path[0] is a normalized Windows drive letter, then append base’s path[0] to url’s path. + if ( + !$context->input->substr($context->iter->key())->startsWithWindowsDriveLetter() + && $path->isNormalizedWindowsDriveLetter() + ) { + // This is a (platform-independent) Windows drive letter quirk. Both url’s and + // base’s host are null under these conditions and therefore not copied. + $context->url->path->push($path); + } + } + + // 2.2. Set state to path state, and decrease pointer by 1. + $context->state = ParserState::PATH; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileState.php new file mode 100644 index 0000000000..858dd210e9 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FileState.php @@ -0,0 +1,108 @@ +url->scheme = new Scheme('file'); + + // 2. Set url’s host to the empty string. + $context->url->host = new StringHost(); + + // 3. If c is U+002F (/) or U+005C (\), then: + if ($codePoint === '/' || $codePoint === '\\') { + // 3.1. If c is U+005C (\), validation error. + if ($codePoint === '\\') { + // Validation error + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-reverse-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + // 3.2. Set state to file slash state. + $context->state = ParserState::FILE_SLASH; + + return StatusCode::OK; + } + + // 4. Otherwise, if base is non-null and base’s scheme is "file": + if ($context->base !== null && $context->base->scheme->isFile()) { + // 4.1. Set url’s host to base’s host, url’s path to a clone of base’s path, and url’s query to base’s + // query. + $context->url->host = clone $context->base->host; + $context->url->path = clone $context->base->path; + $context->url->query = $context->base->query; + + // 4.2. If c is U+003F (?), then set url’s query to the empty string and state to query state. + if ($codePoint === '?') { + $context->url->query = ''; + $context->state = ParserState::QUERY; + + return StatusCode::OK; + } + + // 4.3. Otherwise, if c is U+0023 (#), set url’s fragment to the empty string and state to fragment state. + if ($codePoint === '#') { + $context->url->fragment = ''; + $context->state = ParserState::FRAGMENT; + + return StatusCode::OK; + } + + // 4.4. Otherwise, if c is not the EOF code point: + if ($codePoint === CodePoint::EOF) { + return StatusCode::OK; + } + + // 4.4.1. Set url’s query to null. + $context->url->query = null; + + // This is a (platform-independent) Windows drive letter quirk. + // + // 4.4.2. If the substring from pointer in input does not start with a Windows drive letter, then shorten + // url’s path. + if (!$context->input->substr($context->iter->key())->startsWithWindowsDriveLetter()) { + $context->url->path->shorten($context->url->scheme); + + // 4.4.3. Otherwise: + } else { + // 4.4.3.1 Validation error. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->notice('file-invalid-Windows-drive-letter', [ + 'input' => (string) $context->input, + 'column_range' => [$context->iter->key() + 1, $context->iter->key() + 3], + ]) : null; + + // 4.4.3.2. Set url’s path to an empty list. + $context->url->path = new PathList(); + } + + // 4.4.4 Set state to path state and decrease pointer by 1. + $context->state = ParserState::PATH; + $context->iter->prev(); + + return StatusCode::OK; + } + + // 5. Otherwise, set state to path state, and decrease pointer by 1. + $context->state = ParserState::PATH; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FragmentState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FragmentState.php new file mode 100644 index 0000000000..4c1017dfd8 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/FragmentState.php @@ -0,0 +1,54 @@ +logger) ? $nullsafeVariable1->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + // 1.2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, validation error. + } elseif ( + $codePoint === '%' + && !$context->input->substr($context->iter->key() + 1)->startsWithTwoAsciiHexDigits() + ) { + // Validation error. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + $buffer .= $codePoint; + $context->iter->next(); + $codePoint = $context->iter->current(); + } + + // 1.3. UTF-8 percent-encode c using the fragment percent-encode set and append the result to url’s fragment. + $percentEncoder = new PercentEncoder(); + $context->url->fragment .= $percentEncoder->percentEncodeAfterEncoding('utf-8', $buffer, EncodeSet::FRAGMENT); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/HostState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/HostState.php new file mode 100644 index 0000000000..3a95127a32 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/HostState.php @@ -0,0 +1,9 @@ +base === null || ($context->base->path->isOpaque() && $codePoint !== '#')) { + // Validation error. Return failure. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->warning('missing-scheme-non-relative-URL', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + return StatusCode::FAILURE; + } + + // 2. Otherwise, if base has an opaque path and c is U+0023 (#), set url’s scheme to base’s scheme, url’s path + // to base’s path, url’s query to base’s query, url’s fragment to the empty string, and set state to fragment + // state. + if ($context->base->path->isOpaque() && $codePoint === '#') { + $context->url->scheme = clone $context->base->scheme; + $context->url->path = clone $context->base->path; + $context->url->query = $context->base->query; + $context->url->fragment = ''; + $context->state = ParserState::FRAGMENT; + + return StatusCode::OK; + } + + // 3. Otherwise, if base’s scheme is not "file", set state to relative state and decrease pointer by 1. + if (!$context->base->scheme->isFile()) { + $context->state = ParserState::RELATIVE; + $context->iter->prev(); + + return StatusCode::OK; + } + + // 4. Otherwise, set state to file state and decrease pointer by 1. + $context->state = ParserState::FILE; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/OpaquePathState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/OpaquePathState.php new file mode 100644 index 0000000000..554e8b1a9c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/OpaquePathState.php @@ -0,0 +1,77 @@ +url->query = ''; + $context->state = ParserState::QUERY; + + break; + } + + // 2. Otherwise, if c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. + if ($codePoint === '#') { + $context->url->fragment = ''; + $context->state = ParserState::FRAGMENT; + + break; + } + + // 3. Otherwise: + // 3.1. If c is not the EOF code point, not a URL code point, and not U+0025 (%), validation error. + if ($codePoint !== CodePoint::EOF && $codePoint !== '%' && !CodePoint::isUrlCodePoint($codePoint)) { + // Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + // 3.2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, validation error. + } elseif ( + $codePoint === '%' + && !$context->input->substr($context->iter->key() + 1)->startsWithTwoAsciiHexDigits() + ) { + // Validation error. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + // 3.3. If c is not the EOF code point, UTF-8 percent-encode c using the C0 control percent-encode set and + // append the result to url’s path. + if ($codePoint !== CodePoint::EOF) { + $percentEncoder = $percentEncoder ?? new PercentEncoder(); + $context->url->path->first()->append($percentEncoder->percentEncodeAfterEncoding( + 'utf-8', + $codePoint, + EncodeSet::C0_CONTROL + )); + } + + $context->iter->next(); + $codePoint = $context->iter->current(); + } while ($codePoint !== CodePoint::EOF); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathOrAuthorityState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathOrAuthorityState.php new file mode 100644 index 0000000000..c56acf1431 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathOrAuthorityState.php @@ -0,0 +1,30 @@ +state = ParserState::AUTHORITY; + + return StatusCode::OK; + } + + // 2. Otherwise, set state to path state, and decrease pointer by 1. + $context->state = ParserState::PATH; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathStartState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathStartState.php new file mode 100644 index 0000000000..7aa7a1e7a5 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathStartState.php @@ -0,0 +1,79 @@ +url->scheme->isSpecial()) { + // 1.1. If c is U+005C (\), validation error. + if ($codePoint === '\\') { + // Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-reverse-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + // 1.2. Set state to path state. + $context->state = ParserState::PATH; + + // 1.3. If c is neither U+002F (/) nor U+005C (\), then decrease pointer by 1. + if ($codePoint !== '/' && $codePoint !== '\\') { + $context->iter->prev(); + } + + return StatusCode::OK; + } + + // 2. Otherwise, if state override is not given and c is U+003F (?), set url’s query to the empty string and + // state to query state. + if (!$context->isStateOverridden() && $codePoint === '?') { + $context->url->query = ''; + $context->state = ParserState::QUERY; + + return StatusCode::OK; + } + + // 3. Otherwise, if state override is not given and c is U+0023 (#), set url’s fragment to the empty string and + // state to fragment state. + if (!$context->isStateOverridden() && $codePoint === '#') { + $context->url->fragment = ''; + $context->state = ParserState::FRAGMENT; + + return StatusCode::OK; + } + + // 4. Otherwise, if c is not the EOF code point: + if ($codePoint !== CodePoint::EOF) { + // 4.1. Set state to path state. + $context->state = ParserState::PATH; + + // 4.2. If c is not U+002F (/), then decrease pointer by 1. + if ($codePoint !== '/') { + $context->iter->prev(); + } + + return StatusCode::OK; + } + + // 5. Otherwise, if state override is given and url’s host is null, append the empty string to url’s path. + if ($context->isStateOverridden() && $context->url->host->isNull()) { + $context->url->path->push(new PathSegment()); + } + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathState.php new file mode 100644 index 0000000000..8f344b4c3e --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PathState.php @@ -0,0 +1,153 @@ + '', + '.%2e' => '', + '.%2E' => '', + '%2e.' => '', + '%2E.' => '', + '%2e%2e' => '', + '%2E%2E' => '', + '%2e%2E' => '', + '%2E%2e' => '', + ]; + + /** + * @see https://url.spec.whatwg.org/#single-dot-path-segment + */ + private const SINGLE_DOT_SEGMENT = [ + '.' => '', + '%2e' => '', + '%2E' => '', + ]; + + public function handle(ParserContext $context, string $codePoint) + { + $percentEncoder = null; + + do { + // 1. If one of the following is true: + // - c is the EOF code point or U+002F (/) + // - url is special and c is U+005C (\) + // - state override is not given and c is U+003F (?) or U+0023 (#) + if ( + $codePoint === CodePoint::EOF + || $codePoint === '/' + || ($context->url->scheme->isSpecial() && $codePoint === '\\') + || (!$context->isStateOverridden() && ($codePoint === '?' || $codePoint === '#')) + ) { + $urlIsSpecial = $context->url->scheme->isSpecial(); + + // 1.1. If url is special and c is U+005C (\), validation error. + if ($urlIsSpecial && $codePoint === '\\') { + // Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-reverse-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + $stringBuffer = (string) $context->buffer; + + // 1.2. If buffer is a double-dot path segment, then: + if (isset(self::DOUBLE_DOT_SEGMENT[$stringBuffer])) { + // 1.2.1. Shorten url’s path. + $context->url->path->shorten($context->url->scheme); + + // 1.2.2. If neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string + // to url’s path. + if ($codePoint !== '/' && !($urlIsSpecial && $codePoint === '\\')) { + $context->url->path->push(new PathSegment()); + } + + // 1.3. Otherwise, if buffer is a single-dot path segment and if neither c is U+002F (/), nor url is special + // and c is U+005C (\), append the empty string to url’s path. + } elseif ( + isset(self::SINGLE_DOT_SEGMENT[$stringBuffer]) + && $codePoint !== '/' + && !($urlIsSpecial && $codePoint === '\\') + ) { + $context->url->path->push(new PathSegment()); + + // 1.4. Otherwise, if buffer is not a single-dot path segment, then: + } elseif (!isset(self::SINGLE_DOT_SEGMENT[$stringBuffer])) { + // 1.4.1. If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then + // replace the second code point in buffer with U+003A (:). + if ( + $context->url->scheme->isFile() + && $context->url->path->isEmpty() + && $context->buffer->isWindowsDriveLetter() + ) { + // This is a (platform-independent) Windows drive letter quirk. + $context->buffer->setCodePointAt(1, ':'); + } + + // 1.4.2. Append buffer to url’s path. + $context->url->path->push($context->buffer->toPath()); + } + + // 1.5. Set buffer to the empty string. + $context->buffer->clear(); + + // 1.6. If c is U+003F (?), then set url’s query to the empty string and state to query state. + if ($codePoint === '?') { + $context->url->query = ''; + $context->state = ParserState::QUERY; + + // If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. + } elseif ($codePoint === '#') { + $context->url->fragment = ''; + $context->state = ParserState::FRAGMENT; + } + + return StatusCode::OK; + } + + // 2. Otherwise, run these steps: + // 2.1. If c is not a URL code point and not U+0025 (%), validation error. + if ($codePoint !== '%' && !CodePoint::isUrlCodePoint($codePoint)) { + // Validation error + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + // 2.2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, validation error. + } elseif ( + $codePoint === '%' + && !$context->input->substr($context->iter->key() + 1)->startsWithTwoAsciiHexDigits() + ) { + // Validation error + ($nullsafeVariable3 = $context->logger) ? $nullsafeVariable3->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + // 2.3. UTF-8 percent-encode c using the path percent-encode set and append the result to buffer. + $percentEncoder = $percentEncoder ?? new PercentEncoder(); + $context->buffer->append($percentEncoder->percentEncodeAfterEncoding('utf-8', $codePoint, EncodeSet::PATH)); + $context->iter->next(); + $codePoint = $context->iter->current(); + } while (true); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PortState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PortState.php new file mode 100644 index 0000000000..bb82a3c9bb --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/PortState.php @@ -0,0 +1,92 @@ +buffer->append($codePoint); + $context->iter->next(); + $codePoint = $context->iter->current(); + } + + // 2. Otherwise, if one of the following is true: + // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#) + // - url is special and c is U+005C (\) + // - state override is given + if ( + ( + $codePoint === CodePoint::EOF + || $codePoint === '/' + || $codePoint === '?' + || $codePoint === '#' + ) + || ($context->url->scheme->isSpecial() && $codePoint === '\\') + || $context->isStateOverridden() + ) { + // 2.1. If buffer is not the empty string, then: + if (!$context->buffer->isEmpty()) { + // 2.1.1. Let port be the mathematical integer value that is represented by buffer in radix-10 using + // ASCII digits for digits with values 0 through 9. + $port = $context->buffer->toInt(); + + // 2.1.2. If port is greater than 2 ^ 16 − 1, validation error, return failure. + if ($port > 2 ** 16 - 1) { + // Validation error. Return failure. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->warning('port-out-of-range', [ + 'input' => (string) $context->input, + 'column_range' => [ + $context->iter->key() - $context->buffer->length() + 1, + $context->iter->key(), + ], + ]) : null; + + return StatusCode::FAILURE; + } + + // 2.1.3. Set url’s port to null, if port is url’s scheme’s default port, and to port otherwise. + if ($context->url->scheme->isSpecial() && $context->url->scheme->isDefaultPort($port)) { + $context->url->port = null; + } else { + $context->url->port = $port; + } + + // 2.1.4. Set buffer to the empty string. + $context->buffer->clear(); + } + + // 2.2. If state override is given, then return. + if ($context->isStateOverridden()) { + return StatusCode::BREAK; + } + + // 2.3. Set state to path start state and decrease pointer by 1. + $context->state = ParserState::PATH_START; + $context->iter->prev(); + + return StatusCode::OK; + } + + // 3. Otherwise, validation error, return failure. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->warning('port-invalid', [ + 'input' => (string) $context->input, + 'column_range' => [$context->iter->key() - $context->buffer->length() + 1, $context->iter->key() + 1], + ]) : null; + + return StatusCode::FAILURE; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/QueryState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/QueryState.php new file mode 100644 index 0000000000..e8e2095cf3 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/QueryState.php @@ -0,0 +1,93 @@ +getOutputEncoding() !== 'utf-8' + && (!$context->url->scheme->isSpecial() || $context->url->scheme->isWebsocket()) + ) { + $context->setOutputEncoding('utf-8'); + } + + do { + // 2. If one of the following is true: + // - state override is not given and c is U+0023 (#) + // - c is the EOF code point + // then: + if (!$context->isStateOverridden() && $codePoint === '#' || $codePoint === CodePoint::EOF) { + // 2.1. Let queryPercentEncodeSet be the special-query percent-encode set if url is special; otherwise the + // query percent-encode set. + $queryPercentEncodeSet = $context->url->scheme->isSpecial() + ? EncodeSet::SPECIAL_QUERY + : EncodeSet::QUERY; + + // 2.2. Percent-encode after encoding, with encoding, buffer, and queryPercentEncodeSet, and append the + // result to url’s query. + // + // NOTE: This operation cannot be invoked code-point-for-code-point due to the stateful ISO-2022-JP encoder. + $percentEncoder = new PercentEncoder(); + $context->url->query .= $percentEncoder->percentEncodeAfterEncoding( + $context->getOutputEncoding(), + (string) $context->buffer, + $queryPercentEncodeSet + ); + + // 2.3. Set buffer to the empty string. + $context->buffer->clear(); + + // 2.4. If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. + if ($codePoint === '#') { + $context->url->fragment = ''; + $context->state = ParserState::FRAGMENT; + } + + return StatusCode::OK; + } + + // 3. Otherwise, if c is not the EOF code point: + // 3.1. If c is not a URL code point and not U+0025 (%), validation error. + if ($codePoint !== '%' && !CodePoint::isUrlCodePoint($codePoint)) { + // Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + // 3.2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, validation error. + } elseif ( + $codePoint === '%' + && !$context->input->substr($context->iter->key() + 1)->startsWithTwoAsciiHexDigits() + ) { + // Validation error. + ($nullsafeVariable2 = $context->logger) ? $nullsafeVariable2->notice('invalid-URL-unit', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + // 3.3. Append c to buffer. + $context->buffer->append($codePoint); + $context->iter->next(); + $codePoint = $context->iter->current(); + } while (true); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/RelativeSlashState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/RelativeSlashState.php new file mode 100644 index 0000000000..89b39b1775 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/RelativeSlashState.php @@ -0,0 +1,56 @@ +base !== null); + + // 1. If url is special and c is U+002F (/) or U+005C (\), then: + if ($context->url->scheme->isSpecial() && ($codePoint === '/' || $codePoint === '\\')) { + // 1.1. If c is U+005C (\), validation error. + if ($codePoint === '\\') { + // Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-reverse-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + } + + // 1.2. Set state to special authority ignore slashes state. + $context->state = ParserState::SPECIAL_AUTHORITY_IGNORE_SLASHES; + + return StatusCode::OK; + } + + // 2. Otherwise, if c is U+002F (/), then set state to authority state. + if ($codePoint === '/') { + $context->state = ParserState::AUTHORITY; + + return StatusCode::OK; + } + + // 3. Otherwise, set url’s username to base’s username, url’s password to base’s password, url’s host to base’s + // host, url’s port to base’s port, state to path state, and then, decrease pointer by 1. + $context->url->username = $context->base->username; + $context->url->password = $context->base->password; + $context->url->host = clone $context->base->host; + $context->url->port = $context->base->port; + $context->state = ParserState::PATH; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/RelativeState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/RelativeState.php new file mode 100644 index 0000000000..76112170a9 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/RelativeState.php @@ -0,0 +1,88 @@ +base !== null && !$context->base->scheme->isFile()); + + // 2. Set url’s scheme to base’s scheme. + $context->url->scheme = clone $context->base->scheme; + + // 3. If c is U+002F (/), then set state to relative slash state. + if ($codePoint === '/') { + $context->state = ParserState::RELATIVE_SLASH; + + return StatusCode::OK; + } + + // 4. Otherwise, if url is special and c is U+005C (\), validation error, set state to relative slash state. + if ($context->url->scheme->isSpecial() && $codePoint === '\\') { + // Validation error + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('invalid-reverse-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + $context->state = ParserState::RELATIVE_SLASH; + + return StatusCode::OK; + } + + // 5. Otherwise: + // 5.1. Set url’s username to base’s username, url’s password to base’s password, url’s host to base’s host, + // url’s port to base’s port, url’s path to a clone of base’s path, and url’s query to base’s query. + $context->url->username = $context->base->username; + $context->url->password = $context->base->password; + $context->url->host = clone $context->base->host; + $context->url->port = $context->base->port; + $context->url->path = clone $context->base->path; + $context->url->query = $context->base->query; + + // 5.2. If c is U+003F (?), then set url’s query to the empty string, and state to query state. + if ($codePoint === '?') { + $context->url->query = ''; + $context->state = ParserState::QUERY; + + return StatusCode::OK; + } + + // 5.3. Otherwise, if c is U+0023 (#), set url’s fragment to the empty string and state to fragment state. + if ($codePoint === '#') { + $context->url->fragment = ''; + $context->state = ParserState::FRAGMENT; + + return StatusCode::OK; + } + + // 5.4 Otherwise, if c is not the EOF code point: + if ($codePoint === CodePoint::EOF) { + return StatusCode::OK; + } + + // 5.4.1. Set url’s query to null. + $context->url->query = null; + + // 5.4.2. Shorten url’s path. + $context->url->path->shorten($context->url->scheme); + + // 5.4.3. Set state to path state and decrease pointer by 1. + $context->state = ParserState::PATH; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SchemeStartState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SchemeStartState.php new file mode 100644 index 0000000000..db2601a410 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SchemeStartState.php @@ -0,0 +1,43 @@ +buffer->append(strtolower($codePoint)); + $context->state = ParserState::SCHEME; + + return StatusCode::OK; + } + + // 2. Otherwise, if state override is not given, set state to no scheme state and decrease pointer by 1. + if (!$context->isStateOverridden()) { + $context->state = ParserState::NO_SCHEME; + $context->iter->prev(); + + return StatusCode::OK; + } + + // 3. Otherwise, return failure. + // + // Note: This indication of failure is used exclusively by the Location object's protocol + // attribute. + return StatusCode::FAILURE; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SchemeState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SchemeState.php new file mode 100644 index 0000000000..20802bbb57 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SchemeState.php @@ -0,0 +1,150 @@ +buffer->append(strtolower($codePoint)); + $context->iter->next(); + $codePoint = $context->iter->current(); + } + + // 2. Otherwise, if c is U+003A (:), then: + if ($codePoint === ':') { + $stateIsOverridden = $context->isStateOverridden(); + $bufferIsSpecialScheme = false; + $candidateScheme = $context->buffer->toScheme(); + + // 2.1. If state override is given, then: + if ($stateIsOverridden) { + $bufferIsSpecialScheme = $candidateScheme->isSpecial(); + $urlIsSpecial = $context->url->scheme->isSpecial(); + + // 2.1.1. If url’s scheme is a special scheme and buffer is not a special scheme, then return. + // 2.1.2. If url’s scheme is not a special scheme and buffer is a special scheme, then return. + if ($urlIsSpecial xor $bufferIsSpecialScheme) { + return StatusCode::BREAK; + } + + // 2.1.3. If url includes credentials or has a non-null port, and buffer is "file", then return. + if ( + $context->url->includesCredentials() + || ($context->url->port !== null && $candidateScheme->isFile()) + ) { + return StatusCode::BREAK; + } + + // 2.1.4. If url’s scheme is "file" and its host is an empty host, then return. + if ($context->url->scheme->isFile() && $context->url->host->isEmpty()) { + return StatusCode::BREAK; + } + } + + // 2.2. Set url’s scheme to buffer. + $context->url->scheme = $candidateScheme; + + // 2.3. If state override is given, then: + if ($stateIsOverridden) { + // 2.3.1. If url’s port is url’s scheme’s default port, then set url’s port to null. + if ($bufferIsSpecialScheme && $context->url->scheme->isDefaultPort($context->url->port)) { + $context->url->port = null; + } + + // 2.3.2. Return. + return StatusCode::BREAK; + } + + // 2.4. Set buffer to the empty string. + $context->buffer->clear(); + $urlIsSpecial = $context->url->scheme->isSpecial(); + + // 2.5. If url’s scheme is "file", then: + if ($context->url->scheme->isFile()) { + // 2.5.1. If remaining does not start with "//", validation error. + if ($context->iter->peek(2) !== '//') { + // Validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('special-scheme-missing-following-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 2, + ]) : null; + } + + // 2.5.2. Set state to file state. + $context->state = ParserState::FILE; + + // 2.6. Otherwise, if url is special, base is non-null, and base’s scheme is equal to url’s scheme, set + // state to special relative or authority state. + } elseif ( + $urlIsSpecial + && $context->base !== null + && $context->base->scheme->equals($context->url->scheme) + ) { + assert( + $context->base->scheme->isSpecial(), + 'base is special (and therefore does not have an opaque path)' + ); + $context->state = ParserState::SPECIAL_RELATIVE_OR_AUTHORITY; + + // 2.7. Otherwise, if url is special, set state to special authority slashes state. + } elseif ($urlIsSpecial) { + $context->state = ParserState::SPECIAL_AUTHORITY_SLASHES; + + // 2.8. Otherwise, if remaining starts with an U+002F (/), set state to path or authority state and + // increase pointer by 1. + } elseif ($context->iter->peek() === '/') { + $context->state = ParserState::PATH_OR_AUTHORITY; + $context->iter->next(); + + // 2.9. Otherwise, set url’s path to the empty string and set state to opaque path state. + } else { + $context->url->path = new OpaquePath(new PathSegment()); + $context->state = ParserState::OPAQUE_PATH; + } + + return StatusCode::OK; + } + + // 3. Otherwise, if state override is not given, set buffer to the empty string, state to no scheme state, and + // start over (from the first code point in input). + if (!$context->isStateOverridden()) { + $context->buffer->clear(); + $context->state = ParserState::NO_SCHEME; + + // Reset the pointer to point at the first code point. + $context->iter->rewind(); + + return StatusCode::CONTINUE; + } + + // 4. Otherwise, return failure. + // + // Note: This indication of failure is used exclusively by the Location object's protocol + // attribute. Furthermore, the non-failure termination earlier in this state is an + // intentional difference for defining that attribute. + return StatusCode::FAILURE; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialAuthorityIgnoreSlashesState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialAuthorityIgnoreSlashesState.php new file mode 100644 index 0000000000..800968975d --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialAuthorityIgnoreSlashesState.php @@ -0,0 +1,33 @@ +state = ParserState::AUTHORITY; + $context->iter->prev(); + + return StatusCode::OK; + } + + // 2. Otherwise, validation error. + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('special-scheme-missing-following-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialAuthoritySlashesState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialAuthoritySlashesState.php new file mode 100644 index 0000000000..2945fb45b1 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialAuthoritySlashesState.php @@ -0,0 +1,37 @@ +iter->peek() === '/') { + $context->state = ParserState::SPECIAL_AUTHORITY_IGNORE_SLASHES; + $context->iter->next(); + + return StatusCode::OK; + } + + // 2. Otherwise, validation error, set state to special authority ignore slashes state and decrease pointer + // by 1. + $context->state = ParserState::SPECIAL_AUTHORITY_IGNORE_SLASHES; + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('special-scheme-missing-following-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialRelativeOrAuthorityState.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialRelativeOrAuthorityState.php new file mode 100644 index 0000000000..83ec97a50a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/SpecialRelativeOrAuthorityState.php @@ -0,0 +1,36 @@ +iter->peek() === '/') { + $context->state = ParserState::SPECIAL_AUTHORITY_IGNORE_SLASHES; + $context->iter->next(); + + return StatusCode::OK; + } + + // 2. Otherwise, validation error, set state to relative state and decrease pointer by 1. + $context->state = ParserState::RELATIVE; + ($nullsafeVariable1 = $context->logger) ? $nullsafeVariable1->notice('special-scheme-missing-following-solidus', [ + 'input' => (string) $context->input, + 'column' => $context->iter->key() + 1, + ]) : null; + $context->iter->prev(); + + return StatusCode::OK; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/State.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/State.php new file mode 100644 index 0000000000..c4da6b93c9 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/State/State.php @@ -0,0 +1,11 @@ + + */ +abstract class AbstractStringBuffer implements IteratorAggregate +{ + /** + * @var string + */ + protected $string; + + public function __construct(string $string = '') + { + $this->string = $string; + } + + public function append(string $string): void + { + $this->string .= $string; + } + + public function clear(): void + { + $this->string = ''; + } + + public function getIterator(): StringIteratorInterface + { + return new Utf8StringIterator($this->string); + } + + public function isEmpty(): bool + { + return $this->string === ''; + } + + public function length(): int + { + return mb_strlen($this->string, 'utf-8'); + } + + public function prepend(string $string): void + { + $this->string = $string . $this->string; + } + + public function setCodePointAt(int $index, string $codePoint): void + { + $prefix = mb_substr($this->string, 0, $index, 'utf-8'); + $suffix = mb_substr($this->string, $index + 1, null, 'utf-8'); + $this->string = $prefix . $codePoint . $suffix; + } + + public function toUtf8String(): USVStringInterface + { + return new Utf8String($this->string); + } + + public function __toString(): string + { + return $this->string; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/AbstractStringList.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/AbstractStringList.php new file mode 100644 index 0000000000..237a5773e3 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/AbstractStringList.php @@ -0,0 +1,103 @@ + + */ + protected $list; + + /** + * @param array $list + */ + public function __construct(array $list = []) + { + $this->list = $list; + } + + public function count(): int + { + return count($this->list); + } + + /** + * @return T + */ + public function first() + { + if (!isset($this->list[0])) { + throw new UndefinedIndexException(); + } + + return $this->list[0]; + } + + public function isEmpty(): bool + { + return $this->list === []; + } + + /** + * @return T + */ + public function last() + { + $last = count($this->list) - 1; + + if ($last < 0) { + throw new UndefinedIndexException(); + } + + return $this->list[$last]; + } + + /** + * @return T|null + */ + public function pop() + { + return array_pop($this->list); + } + + /** + * @param T $string + */ + public function push($string): void + { + $this->list[] = $string; + } + + /** + * @return \Generator + */ + public function getIterator(): Generator + { + foreach ($this->list as $string) { + yield $string; + } + } + + public function __clone() + { + $temp = []; + + foreach ($this->list as $string) { + $temp[] = clone $string; + } + + $this->list = $temp; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/AbstractUSVString.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/AbstractUSVString.php new file mode 100644 index 0000000000..5a1a774824 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/AbstractUSVString.php @@ -0,0 +1,151 @@ +string = $string; + } + + public function append(string $string): USVStringInterface + { + $copy = clone $this; + $copy->string .= $string; + + return $copy; + } + + public function endsWith(string $string): bool + { + return substr_compare($this->string, $string, -strlen($string)) === 0; + } + + public function getIterator(): StringIteratorInterface + { + return new Utf8StringIterator($this->string); + } + + public function isEmpty(): bool + { + return $this->string === ''; + } + + public function length(): int + { + return mb_strlen($this->string, 'utf-8'); + } + + public function matches(string $pattern, ?array &$matches = null, int $flags = 0, int $offset = 0): bool + { + $result = preg_match($pattern, $this->string, $matches, $flags, $offset); + + if ($result === false) { + throw new RegexException(sprintf('preg_match encountered an error with message "%s".', preg_last_error_msg())); + } + + return $result === 1; + } + + public function replaceRegex( + string $pattern, + string $replacement, + int $limit = -1, + int &$count = 0 + ): USVStringInterface { + $result = preg_replace($pattern, $replacement, $this->string, $limit, $count); + + if ($result === null) { + throw new RegexException(sprintf('preg_replace encountered an error with message "%s".', preg_last_error_msg())); + } + + $copy = clone $this; + $copy->string = $result; + + return $copy; + } + + public function split(string $delimiter, ?int $limit = null): StringListInterface + { + if ($delimiter === '') { + return new StringList(); + } + + /** @var non-empty-list $list */ + $list = explode($delimiter, $this->string, $limit ?? PHP_INT_MAX); + $temp = []; + + foreach ($list as $string) { + $copy = clone $this; + $copy->string = $string; + $temp[] = $copy; + } + + return new StringList($temp); + } + + public function startsWith(string $string): bool + { + return strncmp($this->string, $string, strlen($string)) === 0; + } + + public function startsWithTwoAsciiHexDigits(): bool + { + if (!isset($this->string[1])) { + return false; + } + + return strspn($this->string, CodePoint::HEX_DIGIT_MASK, 0, 2) === 2; + } + + /** + * @see https://url.spec.whatwg.org/#start-with-a-windows-drive-letter + */ + public function startsWithWindowsDriveLetter(): bool + { + return preg_match('/^[A-Za-z][:|](?:$|[\/\\\?#])/u', $this->string) === 1; + } + + public function substr(int $start, ?int $length = null): USVStringInterface + { + $copy = clone $this; + $copy->string = mb_substr($this->string, $start, $length, 'utf-8'); + + return $copy; + } + + public function toInt(int $base = 10): int + { + return intval($this->string, $base); + } + + public function __toString(): string + { + return $this->string; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/CodePoint.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/CodePoint.php new file mode 100644 index 0000000000..4a76092bd5 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/CodePoint.php @@ -0,0 +1,86 @@ += '&' && $codePoint <= '/') + || ($codePoint >= "\xA0" && $codePoint <= "\u{10FFFD}") + ) + + // Not a surrogate + && ($codePoint < "\u{D800}" || $codePoint > "\u{DFFF}") + + // Not a non-character + && ($codePoint < "\u{FDD0}" || $codePoint > "\u{FDEF}") + && !in_array($codePoint, [ + "\u{FFFE}", + "\u{FFFF}", + "\u{1FFFE}", + "\u{1FFFF}", + "\u{2FFFE}", + "\u{2FFFF}", + "\u{3FFFE}", + "\u{3FFFF}", + "\u{4FFFE}", + "\u{4FFFF}", + "\u{5FFFE}", + "\u{5FFFF}", + "\u{6FFFE}", + "\u{6FFFF}", + "\u{7FFFE}", + "\u{7FFFF}", + "\u{8FFFE}", + "\u{8FFFF}", + "\u{9FFFE}", + "\u{9FFFF}", + "\u{AFFFE}", + "\u{AFFFF}", + "\u{BFFFE}", + "\u{BFFFF}", + "\u{CFFFE}", + "\u{CFFFF}", + "\u{DFFFE}", + "\u{DFFFF}", + "\u{EFFFE}", + "\u{EFFFF}", + "\u{FFFFE}", + "\u{FFFFF}", + "\u{10FFFE}", + "\u{10FFFF}", + ], true); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/EncodeSet.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/EncodeSet.php new file mode 100644 index 0000000000..ec86034794 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/EncodeSet.php @@ -0,0 +1,17 @@ +randomBytes = null; + } + + /** + * While letting mbstring do the text encoding works in the majority of cases, it isn't perfect due to differences + * in the encoders for mbstring and those defined by the WHATWG encoding standard. This is the best we can do + * without actually implementing custom encoders from the WHATWG standard. Mbstring's lack of granular error + * handling also makes this more complex than it would otherwise need to be. + * + * @see https://url.spec.whatwg.org/#string-percent-encode-after-encoding + * + * @param string $encoding Output encoding + * @param string $input UTF-8 encoded string + * @param \Rowbot\URL\String\EncodeSet::* $percentEncodeSet + */ + public function percentEncodeAfterEncoding( + string $encoding, + string $input, + $percentEncodeSet, + bool $spaceAsPlus = false + ): string { + // 1. Let encoder be the result of getting an encoder from encoding. + $encoder = EncodingHelper::getOutputEncoding($encoding) ?? 'utf-8'; + + // 3. Let output be the empty string. + $output = ''; + + // Avoid the costly encoding conversion and error handling in the common case of UTF-8 as it is only relevant + // for non-UTF-8 strings. + if (strcasecmp($encoder, 'utf-8') !== 0) { + // Generate a random string to be used as a placeholder, only changing it if the given input contains the + // same sequence of bytes. + while ($this->randomBytes === null || strpos($input, $this->randomBytes) !== false) { + $this->randomBytes = bin2hex(random_bytes(16)); + } + + // Replace any existing numeric entities, that are in the hexadecimal format, so that we can distinguish + // encoding errors below. These will be reinserted later. + $replacedEntities = 0; + $input = preg_replace( + '/&#x([[:xdigit:]]{2,6};?)/', + '__' . $this->randomBytes . '_${1}__', + $input, + -1, + $replacedEntities + ); + + if ($input === null) { + throw new RegexException(sprintf( + 'preg_replace encountered an error with message "%s".', + preg_last_error_msg() + )); + } + + // 5.1. Let encodeOutput be an empty I/O queue. + // 5.2. Set potentialError to the result of running encode or fail with inputQueue, encoder, and + // encodeOutput. + $substituteChar = mb_substitute_character(); + mb_substitute_character('entity'); + $encodeOutput = mb_convert_encoding($input, $encoder, 'utf-8'); + mb_substitute_character($substituteChar); + + $chunks = preg_split('/&#x([[:xdigit:]]{2,6});/', $encodeOutput, -1, PREG_SPLIT_DELIM_CAPTURE); + + if ($chunks === false) { + throw new RegexException(sprintf( + 'preg_split encountered an error with message "%s".', + preg_last_error_msg() + )); + } + + // Replace the inserted placeholders of original numeric entities with the original text, so they get + // percent encoded. + if ($replacedEntities > 0) { + $chunks = preg_replace("/__{$this->randomBytes}_([[:xdigit:]]+;?)__/", '&#x${1}', $chunks); + + if ($chunks === null) { + throw new RegexException(sprintf( + 'preg_replace encountered an error with message "%s".', + preg_last_error_msg() + )); + } + } + } + + $chunks = $chunks ?? [$input]; + + foreach ($chunks as $key => $bytes) { + // 5.4. If potentialError is non-null, then append "%26%23", followed by the shortest sequence of ASCII + // digits representing potentialError in base ten, followed by "%3B", to output. + // + // NOTE: This can happen when encoding is not UTF-8. + // + // Because we are splitting using the PREG_SPLIT_DELIM_CAPTURE_FLAG, odd keys contain the hex number of the + // numeric entity inserted during encoding conversion should an invalid character be encountered. + if ($key % 2 === 1) { + $output .= '%26%23' . hexdec($bytes) . '%3B'; + + continue; + } + + // 5.3. For each byte of encodeOutput converted to a byte sequence: + for ($i = 0, $length = strlen($bytes); $i < $length; ++$i) { + // 5.3.1. If spaceAsPlus is true and byte is 0x20 (SP), then append U+002B (+) to output and continue. + if ($spaceAsPlus && $bytes[$i] === "\x20") { + $output .= '+'; + + continue; + } + + // 5.3.2. Let isomorph be a code point whose value is byte’s value. + $isomorph = ord($bytes[$i]); + + // 5.3.4. If isomorph is not in percentEncodeSet, then append isomorph to output. + if (!$this->inEncodeSet($isomorph, $percentEncodeSet)) { + $output .= $bytes[$i]; + + continue; + } + + // 5.3.5. Otherwise, percent-encode byte and append the result to output. + $output .= rawurlencode($bytes[$i]); + } + } + + // 6. Return output. + return $output; + } + + /** + * @see https://url.spec.whatwg.org/#c0-control-percent-encode-set + * @see https://url.spec.whatwg.org/#fragment-percent-encode-set + * @see https://url.spec.whatwg.org/#query-percent-encode-set + * @see https://url.spec.whatwg.org/#special-query-percent-encode-set + * @see https://url.spec.whatwg.org/#path-percent-encode-set + * @see https://url.spec.whatwg.org/#userinfo-percent-encode-set + * @see https://url.spec.whatwg.org/#component-percent-encode-set + * @param \Rowbot\URL\String\EncodeSet::* $percentEncodeSet + */ + private function inEncodeSet(int $codePoint, $percentEncodeSet): bool + { + switch ($percentEncodeSet) { + case EncodeSet::X_WWW_URLENCODED: + switch ($codePoint) { + case ord('!'): + case ord('\''): + case ord('('): + case ord(')'): + case ord('~'): + return true; + } + + // no break + + case EncodeSet::COMPONENT: + switch ($codePoint) { + case ord('$'): + case ord('%'): + case ord('&'): + case ord('+'): + case ord(','): + return true; + } + + // no break + + case EncodeSet::USERINFO: + switch ($codePoint) { + case ord('/'): + case ord(':'): + case ord(';'): + case ord('='): + case ord('@'): + case ord('['): + case ord('\\'): + case ord(']'): + case ord('^'): + case ord('|'): + return true; + } + + // no break + + case EncodeSet::PATH: + switch ($codePoint) { + case ord('?'): + case ord('`'): + case ord('{'): + case ord('}'): + return true; + + default: + $percentEncodeSet = EncodeSet::QUERY; + } + } + + switch ($percentEncodeSet) { + case EncodeSet::SPECIAL_QUERY: + if ($codePoint === ord('\'')) { + return true; + } + + // no break + + case EncodeSet::QUERY: + switch ($codePoint) { + case ord("\x20"): + case ord('"'): + case ord('#'): + case ord('<'): + case ord('>'): + return true; + } + + break; + + case EncodeSet::FRAGMENT: + switch ($codePoint) { + case ord("\x20"): + case ord('"'): + case ord('<'): + case ord('>'): + case ord('`'): + return true; + } + } + + // C0_CONTROL + return $codePoint < 0x20 || $codePoint > 0x7E; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringBuffer.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringBuffer.php new file mode 100644 index 0000000000..a258567ef5 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringBuffer.php @@ -0,0 +1,34 @@ +string) === 1; + } + + public function toInt(int $base = 10): int + { + return intval($this->string, $base); + } + + public function toPath(): PathSegment + { + return new PathSegment($this->string); + } + + public function toScheme(): Scheme + { + return new Scheme($this->string); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringBufferInterface.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringBufferInterface.php new file mode 100644 index 0000000000..59b659dc5c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringBufferInterface.php @@ -0,0 +1,42 @@ + + */ +interface StringBufferInterface extends IteratorAggregate +{ + public function append(string $string): void; + + public function clear(): void; + + public function isEmpty(): bool; + + /** + * @see https://url.spec.whatwg.org/#windows-drive-letter + */ + public function isWindowsDriveLetter(): bool; + + public function length(): int; + + public function prepend(string $string): void; + + public function setCodePointAt(int $index, string $codePoint): void; + + public function toInt(int $base = 10): int; + + public function toPath(): PathSegment; + + public function toScheme(): Scheme; + + public function toUtf8String(): USVStringInterface; + + public function __toString(): string; +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringIteratorInterface.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringIteratorInterface.php new file mode 100644 index 0000000000..3de3870b83 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringIteratorInterface.php @@ -0,0 +1,33 @@ + + */ +interface StringIteratorInterface extends SeekableIterator +{ + public function current(): string; + + public function key(): int; + + public function next(): void; + + public function peek(int $count = 1): string; + + public function prev(): void; + + /** + * @param int $position The position to seek to relative to the current position. + */ + + public function seek($position): void; + + public function rewind(): void; + + public function valid(): bool; +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringList.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringList.php new file mode 100644 index 0000000000..83bf91131f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringList.php @@ -0,0 +1,12 @@ + + */ +class StringList extends AbstractStringList implements StringListInterface +{ +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringListInterface.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringListInterface.php new file mode 100644 index 0000000000..48dca21490 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/StringListInterface.php @@ -0,0 +1,36 @@ + + */ +interface StringListInterface extends Countable, IteratorAggregate +{ + /** + * @return \Rowbot\URL\String\USVStringInterface + */ + public function first(); + + public function isEmpty(): bool; + + /** + * @return \Rowbot\URL\String\USVStringInterface + */ + public function last(); + + /** + * @return \Rowbot\URL\String\USVStringInterface|null + */ + public function pop(); + + /** + * @param \Rowbot\URL\String\USVStringInterface $item + */ + public function push($item): void; +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/USVStringInterface.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/USVStringInterface.php new file mode 100644 index 0000000000..e2c815ed39 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/USVStringInterface.php @@ -0,0 +1,53 @@ + + */ +interface USVStringInterface extends IteratorAggregate +{ + public function append(string $string): self; + + public function endsWith(string $string): bool; + + public function getIterator(): StringIteratorInterface; + + public function isEmpty(): bool; + + public function length(): int; + + /** + * @param array $matches + * @param int-mask<0, 256, 512> $flags + */ + public function matches(string $pattern, ?array &$matches = null, int $flags = 0, int $offset = 0): bool; + + public function replaceRegex( + string $pattern, + string $replacement, + int $limit = -1, + int &$count = 0 + ): self; + + public function split(string $delimiter, ?int $limit = null): StringListInterface; + + public function startsWith(string $string): bool; + + public function startsWithTwoAsciiHexDigits(): bool; + + /** + * @see https://url.spec.whatwg.org/#start-with-a-windows-drive-letter + */ + public function startsWithWindowsDriveLetter(): bool; + + public function substr(int $start, ?int $length = null): self; + + public function toInt(int $base = 10): int; + + public function __toString(): string; +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/Utf8String.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/Utf8String.php new file mode 100644 index 0000000000..261170baad --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/String/Utf8String.php @@ -0,0 +1,48 @@ + + */ + private $codePoints; + + /** + * @var int + */ + private $cursor; + + public function __construct(string $string) + { + $this->codePoints = mb_str_split($string, 1, 'utf-8'); + $this->cursor = 0; + } + + public function current(): string + { + return $this->codePoints[$this->cursor] ?? ''; + } + + public function key(): int + { + return $this->cursor; + } + + public function next(): void + { + ++$this->cursor; + } + + public function peek(int $count = 1): string + { + if ($count === 1) { + return $this->codePoints[$this->cursor + 1] ?? ''; + } + + $output = ''; + $cursor = $this->cursor + 1; + + for ($i = 0; $i < $count; ++$i) { + if (!isset($this->codePoints[$cursor])) { + break; + } + + $output .= $this->codePoints[$cursor]; + ++$cursor; + } + + return $output; + } + + public function prev(): void + { + --$this->cursor; + } + + public function rewind(): void + { + $this->cursor = 0; + } + + public function seek($position): void + { + $this->cursor += $position; + } + + public function valid(): bool + { + return $this->cursor > -1 && isset($this->codePoints[$this->cursor]); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Support/EncodingHelper.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Support/EncodingHelper.php new file mode 100644 index 0000000000..e3282f3f88 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/Support/EncodingHelper.php @@ -0,0 +1,38 @@ +logger = null; + + if (isset($options['logger'])) { + if (!$options['logger'] instanceof LoggerInterface) { + throw new TypeError(sprintf( + 'The passed logger must be null or an instance of %s', + LoggerInterface::class + )); + } + + $this->logger = $options['logger']; + } + + // 1. Let parsedURL be the result of running the API URL parser on url with base, if given. + $parsedURL = self::parseURL($url, $base, $this->logger); + + if ($parsedURL->error === APIParserErrorType::NONE) { + // 3. Initialize this with parsedURL. + assert($parsedURL->url !== null); + self::initializeURL($this, $parsedURL->url); + + return; + } + + switch ($parsedURL->error) { + case APIParserErrorType::BASE: + $message = 'Invalid base URL'; + break; + case APIParserErrorType::URL: + $message = 'Invalid URL'; + break; + } + + throw new TypeError($message); + } + + /** + * @see https://url.spec.whatwg.org/#dom-url-parse + * @param string|\Stringable $url + * @param string|\Stringable|null $base + */ + public static function parse($url, $base = null): ?self + { + try { + return new self($url, $base); + } catch (Throwable $exception) { + return null; + } + } + + /** + * @see https://url.spec.whatwg.org/#dom-url-canparse + * @param string|\Stringable $url + * @param string|\Stringable|null $base + */ + public static function canParse($url, $base = null): bool + { + $parsedURL = self::parseURL($url, $base); + + return $parsedURL->error === APIParserErrorType::NONE; + } + + public function toString(): string + { + return $this->url->serializeURL(); + } + + /** + * Returns a JSON encoded string without escaping forward slashes. If you + * need forward slashes to be escaped, pass the URL object to json_encode() + * instead of calling this method. + * + * @see https://url.spec.whatwg.org/#dom-url-tojson + */ + public function toJSON(): string + { + // Use JSON_UNESCAPED_SLASHES here since JavaScript's JSON.stringify() + // method does not escape forward slashes by default. + return json_encode($this->url->serializeURL(), JSON_UNESCAPED_SLASHES); + } + + /** + * Returns the serialized URL for consumption by json_encode(). To match + * JavaScript's behavior, you should pass the JSON_UNESCAPED_SLASHES option + * to json_encode(). + */ + public function jsonSerialize(): string + { + return $this->url->serializeURL(); + } + + /** + * @see https://url.spec.whatwg.org/#api-url-parser + * @param string|\Stringable $url + * @param string|\Stringable|null $base + */ + private static function parseURL( + $url, + $base = null, + ?LoggerInterface $logger = null + ): APIParserResult { + // 1. Let parsedBase be null. + $parsedBase = null; + $parser = new BasicURLParser($logger); + + // 2. If base is non-null: + if ($base !== null) { + // 2.1. Set parsedBase to the result of running the basic URL parser on base. + $stringBase = (string) $base; + $parsedBase = $parser->parse(Utf8String::fromUnsafe($stringBase)); + + // 2.2. If parsedBase is failure, then return failure. + if ($parsedBase === false) { + return new APIParserResult(null, APIParserErrorType::BASE); + } + } + + // 3. Return the result of running the basic URL parser on url with parsedBase. + $stringURL = (string) $url; + $parsedURL = $parser->parse(Utf8String::fromUnsafe($stringURL), $parsedBase); + + if ($parsedURL === false) { + return new APIParserResult(null, APIParserErrorType::URL); + } + + return new APIParserResult($parsedURL, APIParserErrorType::NONE); + } + + /** + * @see https://url.spec.whatwg.org/#url-initialize + */ + private static function initializeURL(self $url, URLRecord $urlRecord): void + { + // 1. Let query be urlRecord’s query, if that is non-null; otherwise the empty string. + $query = $urlRecord->query ?? ''; + + // 2. Set url’s URL to urlRecord. + $url->url = $urlRecord; + + // 3. Set url’s query object to a new URLSearchParams object. + $url->queryObject = new URLSearchParams(); + + // 4. Initialize url’s query object with query. + $url->queryObject->setList(QueryList::fromString($query)); + + // 5. Set url’s query object’s URL object to url. + $url->queryObject->setUrl($urlRecord); + } + + public function __clone() + { + $this->url = clone $this->url; + $this->queryObject = clone $this->queryObject; + $this->queryObject->setUrl($this->url); + } + + /** + * @throws \InvalidArgumentException When an invalid $name value is passed. + * @return string|\Rowbot\URL\URLSearchParams + */ + public function __get(string $name) + { + if ($name === 'hash') { + if ($this->url->fragment === null || $this->url->fragment === '') { + return ''; + } + + return '#' . $this->url->fragment; + } + + if ($name === 'host') { + if ($this->url->host->isNull()) { + return ''; + } + + $serializer = $this->url->host->getSerializer(); + + if ($this->url->port === null) { + return $serializer->toFormattedString(); + } + + return $serializer->toFormattedString() . ':' . $this->url->port; + } + + if ($name === 'hostname') { + if ($this->url->host->isNull()) { + return ''; + } + + return $this->url->host->getSerializer()->toFormattedString(); + } + + if ($name === 'href') { + return $this->url->serializeURL(); + } + + if ($name === 'origin') { + return (string) $this->url->getOrigin(); + } + + if ($name === 'password') { + return $this->url->password; + } + + if ($name === 'pathname') { + return (string) $this->url->path; + } + + if ($name === 'port') { + if ($this->url->port === null) { + return ''; + } + + return (string) $this->url->port; + } + + if ($name === 'protocol') { + return $this->url->scheme . ':'; + } + + if ($name === 'search') { + if ($this->url->query === null || $this->url->query === '') { + return ''; + } + + return '?' . $this->url->query; + } + + if ($name === 'searchParams') { + return $this->queryObject; + } + + if ($name === 'username') { + return $this->url->username; + } + + throw new InvalidArgumentException(sprintf('"%s" is not a valid property.', $name)); + } + + /** + * @throws \InvalidArgumentException When an invalid $name or $value value is passed. + * @throws \Rowbot\URL\Exception\TypeError Only when trying to set URL::$searchParams + */ + public function __set(string $name, string $value): void + { + if ($name === 'searchParams') { + throw new TypeError('Cannot redefine the searchParams property.'); + } + + $input = Utf8String::fromUnsafe($value); + $parser = new BasicURLParser($this->logger); + + if ($name === 'hash') { + if ($input->isEmpty()) { + $this->url->fragment = null; + $this->url->path->potentiallyStripTrailingSpaces($this->url); + + // Terminate these steps + return; + } + + if ($input->startsWith('#')) { + $input = $input->substr(1); + } + + $this->url->fragment = ''; + $parser->parse($input, null, null, $this->url, ParserState::FRAGMENT); + } elseif ($name === 'host') { + if ($this->url->path->isOpaque()) { + // Terminate these steps + return; + } + + $parser->parse($input, null, null, $this->url, ParserState::HOST); + } elseif ($name === 'hostname') { + if ($this->url->path->isOpaque()) { + // Terminate these steps + return; + } + + $parser->parse($input, null, null, $this->url, ParserState::HOSTNAME); + } elseif ($name === 'href') { + $parsedURL = $parser->parse($input); + + if ($parsedURL === false) { + throw new TypeError(sprintf('"%s" is not a valid URL.', $value)); + } + + $this->url = $parsedURL; + $this->queryObject->setUrl($this->url); + + if ($this->url->query === null) { + return; + } + + $this->queryObject->setList(QueryList::fromString($this->url->query)); + } elseif ($name === 'password') { + if ($this->url->cannotHaveUsernamePasswordPort()) { + return; + } + + $this->url->setPassword($input); + } elseif ($name === 'pathname') { + if ($this->url->path->isOpaque()) { + // Terminate these steps + return; + } + + $this->url->path = new PathList(); + $parser->parse($input, null, null, $this->url, ParserState::PATH_START); + } elseif ($name === 'port') { + if ($this->url->cannotHaveUsernamePasswordPort()) { + return; + } + + if ($value === '') { + $this->url->port = null; + + return; + } + + $parser->parse($input, null, null, $this->url, ParserState::PORT); + } elseif ($name === 'protocol') { + $parser->parse($input->append(':'), null, null, $this->url, ParserState::SCHEME_START); + } elseif ($name === 'search') { + if ($value === '') { + $this->url->query = null; + $this->queryObject->setList(new QueryList()); + $this->url->path->potentiallyStripTrailingSpaces($this->url); + + return; + } + + if ($input->startsWith('?')) { + $input = $input->substr(1); + } + + $this->url->query = ''; + $parser->parse($input, null, null, $this->url, ParserState::QUERY); + $this->queryObject->setList(QueryList::fromString((string) $input)); + } elseif ($name === 'username') { + if ($this->url->cannotHaveUsernamePasswordPort()) { + return; + } + + $this->url->setUsername($input); + } else { + throw new InvalidArgumentException(sprintf('"%s" is not a valid property.', $name)); + } + } + + public function __toString(): string + { + return $this->url->serializeURL(); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/URLRecord.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/URLRecord.php new file mode 100644 index 0000000000..d7dbd01305 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/URLRecord.php @@ -0,0 +1,242 @@ +scheme = new Scheme(); + $this->username = ''; + $this->password = ''; + $this->host = new NullHost(); + $this->port = null; + $this->path = new PathList(); + $this->query = null; + $this->fragment = null; + } + + /** + * Whether or not a URL can have a username, password, or port set. + * + * @see https://url.spec.whatwg.org/#cannot-have-a-username-password-port + */ + public function cannotHaveUsernamePasswordPort(): bool + { + return $this->host->isNull() || $this->host->isEmpty() || $this->scheme->isFile(); + } + + /** + * Whether or not the URL has a username or password. + * + * @see https://url.spec.whatwg.org/#include-credentials + */ + public function includesCredentials(): bool + { + return $this->username !== '' || $this->password !== ''; + } + + /** + * Computes a URL's origin. + * + * @see https://url.spec.whatwg.org/#origin + */ + public function getOrigin(): Origin + { + if ($this->scheme->isBlob()) { + $parser = new BasicURLParser(); + $url = $parser->parse($this->path->first()->toUtf8String()); + + if ($url === false) { + // Return a new opaque origin + return new OpaqueOrigin(); + } + + switch ((string) $url->scheme) { + case 'https': + case 'http': + case 'file': + return $url->getOrigin(); + default: + return new OpaqueOrigin(); + } + } + + if ($this->scheme->isFile()) { + // Unfortunate as it is, this is left as an exercise to the + // reader. When in doubt, return a new opaque origin. + return new OpaqueOrigin(); + } + + if ($this->scheme->isSpecial()) { + // Return a tuple consiting of URL's scheme, host, port, and null + return new TupleOrigin((string) $this->scheme, $this->host, $this->port); + } + + // Return a new opaque origin. + return new OpaqueOrigin(); + } + + /** + * Determines whether two URLs are equal to eachother. + * + * @see https://url.spec.whatwg.org/#concept-url-equals + * + * @param bool $excludeFragment (optional) determines whether a URL's fragment should be factored into equality. + */ + public function isEqual(self $otherUrl, bool $excludeFragment = false): bool + { + return $this->serializeURL($excludeFragment) === $otherUrl->serializeURL($excludeFragment); + } + + /** + * Serializes a URL object. + * + * @see https://url.spec.whatwg.org/#concept-url-serializer + * + * @param bool $excludeFragment (optional) When specified it will exclude the URL's fragment from being serialized. + */ + public function serializeURL(bool $excludeFragment = false): string + { + $output = $this->scheme . ':'; + $isNullHost = $this->host->isNull(); + + if (!$isNullHost) { + $output .= '//'; + + if ($this->username !== '' || $this->password !== '') { + $output .= $this->username; + + if ($this->password !== '') { + $output .= ':' . $this->password; + } + + $output .= '@'; + } + + $output .= $this->host->getSerializer()->toFormattedString(); + + if ($this->port !== null) { + $output .= ':' . $this->port; + } + } + + // 3. If url’s host is null, url does not have an opaque path, url’s path’s size is greater than 1, and url’s + // path[0] is the empty string, then append U+002F (/) followed by U+002E (.) to output. + if ($isNullHost && !$this->path->isOpaque() && $this->path->count() > 1 && $this->path->first()->isEmpty()) { + // NOTE: This prevents web+demo:/.//not-a-host/ or web+demo:/path/..//not-a-host/, when parsed and then + // serialized, from ending up as web+demo://not-a-host/ (they end up as web+demo:/.//not-a-host/) + $output .= '/.'; + } + + // 4. Append the result of URL path serializing url to output. + $output .= $this->path; + + // 5. If url’s query is non-null, append U+003F (?), followed by url’s query, to output. + if ($this->query !== null) { + $output .= '?' . $this->query; + } + + // 6. If exclude fragment is false and url’s fragment is non-null, then append U+0023 (#), followed by url’s + // fragment, to output. + if (!$excludeFragment && $this->fragment !== null) { + $output .= '#' . $this->fragment; + } + + // 7. Return output. + return $output; + } + + /** + * @see https://url.spec.whatwg.org/#set-the-password + */ + public function setPassword(USVStringInterface $input): void + { + $percentEncoder = new PercentEncoder(); + $this->password = $percentEncoder->percentEncodeAfterEncoding( + 'utf-8', + (string) $input, + EncodeSet::USERINFO + ); + } + + /** + * @see https://url.spec.whatwg.org/#set-the-username + */ + public function setUsername(USVStringInterface $input): void + { + $percentEncoder = new PercentEncoder(); + $this->username = $percentEncoder->percentEncodeAfterEncoding( + 'utf-8', + (string) $input, + EncodeSet::USERINFO + ); + } + + public function __clone() + { + $this->scheme = clone $this->scheme; + $this->host = clone $this->host; + $this->path = clone $this->path; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/src/URLSearchParams.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/URLSearchParams.php new file mode 100644 index 0000000000..d4edf4025f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/src/URLSearchParams.php @@ -0,0 +1,425 @@ + + * + * @property-read int<0, max> $size + */ +class URLSearchParams implements Countable, Iterator +{ + /** + * @var 0|positive-int + */ + private $cursor; + + /** + * @var \Rowbot\URL\Component\QueryList + */ + private $list; + + /** + * @var \Rowbot\URL\URLRecord|null + */ + private $url; + + /** + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams + * + * @param iterable|(\Traversable&\Countable)>|object|string|\Stringable $init + */ + public function __construct($init = '') + { + $this->list = new QueryList(); + $this->url = null; + $this->cursor = 0; + + if (func_num_args() < 1) { + return; + } + + if ($this->isStringable($init)) { + $init = Utf8String::scrub((string) $init); + + if ($init !== '' && $init[0] === '?') { + $init = substr($init, 1); + } + + $this->list = QueryList::fromString($init); + + return; + } + + if (is_iterable($init)) { + $this->initIterator($init); + + return; + } + + if (is_object($init)) { + $this->initObject($init); + + return; + } + } + + /** + * Appends a new name-value pair to the end of the query string. + * + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append + * + * @param string $name The name of the key in the pair. + * @param string $value The value assigned to the key. + */ + public function append(string $name, string $value): void + { + $this->list->append(Utf8String::scrub($name), Utf8String::scrub($value)); + $this->update(); + } + + public function count(): int + { + return $this->list->count(); + } + + /** + * @return array{0: string, 1: string}|null + */ + public function current(): ?array + { + $tuple = $this->list->getTupleAt($this->cursor); + + if ($tuple === null) { + return null; + } + + return [$tuple['name'], $tuple['value']]; + } + + /** + * Deletes all occurances of pairs with the specified key name. + * + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-delete + * + * @param string $name The name of the key to delete. + */ + public function delete(string $name, ?string $value = null): void + { + if ($value !== null) { + $value = Utf8String::scrub($value); + } + + $this->list->remove(Utf8String::scrub($name), $value); + $this->update(); + } + + /** + * Get the value of the first name-value pair with the specified key name. + * + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get + * + * @param string $name The name of the key whose value you want to retrive. + * + * @return string|null The value of the specified key. + */ + public function get(string $name): ?string + { + return $this->list->first(Utf8String::scrub($name)); + } + + /** + * Gets all name-value pairs that has the specified key name. + * + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall + * + * @param string $name The name of the key whose values you want to retrieve. + * + * @return array An array containing all the values of the specified key. + */ + public function getAll(string $name): array + { + $name = Utf8String::scrub($name); + + return array_column($this->list->filter(static function (array $pair) use ($name) : bool { + return $pair['name'] === $name; + }), 'value'); + } + + /** + * Indicates whether or not a query string contains any keys with the specified key name. + * + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has + * + * @param string $name The key name you want to test if it exists. + * + * @return bool Returns true if the key exits, otherwise false. + */ + public function has(string $name, ?string $value = null): bool + { + if ($value !== null) { + $value = Utf8String::scrub($value); + } + + return $this->list->contains(Utf8String::scrub($name), $value); + } + + public function key(): int + { + return $this->cursor; + } + + public function next(): void + { + ++$this->cursor; + } + + public function rewind(): void + { + $this->cursor = 0; + } + + /** + * Sets the value of the specified key name. If multiple pairs exist with the same key name it + * will set the value for the first occurance of the key in the query string and all other + * occurances will be removed from the query string. If the key does not already exist in the + * query string, it will be added to the end of the query string. + * + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set + * + * @param string $name The name of the key you want to modify the value of. + * @param string $value The value you want to associate with the key name. + */ + public function set(string $name, string $value): void + { + $name = Utf8String::scrub($name); + $value = Utf8String::scrub($value); + + if ($this->list->contains($name)) { + $this->list->set($name, $value); + } else { + $this->list->append($name, $value); + } + + $this->update(); + } + + /** + * @internal + */ + public function setList(QueryList $list): void + { + $this->list = $list; + } + + /** + * Sets the associated url record. + * + * @internal + */ + public function setUrl(URLRecord $url): void + { + $this->url = $url; + } + + /** + * Sorts the list of search params by their names by comparing their code unit values, + * preserving the relative order between pairs with the same name. + * + * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort + */ + public function sort(): void + { + $this->list->sort(); + $this->update(); + } + + public function toString(): string + { + return $this->list->toUrlencodedString(); + } + + /** + * @phpstan-assert-if-true array{0: string, 1: string} $this->current() + */ + public function valid(): bool + { + return $this->list->getTupleAt($this->cursor) !== null; + } + + /** + * Set's the associated URL object's query to the serialization of URLSearchParams. + * + * @see https://url.spec.whatwg.org/#concept-urlsearchparams-update + * + * @internal + */ + protected function update(): void + { + if ($this->url === null) { + return; + } + + $query = $this->list->toUrlencodedString(); + + if ($query === '') { + $query = null; + } + + $this->url->query = $query; + + if ($query === null) { + $this->url->path->potentiallyStripTrailingSpaces($this->url); + } + } + + /** + * @param iterable&\Countable> $input + * + * @throws \Rowbot\URL\Exception\TypeError + */ + private function initIterator(iterable $input): void + { + foreach ($input as $key => $pair) { + // Try to catch cases where $pair isn't countable or $pair is + // countable, but isn't a valid sequence, such as: + // + // class CountableClass implements \Countable + // { + // public function count() + // { + // return 2; + // } + // } + // + // $s = new \Rowbot\URL\URLSearchParams([new CountableClass()]); + // + // or: + // + // $a = new \ArrayObject(['x', 'y']); + // $s = new \Rowbot\URL\URLSearchParams($a); + // + // while still allowing things like: + // + // $a = new \ArrayObject(new \ArrayObject(['x', 'y'])); + // $s = new \Rowbot\URL\URLSearchParams($a);' + if (!(is_array($pair) || $pair instanceof \Countable) || !is_iterable($pair)) { + throw new TypeError(sprintf( + 'Expected a valid sequence such as an Array or iterable Object that implements ' + . 'the \\Countable interface. %s found instead.', + get_debug_type($pair) + )); + } + + if (count($pair) !== 2) { + throw new TypeError(sprintf( + 'Expected sequence with exactly 2 items. Sequence contained %d items.', + count($pair) + )); + } + + [$name, $value] = $pair; + + if (!$this->isStringable($name)) { + throw new TypeError(sprintf( + 'The name of the name-value pair at index "%s" must be a scalar value or stringable.', + $key + )); + } + + if (!$this->isStringable($value)) { + throw new TypeError(sprintf( + 'The value of the name-value pair at index "%s" must be a scalar value or stringable.', + $key + )); + } + + $this->list->append(Utf8String::scrub((string) $name), Utf8String::scrub((string) $value)); + } + } + + private function initObject(object $input): void + { + $reflection = new ReflectionObject($input); + + foreach ($reflection->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { + $value = $property->getValue($input); + + if (!$this->isStringable($value)) { + throw new TypeError(sprintf( + 'The value of property "%s" must be a scalar value or \\Stringable.', + $reflection->getName() + )); + } + + $this->list->append(Utf8String::scrub($property->getName()), Utf8String::scrub((string) $value)); + } + } + + /** + * @phpstan-assert-if-true scalar|\Stringable $value + * @param mixed $value + */ + private function isStringable($value): bool + { + return $value instanceof Stringable || is_scalar($value); + } + + public function __clone() + { + $this->list = clone $this->list; + + // Null out the url in-case someone tries cloning the object returned by + // the URL::searchParams attribute. + $this->url = null; + } + + /** + * @return mixed + */ + public function __get(string $name) + { + switch ($name) { + case 'size': + return $this->list->count(); + default: + return null; + } + } + + /** + * Returns all name-value pairs stringified in the correct order. + */ + public function __toString(): string + { + return $this->list->toUrlencodedString(); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/EncodingHelperTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/EncodingHelperTest.php new file mode 100644 index 0000000000..f63b1ea84a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/EncodingHelperTest.php @@ -0,0 +1,17 @@ +getIterator(), new StringBuffer(), new URLRecord(), null, null, null, null); + foreach ($output as $i => $expected) { + $isOpaque = $i % 2 === 1; + $host = $parser->parse($context, $in, $isOpaque); + + if ($expected === false) { + self::assertFalse($host); + } else { + self::assertInstanceOf(HostInterface::class, $host); + self::assertSame($expected, $host->getSerializer()->toFormattedString()); + } + } + } + + public static function exampleDataProvider(): array + { + return [ + ['input' => 'EXAMPLE.COM', 'output' => ['example.com', 'EXAMPLE.COM']], + ['input' => 'example%2Ecom', 'output' => ['example.com', 'example%2Ecom']], + ['input' => 'faß.example', 'output' => ['xn--fa-hia.example', 'fa%C3%9F.example']], + ['input' => '0', 'output' => ['0.0.0.0', '0']], + ['input' => '%30', 'output' => ['0.0.0.0', '%30']], + ['input' => '0x', 'output' => ['0.0.0.0', '0x']], + ['input' => '0xffffffff', 'output' => ['255.255.255.255', '0xffffffff']], + ['input' => '[0:0::1]', 'output' => ['[::1]', '[::1]']], + ['input' => '[0:0::1%5D', 'output' => [false, false]], + ['input' => '[0:0::%31]', 'output' => [false, false]], + ['input' => '09', 'output' => [false, '09']], + ['input' => 'example.255', 'output' => [false, 'example.255']], + ['input' => 'example^example', 'output' => [false, false]], + ]; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/BrickMathAdapterTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/BrickMathAdapterTest.php new file mode 100644 index 0000000000..b3170e93f2 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/BrickMathAdapterTest.php @@ -0,0 +1,39 @@ +expectException(MathException::class); + (new BrickMathAdapter(42))->isEqualTo(new NativeIntAdapter(42)); + } + + public function testIsGreaterThanOrEqualToError(): void + { + $this->expectException(MathException::class); + (new BrickMathAdapter(42))->isGreaterThanOrEqualTo(new NativeIntAdapter(42)); + } + + public function testPlusError(): void + { + $this->expectException(MathException::class); + (new BrickMathAdapter(42))->plus(new NativeIntAdapter(42)); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/MathTestCase.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/MathTestCase.php new file mode 100644 index 0000000000..50d79b7386 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/MathTestCase.php @@ -0,0 +1,92 @@ +createNumber(42); + $computedQuotient = $dividend->intdiv($divisor); + self::assertTrue($computedQuotient->isEqualTo($this->createNumber($quoient))); + self::assertSame($quoient, (string) $computedQuotient); + } + + public static function equalityNumberProvider(): array + { + return [ + [PHP_INT_MAX, 10, (string) PHP_INT_MAX], + ['01234567', 8, '342391'], + ['DF', 16, '223'], + ['-24', 10, '-24'], + ]; + } + + /** + * @param int|string $number + */ + public function testIsEqualTo($number, int $base, string $expected): void + { + self::assertTrue($this->createNumber($number, $base)->isEqualTo($this->createNumber($expected))); + } + + public function testIsGreaterThan(int $number1, int $number2, bool $result): void + { + self::assertSame($result, $this->createNumber($number1)->isGreaterThan($number2)); + } + + public function testIsGreaterThanOrEqualTo(int $number1, int $number2, bool $result): void + { + self::assertSame($result, $this->createNumber($number1)->isGreaterThanOrEqualTo($this->createNumber($number2))); + } + + public function testMod(int $dividend, int $divisor, int $remainder): void + { + $computedRemainder = $this->createNumber($dividend)->mod($divisor); + self::assertTrue($computedRemainder->isEqualTo($this->createNumber($remainder))); + self::assertSame((string) $remainder, (string) $computedRemainder); + } + + public function testMultipliedBy(int $multiplicand, int $multiplier, int $product): void + { + $computedProduct = $this->createNumber($multiplicand)->multipliedBy($multiplier); + self::assertTrue($computedProduct->isEqualTo($this->createNumber($product))); + self::assertSame((string) $product, (string) $computedProduct); + } + + public function testPlus(int $addend1, int $addend2, string $sum): void + { + $computedSum = $this->createNumber($addend1)->plus($this->createNumber($addend2)); + self::assertTrue($computedSum->isEqualTo($this->createNumber($sum))); + self::assertSame($sum, (string) $computedSum); + } + + public function testPow(int $base, int $exponent, string $power): void + { + $computedPower = $this->createNumber($base)->pow($exponent); + self::assertTrue($computedPower->isEqualTo($this->createNumber($power))); + self::assertSame($power, (string) $computedPower); + } + + /** + * @param int|string $number + */ + public function testToString($number, int $base, string $result): void + { + self::assertSame($result, (string) $this->createNumber($number, $base)); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/NativeIntAdapterTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/NativeIntAdapterTest.php new file mode 100644 index 0000000000..fa384de84f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/Math/NativeIntAdapterTest.php @@ -0,0 +1,39 @@ +expectException(MathException::class); + (new NativeIntAdapter(42))->isEqualTo(new BrickMathAdapter(42)); + } + + public function testIsGreaterThanOrEqualToError(): void + { + $this->expectException(MathException::class); + (new NativeIntAdapter(42))->isGreaterThanOrEqualTo(new BrickMathAdapter(42)); + } + + public function testPlusError(): void + { + $this->expectException(MathException::class); + (new NativeIntAdapter(42))->plus(new BrickMathAdapter(42)); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/NullHostTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/NullHostTest.php new file mode 100644 index 0000000000..5eff2fed11 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/NullHostTest.php @@ -0,0 +1,28 @@ +getSerializer(); + self::assertEmpty($serializer->toFormattedString()); + self::assertEmpty($serializer->toString()); + } + + public function testNullHostIsEqualOnlyToItself(): void + { + $host = new NullHost(); + self::assertTrue($host->equals($host)); + self::assertTrue($host->equals(new NullHost())); + self::assertFalse($host->equals(new StringHost())); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/OriginTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/OriginTest.php new file mode 100644 index 0000000000..677e9dde81 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/OriginTest.php @@ -0,0 +1,207 @@ +parse($context, new Utf8String('example.org'), false), + null, + null + ); + $tupleDomain = new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + null, + 'example.org' + ); + $opaque = new OpaqueOrigin(); + $urlParser = new BasicURLParser(); + + return [ + [ + $tuple, + new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + null, + null + ), + 'isSameOrigin' => true, + 'isSameOriginDomain' => true, + ], + [ + new TupleOrigin('https', $hostParser->parse($context, new Utf8String('example.org'), false), 314, null), + new TupleOrigin('https', $hostParser->parse($context, new Utf8String('example.org'), false), 420, null), + 'isSameOrigin' => false, + 'isSameOriginDomain' => false, + ], + [ + new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + 314, + 'example.org' + ), + new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + 420, + 'example.org' + ), + 'isSameOrigin' => false, + 'isSameOriginDomain' => true, + ], + [ + new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + null, + null + ), + new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + null, + 'example.org' + ), + 'isSameOrigin' => true, + 'isSameOriginDomain' => false, + ], + [ + new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + null, + 'example.org' + ), + new TupleOrigin( + 'http', + $hostParser->parse($context, new Utf8String('example.org'), false), + null, + 'example.org' + ), + 'isSameOrigin' => false, + 'isSameOriginDomain' => false, + ], + [ + new TupleOrigin('https', $hostParser->parse($context, new Utf8String('127.0.0.1'), false), null, null), + new TupleOrigin('https', $hostParser->parse($context, new Utf8String('1.1.1.1'), false), null, null), + 'isSameOrigin' => false, + 'isSameOriginDomain' => false, + ], + [ + new TupleOrigin('https', $hostParser->parse($context, new Utf8String('[::1]'), false), null, null), + new TupleOrigin('https', $hostParser->parse($context, new Utf8String('[1::1]'), false), null, null), + 'isSameOrigin' => false, + 'isSameOriginDomain' => false, + ], + [ + $urlParser->parse(new Utf8String('blob:https://foo.com'))->getOrigin(), + $urlParser->parse(new Utf8String('https://foo.com'))->getOrigin(), + 'isSameOrigin' => true, + 'isSameOriginDomain' => true, + ], + [ + $tuple, + $tuple, + 'isSameOrigin' => true, + 'isSameOriginDomain' => true, + ], + [ + $tuple, + $tupleDomain, + 'isSameOrigin' => true, + 'isSameOriginDomain' => false, + ], + [ + $opaque, + new OpaqueOrigin(), + 'isSameOrigin' => false, + 'isSameOriginDomain' => false, + ], + [ + $opaque, + $opaque, + 'isSameOrigin' => true, + 'isSameOriginDomain' => true, + ], + [ + $tuple, + $opaque, + 'isSameOrigin' => false, + 'isSameOriginDomain' => false, + ], + ]; + } + + public function testSameOriginConcept(Origin $originA, Origin $originB, bool $isSameOrigin, bool $isSameOriginDomain): void + { + self::assertSame($isSameOrigin, $originA->isSameOrigin($originB)); + self::assertSame($isSameOriginDomain, $originA->isSameOriginDomain($originB)); + } + + public function testEffectiveDomainConcept(): void + { + $origin = new OpaqueOrigin(); + self::assertTrue($origin->isOpaque()); + self::assertNull($origin->getEffectiveDomain()); + + $parser = new BasicURLParser(); + $record = $parser->parse(new Utf8String('blob:https://foo.com')); + $origin = $record->getOrigin(); + self::assertFalse($origin->isOpaque()); + self::assertNotNull($origin->getEffectiveDomain()); + self::assertSame('foo.com', $origin->getEffectiveDomain()); + + $hostParser = new HostParser(); + $context = new ParserContext( + new Utf8String(''), + new Utf8StringIterator(''), + new StringBuffer(''), + new URLRecord(), + null, + null, + null, + null + ); + $origin = new TupleOrigin( + 'https', + $hostParser->parse($context, new Utf8String('example.org'), false), + 314, + 'example.org' + ); + self::assertFalse($origin->isOpaque()); + self::assertNotNull($origin->getEffectiveDomain()); + self::assertSame('example.org', $origin->getEffectiveDomain()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/PathTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/PathTest.php new file mode 100644 index 0000000000..e885b781af --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/PathTest.php @@ -0,0 +1,53 @@ +isNormalizedWindowsDriveLetter()); + } + + public function testOpaquePathThrowsOnShorten(): void + { + $path = new OpaquePath(new PathSegment()); + $this->expectException(URLException::class); + $path->shorten(new Scheme('file')); + } + + public function testOpaquePathThrowsOnPush(): void + { + $path = new OpaquePath(new PathSegment()); + $this->expectException(URLException::class); + $path->push(new PathSegment()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/QueryListTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/QueryListTest.php new file mode 100644 index 0000000000..d1d3b3140d --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/QueryListTest.php @@ -0,0 +1,22 @@ + 'a', 'value' => 'b'], + ['name' => 'a', 'value' => 'c'], + ]; + $list = new QueryList($input); + $list->set('Foo', 'Bar'); + self::assertSame($input, $list->getIterator()->getArrayCopy()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/QueryStateTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/QueryStateTest.php new file mode 100644 index 0000000000..8908e5444e --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/QueryStateTest.php @@ -0,0 +1,47 @@ +parse($input, null, 'windows-1252'); + + self::assertInstanceOf(URLRecord::class, $record); + self::assertSame('sm%F6rg%E5sbord', $record->query); + + $record = $parser->parse($input, null); + + self::assertInstanceOf(URLRecord::class, $record); + self::assertSame('sm%C3%B6rg%C3%A5sbord', $record->query); + } + + public function testParsingWebsocketForcesUtf8EncodingInQueryString(): void + { + $string = 'wss://example.com/?' . html_entity_decode('smörgåsbord', ENT_HTML5, 'utf-8'); + $input = new Utf8String($string); + $parser = new BasicURLParser(); + $record = $parser->parse($input, null, 'windows-1252'); + + self::assertInstanceOf(URLRecord::class, $record); + self::assertSame('sm%C3%B6rg%C3%A5sbord', $record->query); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/SchemeTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/SchemeTest.php new file mode 100644 index 0000000000..e9205f5bd3 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/SchemeTest.php @@ -0,0 +1,40 @@ +getConstant('SPECIAL_SCHEMES'); + + foreach ($schemes as $scheme => $port) { + if ($port === null) { + continue; + } + + yield [$scheme, $port]; + } + } + + public function testIsDefaultPortReturnsTrueForNonNullPortSpecialSchemes(string $scheme, int $port): void + { + $scheme = new Scheme($scheme); + self::assertTrue($scheme->isDefaultPort($port)); + } + + public function testIsDefaultPortReturnsFalseForNonSpecialSchemesAndNullPorts(string $scheme, ?int $port): void + { + $scheme = new Scheme($scheme); + self::assertFalse($scheme->isDefaultPort($port)); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/StringsTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/StringsTest.php new file mode 100644 index 0000000000..20c98cfbc9 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/StringsTest.php @@ -0,0 +1,118 @@ +expectException(ValueError::class); + Utf8String::transcode('stuff', 'gallifreyan', 'utf-8'); + } + + public static function startsWithTwoAsciiHexDigitsProvider(): array + { + return [ + ['ab', true], + ['a', false], + ['99', true], + ['a3', true], + ['3a', true], + ['a4x', true], + ['AB', true], + ['3F', true], + ['gab', false], + ['', false], + ]; + } + + public function testStartsWithTwoAsciiHexDigits(string $input, bool $expected): void + { + $s = new Utf8String($input); + self::assertSame($expected, $s->startsWithTwoAsciiHexDigits()); + } + + public static function startsWithWindowsDriveLetterProvider(): array + { + return [ + ['c:', true], + ['c:/', true], + ['c:a', false], + ['4:', false], + ['az:', false], + ['a|', true], + ['a:|', false], + ['', false], + ['c:\\', true], + ['c:?', true], + ['c:#', true], + ['c:/f', true], + ]; + } + + public function testStartsWithWindowsDriveLetter(string $input, bool $expected): void + { + $s = new Utf8String($input); + self::assertSame($expected, $s->startsWithWindowsDriveLetter()); + } + + public function testMatchesThrowsWhenOffsetExceedsLength(): void + { + $this->expectException(RegexException::class); + $s = new Utf8String(''); + $s->matches('/[A-Z]/', $matches, 0, 1); + } + + public function testMatchesThrowsOnInvalidUtf8Text(): void + { + $this->expectException(RegexException::class); + $s = new Utf8String("\xC3\x7F"); + $s->matches('/[A-Z]/u'); + } + + public function testReplaceRegexThrowsOnInvalidUtf8Text(): void + { + $this->expectException(RegexException::class); + $s = new Utf8String("\xC3\x7F"); + $s->replaceRegex('/[A-Z]/u', 'foo'); + } + + public function testSplitReturnsEmptyListWithEmptyDelimiter(): void + { + $s = new Utf8String(''); + self::assertTrue($s->split('')->isEmpty()); + } + + public function testStringListFirstThrowsWithEmptyList(): void + { + $this->expectException(UndefinedIndexException::class); + $list = new StringList(); + $list->first(); + } + + public function testStringListLastThrowsWithEmptyList(): void + { + $this->expectException(UndefinedIndexException::class); + $list = new StringList(); + $list->last(); + } + + public function testStringListKeyReturnsInteger(): void + { + $s = new Utf8String('a=b=c=d'); + + foreach ($s->split('=') as $key => $string) { + self::assertIsInt($key); + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLRecordTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLRecordTest.php new file mode 100644 index 0000000000..07c4024176 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLRecordTest.php @@ -0,0 +1,40 @@ +parse(new Utf8String('blob:failure')); + $origin = $record->getOrigin(); + self::assertTrue($origin->isOpaque()); + self::assertNull($origin->getEffectiveDomain()); + } + + public function testFileSchemeCreatesOpaqueOrigin(): void + { + $parser = new BasicURLParser(); + $record = $parser->parse(new Utf8String('file:///C:/Users/Desktop/')); + $origin = $record->getOrigin(); + self::assertTrue($origin->isOpaque()); + self::assertNull($origin->getEffectiveDomain()); + } + + public function testEquality(string $urlA, string $urlB, bool $isEqualWithHash, bool $isEqualWithoutHash): void + { + $parser = new BasicURLParser(); + $recordA = $parser->parse(new Utf8String($urlA)); + $recordB = $parser->parse(new Utf8String($urlB)); + self::assertSame($isEqualWithHash, $recordA->isEqual($recordB, false)); + self::assertSame($isEqualWithoutHash, $recordA->isEqual($recordB, true)); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLSearchParamsTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLSearchParamsTest.php new file mode 100644 index 0000000000..1d3dbf2802 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLSearchParamsTest.php @@ -0,0 +1,123 @@ +append('foo', 'bar'); + + self::assertSame('foo=bar', $query->toString()); + } + + public function testCloningAttachedURLSearchParams(): void + { + $url = new URL('http://example.com/?foo=bar'); + $query = clone $url->searchParams; + $query->append('foo', 'bar'); + + self::assertSame('?foo=bar', $url->search); + self::assertSame('foo=bar', $url->searchParams->toString()); + } + + public function testIterationKey(): void + { + $query = new URLSearchParams('foo=bar&qux=baz'); + $result = [ + ['foo', 'bar'], + ['qux', 'baz'], + ]; + + foreach ($query as $index => $pair) { + self::assertSame($result[$index], $pair); + } + } + + public static function getInvalidIteratorInput(): array + { + $generator = static function (): Generator { + yield 'foo'; + yield 'bar'; + }; + $anonClass = new class ($generator()) implements Countable + { + /** + * @var \Generator + */ + public $foo; + + public function __construct(Generator $foo) + { + $this->foo = $foo; + } + + public function count(): int + { + return 2; + } + }; + + return [ + 'sequences not equal to 2' => [[['foo', 'bar'], ['baz']]], + 'non-iterable' => [new ArrayObject(['x', 'y'])], + 'generator' => [[$generator()]], + 'invalid-name' => [[[null, 'foo']]], + 'invalid-value' => [[['foo', null]]], + 'countable-only' => [[[$anonClass]]], + 'invalid-property-value' => [$anonClass], + 'iterable-non-countable' => [new ArrayObject([$generator(), $generator()])], + ]; + } + + /** + * @param mixed[]|object $input + */ + public function testInvalidIteratorInput($input): void + { + $this->expectException(TypeError::class); + new URLSearchParams($input); + } + + public static function unhandledInputProvider(): array + { + return [ + [static function (): void { + return; + }], + ]; + } + + public function testUnhandledInputDoesNothing($input): void + { + $params = new URLSearchParams($input); + self::assertFalse($params->valid()); + } + + public function testInvalidIteratorReturnsNull(): void + { + $params = new URLSearchParams(); + self::assertNull($params->current()); + self::assertFalse($params->valid()); + } + + public function testSortingPairWithEmptyName(): void + { + $params = new URLSearchParams('=foo&x=bar&c=bar'); + $params->sort(); + self::assertSame('=foo&c=bar&x=bar', $params->toString()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLTest.php new file mode 100644 index 0000000000..baca4d8d0c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/URLTest.php @@ -0,0 +1,124 @@ +href = 'https://foo:bar@foo.com/foo/bar/?foo=bar#foo'; + + self::assertSame('http:', $url1->protocol); + self::assertEmpty($url1->username); + self::assertEmpty($url1->password); + self::assertSame('127.0.0.1', $url1->host); + self::assertSame('127.0.0.1', $url1->hostname); + self::assertEmpty($url1->port); + self::assertSame('/', $url1->pathname); + self::assertEmpty($url1->search); + self::assertEmpty($url1->hash); + } + + /** + * Test variations of percent encoded dot path segements not covered by the WHATWG tests. + */ + public function testPercentEncodedDotPathSegments(): void + { + $url = new URL('http://example.com/foo/bar/%2e%2E/%2E%2e'); + self::assertSame('http://example.com/', $url->href); + self::assertSame('/', $url->pathname); + } + + public function testInvalidGetterPropertyName(): void + { + $this->expectException(InvalidArgumentException::class); + $url = new URL('http://example.com'); + $url->nonExistantProperty; + } + + public function testInvalidSetterPropertyName(): void + { + $this->expectException(InvalidArgumentException::class); + $url = new URL('http://example.com'); + $url->nonExistantProperty = 'foo'; + } + + public function testHrefSetterFailure(): void + { + $this->expectException(TypeError::class); + $url = new URL('http://example.com'); + $url->href = 'foo'; + } + + public function testCastingURLObjectToString(): void + { + $url = new URL('http://example.com'); + self::assertSame('http://example.com/', (string) $url); + self::assertSame('http://example.com/', $url->toString()); + } + + public function testHrefSetterWithNoQueryString(): void + { + $url = new URL('http://example.com'); + $url->href = 'ssh://example.org'; + self::assertSame('ssh://example.org', $url->href); + } + + public function testValidLoggerDoesNotThrow(): void + { + $url = 'https://example.com'; + self::assertInstanceOf(URL::class, new URL($url, null, [])); + self::assertInstanceOf(URL::class, new URL($url, null, ['logger' => null])); + self::assertInstanceOf(URL::class, new URL($url, null, ['logger' => new ValidationErrorLogger()])); + } + + public function testInvalidLoggerThrows($value): void + { + $this->expectException(TypeError::class); + new URL('https://example.com', null, ['logger' => $value]); + } + + public function testURLConstructorAcceptsStringable(): void + { + $foo = new class + { + public function __toString(): string + { + return 'https://foo.com'; + } + }; + $bar = new class + { + public function __toString(): string + { + return 'https://bar.com'; + } + }; + + self::assertInstanceOf(URL::class, new URL($foo)); + self::assertInstanceOf(URL::class, new URL($foo, $bar)); + } + + /** + * @param null|object|string $url + * @param null|object|string $base + */ + public function testURLConstructorWithNonStringableObject($url, $base): void + { + $this->expectException(NativeTypeError::class); + new URL($url, $base); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/ValidationErrorLogger.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/ValidationErrorLogger.php new file mode 100644 index 0000000000..088328bfa6 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/ValidationErrorLogger.php @@ -0,0 +1,27 @@ +messages[] = [$level, $message, $context]; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/FailureTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/FailureTest.php new file mode 100644 index 0000000000..f8fab78ab4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/FailureTest.php @@ -0,0 +1,40 @@ +expectException(TypeError::class); + new URL('about:blank', $test['input']); + } + + public function testUrlHrefSetterThrows(array $test): void + { + $this->expectException(TypeError::class); + $url = new URL('about:blank'); + $url->href = $test['input']; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/PercentEncodingTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/PercentEncodingTest.php new file mode 100644 index 0000000000..fa48dd8779 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/PercentEncodingTest.php @@ -0,0 +1,66 @@ + $expected) { + $url = $parser->parse($in, null, $encoding); + + self::assertNotFalse($url); + self::assertSame($expected, $url->query, $encoding); + self::assertSame($output['utf-8'], $url->fragment); + } + } + + public static function percentEncodedDataProvider(): iterable + { + foreach (self::loadTestData('percent-encoding.json') as $data) { + // Skip tests for encodings where mbstring produces a result that is different from what is expected + foreach (['iso-2022-jp', 'gb18030'] as $encoding) { + if (isset($data['output'][$encoding])) { + unset($data['output'][$encoding]); + } + } + + yield $data; + } + } + + /** + * @see https://url.spec.whatwg.org/#example-percent-encode-operations + * @param mixed $encodeSet + */ + public function testPercentEncodingExamples(string $encoding, string $input, string $output, $encodeSet, bool $spaceAsPlus): void + { + $percentEncoder = new PercentEncoder(); + $result = $percentEncoder->percentEncodeAfterEncoding($encoding, $input, $encodeSet, $spaceAsPlus); + self::assertSame($output, $result); + } + + public static function exampleDataProvider(): array + { + return [ + ['encoding' => 'Shift_JIS', 'input' => ' ', 'output' => '%20', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => false], + ['encoding' => 'Shift_JIS', 'input' => '≡', 'output' => '%81%DF', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => false], + ['encoding' => 'Shift_JIS', 'input' => '‽', 'output' => '%26%238253%3B', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => false], + // ['encoding' => 'ISO-2022-JP', 'input' => '¥', 'output' => '%1B(J\%1B(B', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => false], + ['encoding' => 'Shift_JIS', 'input' => '1+1 ≡ 2%20‽', 'output' => '1+1+%81%DF+2%20%26%238253%3B', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => true], + ['encoding' => 'UTF-8', 'input' => '≡', 'output' => '%E2%89%A1', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => false], + ['encoding' => 'UTF-8', 'input' => '‽', 'output' => '%E2%80%BD', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => false], + ['encoding' => 'UTF-8', 'input' => 'Say what‽', 'output' => 'Say%20what%E2%80%BD', 'encodeSet' => EncodeSet::USERINFO, 'spaceAsPlus' => false], + ]; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/ToASCIIWindowTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/ToASCIIWindowTest.php new file mode 100644 index 0000000000..22b7e87304 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/ToASCIIWindowTest.php @@ -0,0 +1,49 @@ +host); + self::assertSame($hostTest['output'], $url->hostname); + self::assertSame('/x', $url->pathname); + self::assertSame('https://' . $hostTest['output'] . '/x', $url->href); + + return; + } + $this->expectException(TypeError::class); + new URL($hostTest['input']); + } + + public function testHostSetter(array $hostTest): void + { + $url = new URL('https://x/x'); + $url->host = $hostTest['input']; + self::assertSame($hostTest['output'] ?? 'x', $url->host); + } + + public function testHostnameSetter(array $hostTest): void + { + $url = new URL('https://x/x'); + $url->hostname = $hostTest['input']; + self::assertSame($hostTest['output'] ?? 'x', $url->hostname); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLConstructorTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLConstructorTest.php new file mode 100644 index 0000000000..7aef28f8fd --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLConstructorTest.php @@ -0,0 +1,59 @@ +href, 'href'); + self::assertSame($expected['protocol'], $url->protocol, 'protocol'); + self::assertSame($expected['username'], $url->username, 'username'); + self::assertSame($expected['password'], $url->password, 'password'); + self::assertSame($expected['host'], $url->host, 'host'); + self::assertSame($expected['hostname'], $url->hostname, 'hostname'); + self::assertSame($expected['port'], $url->port, 'port'); + self::assertSame($expected['pathname'], $url->pathname, 'pathname'); + self::assertSame($expected['search'], $url->search, 'search'); + if (array_key_exists('searchParams', $expected)) { + self::assertTrue((bool) $url->searchParams); + self::assertSame($expected['searchParams'], $url->searchParams->toString(), 'searchParams'); + } + self::assertSame($expected['hash'], $url->hash, 'hash'); + } + + public static function urlTestDataFailureProvider(): iterable + { + foreach (self::loadTestData('urltestdata.json') as $inputs) { + if (isset($inputs['failure'])) { + yield [$inputs]; + } + } + } + + public function testUrlConstructorFailed(array $expected): void + { + $this->expectException(TypeError::class); + isset($expected['base']) ? new URL($expected['input'], $expected['base']) : new URL($expected['input']); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLEncodedParserTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLEncodedParserTest.php new file mode 100644 index 0000000000..6396f8bcad --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLEncodedParserTest.php @@ -0,0 +1,72 @@ + 'test', 'output' => [['test', '']]], + ['input' => "\u{FEFF}test=\u{FEFF}", 'output' => [["\u{FEFF}test", "\u{FEFF}"]]], + ['input' => '%EF%BB%BFtest=%EF%BB%BF', 'output' => [["\u{FEFF}test", "\u{FEFF}"]]], + ['input' => '%EF%BF%BF=%EF%BF%BF', 'output' => [["\u{FFFF}", "\u{FFFF}"]]], + ['input' => '%FE%FF', 'output' => [["\u{FFFD}\u{FFFD}", '']]], + ['input' => '†&†=x', 'output' => [['†', ''], ['†', 'x']]], + ['input' => '%C2', 'output' => [["\u{FFFD}", '']]], + ['input' => '%C2x', 'output' => [["\u{FFFD}x", '']]], + [ + 'input' => '_charset_=windows-1252&test=%C2x', + 'output' => [['_charset_', 'windows-1252'], ['test', "\u{FFFD}x"]], + ], + ['input' => '', 'output' => []], + ['input' => 'a', 'output' => [['a', '']]], + ['input' => 'a=b', 'output' => [['a', 'b']]], + ['input' => 'a=', 'output' => [['a', '']]], + ['input' => '=b', 'output' => [['', 'b']]], + ['input' => '&', 'output' => []], + ['input' => '&a', 'output' => [['a', '']]], + ['input' => 'a&', 'output' => [['a', '']]], + ['input' => 'a&a', 'output' => [['a', ''], ['a', '']]], + ['input' => 'a&b&c', 'output' => [['a', ''], ['b', ''], ['c', '']]], + ['input' => 'a=b&c=d', 'output' => [['a', 'b'], ['c', 'd']]], + ['input' => 'a=b&c=d&', 'output' => [['a', 'b'], ['c', 'd']]], + ['input' => '&&&a=b&&&&c=d&', 'output' => [['a', 'b'], ['c', 'd']]], + ['input' => 'a=a&a=b&a=c', 'output' => [['a', 'a'], ['a', 'b'], ['a', 'c']]], + ['input' => 'a==a', 'output' => [['a', '=a']]], + ['input' => 'a=a+b+c+d', 'output' => [['a', 'a b c d']]], + ['input' => '%=a', 'output' => [['%', 'a']]], + ['input' => '%a=a', 'output' => [['%a', 'a']]], + ['input' => '%a_=a', 'output' => [['%a_', 'a']]], + ['input' => '%61=a', 'output' => [['a', 'a']]], + ['input' => '%61+%4d%4D=', 'output' => [['a MM', '']]], + ['input' => 'id=0&value=%', 'output' => [['id', '0'], ['value', '%']]], + ['input' => 'b=%2sf%2a', 'output' => [['b', '%2sf*']]], + ['input' => 'b=%2%2af%2a', 'output' => [['b', '%2*f*']]], + ['input' => 'b=%%2a', 'output' => [['b', '%*']]], + ]; + } + + public function test(string $input, array $output): void + { + $sp = new URLSearchParams($input); + $i = 0; + if (in_array($input, ['', '&'], true)) { + self::assertFalse($sp->valid()); + + return; + } + foreach ($sp as $item) { + self::assertSame($output[$i++], $item); + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLOriginTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLOriginTest.php new file mode 100644 index 0000000000..64ffe034f7 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLOriginTest.php @@ -0,0 +1,27 @@ +origin); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsAppendTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsAppendTest.php new file mode 100644 index 0000000000..37778c4dd5 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsAppendTest.php @@ -0,0 +1,48 @@ +append('a', 'b'); + self::assertSame('a=b', $params . ''); + $params->append('a', 'b'); + self::assertSame('a=b&a=b', $params . ''); + $params->append('a', 'c'); + self::assertSame('a=b&a=b&a=c', $params . ''); + } + + public function testAppendEmptyString(): void + { + $params = new URLSearchParams(); + $params->append('', ''); + self::assertSame('=', $params . ''); + $params->append('', ''); + self::assertSame('=&=', $params . ''); + $params->append('a', 'c'); + } + + public function testAppendMultiple(): void + { + $params = new URLSearchParams(); + $params->append('first', 1); + $params->append('second', 2); + $params->append('third', ''); + $params->append('first', 10); + self::assertTrue($params->has('first')); + self::assertSame('1', $params->get('first')); + self::assertSame('2', $params->get('second')); + self::assertSame('', $params->get('third')); + $params->append('first', 10); + self::assertSame('1', $params->get('first')); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsConstructorTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsConstructorTest.php new file mode 100644 index 0000000000..d7f3a033f4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsConstructorTest.php @@ -0,0 +1,279 @@ +toString()); + } + + public function testRemovingLeadingQuestionMark(): void + { + $params = new URLSearchParams('?a=b'); + self::assertSame('a=b', $params->toString()); + } + + public function testConstructorEmptyObject(): void + { + $params = new URLSearchParams(new stdClass()); + self::assertSame('', (string) $params); + } + + public function testConstructorString(): void + { + $params = new URLSearchParams('a=b'); + self::assertNotNull($params); + self::assertTrue($params->has('a')); + self::assertFalse($params->has('b')); + $params = new URLSearchParams('a=b&c'); + self::assertNotNull($params); + self::assertTrue($params->has('a')); + self::assertTrue($params->has('c')); + $params = new URLSearchParams('&a&&& &&&&&a+b=& c&m%c3%b8%c3%b8'); + self::assertNotNull($params); + self::assertTrue($params->has('a')); + self::assertTrue($params->has('a b')); + self::assertTrue($params->has(' ')); + self::assertFalse($params->has('c')); + self::assertTrue($params->has(' c')); + self::assertTrue($params->has('møø')); + + $params = new URLSearchParams('id=0&value=%'); + self::assertNotNull($params); + self::assertTrue($params->has('id')); + self::assertTrue($params->has('value')); + self::assertSame('0', $params->get('id')); + self::assertSame('%', $params->get('value')); + + $params = new URLSearchParams('b=%2sf%2a'); + self::assertNotNull($params); + self::assertTrue($params->has('b')); + self::assertSame('%2sf*', $params->get('b')); + + $params = new URLSearchParams('b=%2%2af%2a'); + self::assertNotNull($params); + self::assertTrue($params->has('b')); + self::assertSame('%2*f*', $params->get('b')); + + $params = new URLSearchParams('b=%%2a'); + self::assertNotNull($params); + self::assertTrue($params->has('b')); + self::assertSame('%*', $params->get('b')); + } + + public function testConstructorObject(): void + { + $seed = new URLSearchParams('a=b&c=d'); + $params = new URLSearchParams($seed); + self::assertNotNull($params, 'message'); + self::assertSame('b', $params->get('a')); + self::assertSame('d', $params->get('c')); + self::assertFalse($params->has('d'), 'message'); + // The name-value pairs are copied when created; later, updates should + // not be observable. + $seed->append('e', 'f'); + self::assertFalse($params->has('e')); + $params->append('g', 'h'); + self::assertFalse($seed->has('g')); + } + + public function testParsePlusSign(): void + { + $params = new URLSearchParams('a=b+c'); + self::assertSame('b c', $params->get('a')); + $params = new URLSearchParams('a+b=c'); + self::assertSame('c', $params->get('a b')); + } + + public function testParsePlusSignPercentEncoded(): void + { + $testValue = '+15555555555'; + $params = new URLSearchParams(); + $params->set('query', $testValue); + + $newParams = new URLSearchParams($params->toString()); + self::assertSame('query=%2B15555555555', $params->toString()); + self::assertSame($testValue, $params->get('query')); + self::assertSame($testValue, $newParams->get('query')); + } + + public function testParseSpace(): void + { + $params = new URLSearchParams('a=b c'); + self::assertSame('b c', $params->get('a')); + $params = new URLSearchParams('a b=c'); + self::assertSame('c', $params->get('a b')); + } + + public function testParseSpacePercentEncoded(): void + { + $params = new URLSearchParams('a=b%20c'); + self::assertSame('b c', $params->get('a')); + $params = new URLSearchParams('a%20b=c'); + self::assertSame('c', $params->get('a b')); + } + + public function testParseNullByte(): void + { + $params = new URLSearchParams("a=b\0c"); + self::assertSame("b\0c", $params->get('a')); + $params = new URLSearchParams("a\0b=c"); + self::assertSame('c', $params->get("a\0b")); + } + + public function testParseNullBytePercentEncoded(): void + { + $params = new URLSearchParams('a=b%00c'); + self::assertSame("b\0c", $params->get('a')); + $params = new URLSearchParams('a%00b=c'); + self::assertSame('c', $params->get("a\0b")); + } + + public function testParseUnicodeCompositionSymbol(): void + { + $params = new URLSearchParams("a=b\u{2384}"); + self::assertSame("b\u{2384}", $params->get('a')); + $params = new URLSearchParams("a\u{2384}=c"); + self::assertSame('c', $params->get("a\u{2384}")); + } + + public function testParseUnicodeCompositionSymbolPercentEncoded(): void + { + $params = new URLSearchParams('a=b%E2%8E%84'); + self::assertSame("b\u{2384}", $params->get('a')); + $params = new URLSearchParams('a%E2%8E%84=c'); + self::assertSame('c', $params->get("a\u{2384}")); + } + + public function testParseUnicodePileOfPoo(): void + { + $params = new URLSearchParams("a=b\u{1F4A9}c"); + self::assertSame("b\u{1F4A9}c", $params->get('a')); + $params = new URLSearchParams("a\u{1F4A9}b=c"); + self::assertSame('c', $params->get("a\u{1F4A9}b")); + } + + public function testParseUnicodePileOfPooPercentEncoded(): void + { + $params = new URLSearchParams('a=b%f0%9f%92%a9c'); + self::assertSame("b\u{1F4A9}c", $params->get('a')); + $params = new URLSearchParams('a%f0%9f%92%a9b=c'); + self::assertSame('c', $params->get("a\u{1F4A9}b")); + } + + public function testSequenceOfSequences(): void + { + $params = new URLSearchParams([]); + self::assertNotNull($params); + $params = new URLSearchParams([['a', 'b'], ['c', 'd']]); + self::assertSame('b', $params->get('a')); + self::assertSame('d', $params->get('c')); + + try { + new URLSearchParams([[1]]); + self::assertTrue(false); + } catch (TypeError $exception) { + self::assertTrue(true); + } + + try { + new URLSearchParams([[1, 2, 3]]); + self::assertTrue(false); + } catch (TypeError $exception) { + self::assertTrue(true); + } + } + + public static function getTestData(): array + { + $obj = new stdClass(); + $obj->{'+'} = '%C2'; + + $obj2 = new stdClass(); + $obj2->c = 'x'; + $obj2->a = '?'; + + $obj3 = new stdClass(); + $obj3->{"a\0b"} = '42'; + $obj3->{"c\u{D83D}"} = '23'; + $obj3->{"d\u{1234}"} = 'foo'; + + // Mimic error handling of JavaScript Object keys + $json = json_encode($obj3, JSON_INVALID_UTF8_SUBSTITUTE); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception(json_last_error_msg()); + } + $obj3 = json_decode($json, false, 512, 0); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception(json_last_error_msg()); + } + + return [ + ['input' => $obj, 'output' => [['+', '%C2']]], + [ + 'input' => $obj2, + 'output' => [ + ['c', 'x'], + ['a', '?'], + ], + ], + [ + 'input' => [ + ['c', 'x'], + ['a', '?'], + ], + 'output' => [ + ['c', 'x'], + ['a', '?'], + ], + ], + [ + 'input' => $obj3, + 'output' => [ + ["a\0b", '42'], + ["c\u{FFFD}", '23'], + ["d\u{1234}", 'foo'], + ], + ], + ]; + } + + public function test($input, array $output): void + { + $params = new URLSearchParams($input); + $i = 0; + foreach ($params as $param) { + self::assertSame($output[$i++], $param); + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsDeleteTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsDeleteTest.php new file mode 100644 index 0000000000..74c2739a4f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsDeleteTest.php @@ -0,0 +1,87 @@ +delete('a'); + self::assertSame('c=d', $params . ''); + $params = new URLSearchParams('a=a&b=b&a=a&c=c'); + $params->delete('a'); + self::assertSame('b=b&c=c', $params . ''); + $params = new URLSearchParams('a=a&=&b=b&c=c'); + $params->delete(''); + self::assertSame('a=a&b=b&c=c', $params . ''); + } + + public function testDeleteAppendMultiple(): void + { + $params = new URLSearchParams(); + $params->append('first', 1); + self::assertTrue($params->has('first')); + self::assertSame('1', $params->get('first')); + $params->delete('first'); + self::assertFalse($params->has('first')); + $params->append('first', 1); + $params->append('first', 10); + $params->delete('first'); + self::assertFalse($params->has('first')); + } + + public function testDeleteAllRemovesQuestionMark(): void + { + $url = new URL('http://example.com/?param1¶m2'); + $url->searchParams->delete('param1'); + $url->searchParams->delete('param2'); + self::assertSame('http://example.com/', $url->href); + self::assertSame('', $url->search); + } + + public function testDeleteNonExistentParamRemovesQuestionMark(): void + { + $url = new URL('http://example.com/?'); + $url->searchParams->delete('param1'); + self::assertSame('http://example.com/', $url->href); + self::assertSame('', $url->search); + } + + public function testChangingTheQueryOfAUrlWithAnOpaquePathCanImpactThePath(): void + { + $url = new URL('data:space ?test'); + self::assertTrue($url->searchParams->has('test')); + $url->searchParams->delete('test'); + self::assertFalse($url->searchParams->has('test')); + self::assertSame('', $url->search); + self::assertSame('space', $url->pathname); + self::assertSame('data:space', $url->href); + } + + public function testChangingTheQueryOfAUrlWithAnOpaquePathCanImpactThePathIfTheUrlHasNoFragment(): void + { + $url = new URL('data:space ?test#test'); + $url->searchParams->delete('test'); + self::assertSame('', $url->search); + self::assertSame('space ', $url->pathname); + self::assertSame('data:space #test', $url->href); + } + + public function testTwoArgumentDelete(): void + { + $params = new URLSearchParams(); + $params->append('a', 'b'); + $params->append('a', 'c'); + $params->append('a', 'd'); + $params->delete('a', 'c'); + self::assertSame('a=b&a=d', $params->toString()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsForeachTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsForeachTest.php new file mode 100644 index 0000000000..9fb74f3cbc --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsForeachTest.php @@ -0,0 +1,102 @@ +searchParams; + $c = []; + + foreach ($b as $i) { + $a->search = 'x=1&y=2&z=3'; + $c[] = $i; + } + + self::assertSame(['a', '1'], $c[0]); + self::assertSame(['y', '2'], $c[1]); + self::assertSame(['z', '3'], $c[2]); + } + + public function test3(): void + { + $a = new URL('http://a.b/c'); + $b = $a->searchParams; + self::assertFalse($b->valid()); + } + + public function testDeleteNextParamDuringIteration(): void + { + $url = new URL('http://localhost/query?param0=0¶m1=1¶m2=2'); + $searchParams = $url->searchParams; + $seen = []; + + foreach ($searchParams as $param) { + if ($param[0] === 'param0') { + $searchParams->delete('param1'); + } + + $seen[] = $param; + } + + self::assertSame(['param0', '0'], $seen[0]); + self::assertSame(['param2', '2'], $seen[1]); + } + + public function testDeleteCurrentParamDuringIteration(): void + { + $url = new URL('http://localhost/query?param0=0¶m1=1¶m2=2'); + $searchParams = $url->searchParams; + $seen = []; + + foreach ($searchParams as $param) { + if ($param[0] === 'param0') { + $searchParams->delete('param1'); + // 'param1=1' is now in the first slot, so the next iteration will see 'param2=2'. + } else { + $seen[] = $param; + } + } + + self::assertSame(['param2', '2'], $seen[0]); + } + + public function testDeleteEveryParamSeenDuringIteration(): void + { + $url = new URL('http://localhost/query?param0=0¶m1=1¶m2=2'); + $searchParams = $url->searchParams; + $seen = []; + + foreach ($searchParams as $param) { + $seen[] = $param[0]; + $searchParams->delete($param[0]); + } + + self::assertSame(['param0', 'param2'], $seen); + self::assertSame('param1=1', (string) $searchParams); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsGetAllTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsGetAllTest.php new file mode 100644 index 0000000000..4b27747b48 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsGetAllTest.php @@ -0,0 +1,42 @@ +getAll('a')); + self::assertSame(['d'], $params->getAll('c')); + self::assertSame([], $params->getAll('e')); + $params = new URLSearchParams('a=b&c=d&a=e'); + self::assertSame(['b', 'e'], $params->getAll('a')); + $params = new URLSearchParams('=b&c=d'); + self::assertSame(['b'], $params->getAll('')); + $params = new URLSearchParams('a=&c=d&a=e'); + self::assertSame(['', 'e'], $params->getAll('a')); + } + + public function testGetAllMultiple(): void + { + $params = new URLSearchParams('a=1&a=2&a=3&a'); + self::assertTrue($params->has('a')); + $matches = $params->getAll('a'); + self::assertTrue($matches && count($matches) === 4); + self::assertSame(['1', '2', '3', ''], $matches); + $params->set('a', 'one'); + self::assertSame('one', $params->get('a')); + $matches = $params->getAll('a'); + self::assertTrue($matches && count($matches) === 1); + self::assertSame(['one'], $matches); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsGetTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsGetTest.php new file mode 100644 index 0000000000..1d12b6ea95 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsGetTest.php @@ -0,0 +1,37 @@ +get('a')); + self::assertSame('d', $params->get('c')); + self::assertNull($params->get('e')); + $params = new URLSearchParams('a=b&c=d&a=e'); + self::assertSame('b', $params->get('a')); + $params = new URLSearchParams('=b&c=d'); + self::assertSame('b', $params->get('')); + $params = new URLSearchParams('a=&c=d&a=e'); + self::assertSame('', $params->get('a')); + } + + public function testMoreGetBasics(): void + { + $params = new URLSearchParams('first=second&third&&'); + + self::assertNotNull($params); + self::assertTrue($params->has('first'), 'constructor returned non-null value.'); + self::assertSame('second', $params->get('first'), 'Search params object has name "first"'); + self::assertSame('', $params->get('third'), 'Search params object has name "third" with the empty value.'); + self::assertNull($params->get('fourth'), 'Search params object has no "fourth" name and value.'); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsHasTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsHasTest.php new file mode 100644 index 0000000000..25ebf7b263 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsHasTest.php @@ -0,0 +1,51 @@ +has('a')); + self::assertTrue($params->has('c')); + self::assertFalse($params->has('e')); + $params = new URLSearchParams('a=b&c=d&a=e'); + self::assertTrue($params->has('a')); + $params = new URLSearchParams('=b&c=d'); + self::assertTrue($params->has('')); + } + + public function testHasFollowingDelete(): void + { + $params = new URLSearchParams('a=b&c=d&&'); + $params->append('first', 1); + $params->append('first', 2); + self::assertTrue($params->has('a')); + self::assertTrue($params->has('c')); + self::assertTrue($params->has('first')); + self::assertFalse($params->has('d')); + $params->delete('first'); + self::assertFalse($params->has('first')); + } + + public function testTwoArgumentHas(): void + { + $params = new URLSearchParams('a=b&a=d&c&e&'); + self::assertTrue($params->has('a', 'b')); + self::assertFalse($params->has('a', 'c')); + self::assertTrue($params->has('a', 'd')); + self::assertTrue($params->has('e', '')); + $params->append('first', 'null'); + self::assertFalse($params->has('first', '')); + self::assertTrue($params->has('first', 'null')); + $params->delete('a', 'b'); + self::assertTrue($params->has('a', 'd')); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSetTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSetTest.php new file mode 100644 index 0000000000..f9aeefa455 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSetTest.php @@ -0,0 +1,63 @@ +set('a', 'B'); + self::assertSame('a=B&c=d', $params . ''); + $params = new URLSearchParams('a=b&c=d&a=e'); + $params->set('a', 'B'); + self::assertSame('a=B&c=d', $params . ''); + $params->set('e', 'f'); + self::assertSame('a=B&c=d&e=f', $params . ''); + } + + public function testURLSearchParamsSet(): void + { + $params = new URLSearchParams('a=1&a=2&a=3'); + + self::assertTrue( + $params->has('a'), + 'Search params object has name "a"' + ); + self::assertSame( + '1', + $params->get('a'), + 'Search params object has name "a" with a value of "1"' + ); + + $params->set('first', 4); + + self::assertTrue( + $params->has('a'), + 'Search params object has name "a"' + ); + self::assertSame( + '1', + $params->get('a'), + 'Search params object has name "a" with value "1"' + ); + + $params->set('a', 4); + + self::assertTrue( + $params->has('a'), + 'Search params object has name "a"' + ); + self::assertSame( + '4', + $params->get('a'), + 'Search params object has name "a" with value "4"' + ); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSizeTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSizeTest.php new file mode 100644 index 0000000000..5f739d5126 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSizeTest.php @@ -0,0 +1,63 @@ +size); + self::assertCount(3, $params); + + $params->delete('a'); + self::assertSame(1, $params->size); + self::assertCount(1, $params); + } + + public function testSizeAndAddition(): void + { + $params = new URLSearchParams('a=1&b=2&a=3'); + self::assertSame(3, $params->size); + self::assertCount(3, $params); + + $params->append('b', '4'); + self::assertSame(4, $params->size); + self::assertCount(4, $params); + } + + public function testSizeWhenObtainedFromAURL(): void + { + $url = new URL("http://localhost/query?a=1&b=2&a=3"); + self::assertSame(3, $url->searchParams->size); + self::assertCount(3, $url->searchParams); + + $url->searchParams->delete('a'); + self::assertSame(1, $url->searchParams->size); + self::assertCount(1, $url->searchParams); + + $url->searchParams->append('b', '4'); + self::assertSame(2, $url->searchParams->size); + self::assertCount(2, $url->searchParams); + } + + public function testSizeWhenObtainedFromAURLAndUsingSearch(): void + { + $url = new URL("http://localhost/query?a=1&b=2&a=3"); + self::assertSame(3, $url->searchParams->size); + self::assertCount(3, $url->searchParams); + + $url->search = '?'; + self::assertSame(0, $url->searchParams->size); + self::assertCount(0, $url->searchParams); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSortTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSortTest.php new file mode 100644 index 0000000000..b5dcab64e6 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsSortTest.php @@ -0,0 +1,105 @@ + 'z=b&a=b&z=a&a=a', + 'output' => [ + ['a', 'b'], + ['a', 'a'], + ['z', 'b'], + ['z', 'a'], + ], + ], + [ + 'input' => "\u{FFFD}=x&\u{FFFC}&\u{FFFD}=a", + 'output' => [ + ["\u{FFFC}", ''], + ["\u{FFFD}", 'x'], + ["\u{FFFD}", 'a'], + ], + ], + [ + 'input' => 'ffi&🌈', + 'output' => [['🌈', ''], ['ffi', '']], + ], + [ + 'input' => "é&e\u{FFFD}&e\u{0301}", + 'output' => [ + ["e\u{0301}", ''], + ["e\u{FFFD}", ''], + ['é', ''], + ], + ], + [ + 'input' => 'z=z&a=a&z=y&a=b&z=x&a=c&z=w&a=d&z=v&a=e&z=u&a=f&z=t&a=g', + 'output' => [ + ['a', 'a'], + ['a', 'b'], + ['a', 'c'], + ['a', 'd'], + ['a', 'e'], + ['a', 'f'], + ['a', 'g'], + ['z', 'z'], + ['z', 'y'], + ['z', 'x'], + ['z', 'w'], + ['z', 'v'], + ['z', 'u'], + ['z', 't'], + ], + ], + [ + 'input' => 'bbb&bb&aaa&aa=x&aa=y', + 'output' => [ + ['aa', 'x'], + ['aa', 'y'], + ['aaa', ''], + ['bb', ''], + ['bbb', ''], + ], + ], + [ + 'input' => 'z=z&=f&=t&=x', + 'output' => [['', 'f'], ['', 't'], ['', 'x'], ['z', 'z']], + ], + [ + 'input' => 'a🌈&a💩', + 'output' => [['a🌈', ''], ['a💩', '']], + ], + ]; + } + + public function testSort(string $input, array $output): void + { + $url = new URL('?' . $input, 'https://example/'); + $url->searchParams->sort(); + $params = new URLSearchParams($url->search); + $i = 0; + foreach ($params as $param) { + self::assertSame($output[$i++], $param); + } + } + + public function testSortingNonExistentParamsRemovesQuestionMark(): void + { + $url = new URL('http://example.com/?'); + $url->searchParams->sort(); + self::assertSame('http://example.com/', $url->href); + self::assertSame('', $url->search); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsStringifierTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsStringifierTest.php new file mode 100644 index 0000000000..4ab081defe --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsStringifierTest.php @@ -0,0 +1,175 @@ +append('a', 'b c'); + self::assertSame('a=b+c', $params . ''); + $params->delete('a'); + $params->append('a b', 'c'); + self::assertSame('a+b=c', $params . ''); + } + + public function testSerializeEmptyValue(): void + { + $params = new URLSearchParams(); + $params->append('a', ''); + self::assertSame('a=', $params . ''); + $params->append('a', ''); + self::assertSame('a=&a=', $params . ''); + $params->append('', 'b'); + self::assertSame('a=&a=&=b', $params . ''); + $params->append('', ''); + self::assertSame('a=&a=&=b&=', $params . ''); + $params->append('', ''); + self::assertSame('a=&a=&=b&=&=', $params . ''); + } + + public function testSerializeEmptyName(): void + { + $params = new URLSearchParams(); + $params->append('', 'b'); + self::assertSame('=b', $params . ''); + $params->append('', 'b'); + self::assertSame('=b&=b', $params . ''); + } + + public function testSerialzieEmptyNameAndValue(): void + { + $params = new URLSearchParams(); + $params->append('', ''); + self::assertSame('=', $params . ''); + $params->append('', ''); + self::assertSame('=&=', $params . ''); + } + + public function testSerialziePlusSign(): void + { + $params = new URLSearchParams(); + $params->append('a', 'b+c'); + self::assertSame('a=b%2Bc', $params . ''); + $params->delete('a'); + $params->append('a+b', 'c'); + self::assertSame('a%2Bb=c', $params . ''); + } + + public function testSerializeEqualSign(): void + { + $params = new URLSearchParams(); + $params->append('=', 'a'); + self::assertSame('%3D=a', $params . ''); + $params->append('b', '='); + self::assertSame('%3D=a&b=%3D', $params . ''); + } + + public function testSerializeAmpersand(): void + { + $params = new URLSearchParams(); + $params->append('&', 'a'); + self::assertSame('%26=a', $params . ''); + $params->append('b', '&'); + self::assertSame('%26=a&b=%26', $params . ''); + } + + public function testSerializeSpecialChars(): void + { + $params = new URLSearchParams(); + $params->append('a', '*-._'); + self::assertSame('a=*-._', $params . ''); + $params->delete('a'); + $params->append('*-._', 'c'); + self::assertSame('*-._=c', $params . ''); + } + + public function testSerializePercentSign(): void + { + $params = new URLSearchParams(); + $params->append('a', 'b%c'); + self::assertSame('a=b%25c', $params . ''); + $params->delete('a'); + $params->append('a%b', 'c'); + self::assertSame('a%25b=c', $params . ''); + + $params = new URLSearchParams('id=0&value=%'); + self::assertSame('id=0&value=%25', $params . ''); + } + + public function testSerializeNullByte(): void + { + $params = new URLSearchParams(); + $params->append('a', "b\0c"); + self::assertSame('a=b%00c', $params . ''); + $params->delete('a'); + $params->append("a\0b", 'c'); + self::assertSame('a%00b=c', $params . ''); + } + + public function testSerializeUnicodePileOfPoo(): void + { + $params = new URLSearchParams(); + $params->append('a', "b\u{1F4A9}c"); + self::assertSame('a=b%F0%9F%92%A9c', $params . ''); + $params->delete('a'); + $params->append("a\u{1F4A9}b", 'c'); + self::assertSame('a%F0%9F%92%A9b=c', $params . ''); + } + + public function testStringification(): void + { + $params = new URLSearchParams('a=b&c=d&&e&&'); + self::assertSame('a=b&c=d&e=', $params->toString()); + $params = new URLSearchParams('a = b &a=b&c=d%20'); + self::assertSame('a+=+b+&a=b&c=d+', $params->toString()); + // The lone '=' _does_ survive the roundtrip. + $params = new URLSearchParams('a=&a=b'); + self::assertSame('a=&a=b', $params->toString()); + + $params = new URLSearchParams('b=%2sf%2a'); + self::assertSame('b=%252sf*', $params->toString()); + + $params = new URLSearchParams('b=%2%2af%2a'); + self::assertSame('b=%252*f*', $params->toString()); + + $params = new URLSearchParams('b=%%2a'); + self::assertSame('b=%25*', $params->toString()); + } + + public function testURLSearchParamsConnectedToURL(): void + { + $url = new URL('http://www.example.com/?a=b,c'); + $params = $url->searchParams; + + self::assertSame('http://www.example.com/?a=b,c', $url->toString()); + self::assertSame('a=b%2Cc', $params->toString()); + + $params->append('x', 'y'); + + self::assertSame( + 'http://www.example.com/?a=b%2Cc&x=y', + $url->toString() + ); + self::assertSame('a=b%2Cc&x=y', $params->toString()); + } + + public function testURLSearchParamsMustNotDoNewlineNormalization(): void + { + $url = new URL('http://www.example.com/'); + $params = $url->searchParams; + + $params->append("a\nb", "c\rd"); + $params->append("e\n\rf", "g\r\nh"); + + self::assertSame('a%0Ab=c%0Dd&e%0A%0Df=g%0D%0Ah', $params->toString()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsTest.php new file mode 100644 index 0000000000..e16f18887b --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSearchParamsTest.php @@ -0,0 +1,87 @@ +searchParams); + $searchParams = $url->searchParams; + self::assertSame($searchParams, $url->searchParams); + } + + /** + * Test URL.searchParams updating, clearing. + */ + public function testURLSearchParamsUpdatingClearing(): void + { + $url = new URL('http://example.org/?a=b', 'about:blank'); + self::assertNotNull($url->searchParams); + $searchParams = $url->searchParams; + self::assertSame('a=b', $searchParams->toString()); + + $searchParams->set('a', 'b'); + self::assertSame('a=b', $url->searchParams->toString()); + self::assertSame('?a=b', $url->search); + $url->search = ''; + self::assertSame('', $url->searchParams->toString()); + self::assertSame('', $url->search); + self::assertSame('', $searchParams->toString()); + } + + public function testURLSearchParamsSetterInvalidValues(): void + { + $this->expectException(TypeError::class); + $urlString = 'http://example.org'; + $url = new URL($urlString, 'about:blank'); + $url->searchParams = new URLSearchParams($urlString); + } + + public function testURLSearchParamsAndURLSearchSettersUpdatePropagation(): void + { + $url = new URL('http://example.org/file?a=b&c=d'); + self::assertInstanceOf(URLSearchParams::class, $url->searchParams); + $searchParams = $url->searchParams; + self::assertSame('?a=b&c=d', $url->search); + self::assertSame('a=b&c=d', $searchParams->toString()); + + // Test that setting 'search' propagates to the URL object's query + // object + $url->search = 'e=f&g=h'; + self::assertSame('?e=f&g=h', $url->search); + self::assertSame('e=f&g=h', $url->searchParams->toString()); + + // ...and same, but with a leading '?' + $url->search = '?e=f&g=h'; + self::assertSame('?e=f&g=h', $url->search); + self::assertSame('e=f&g=h', $url->searchParams->toString()); + + // And in the other direction, altering searchParams propagates back + // to 'search' + $searchParams->append('i', ' j '); + self::assertSame('?e=f&g=h&i=+j+', $url->search); + self::assertSame('e=f&g=h&i=+j+', $url->searchParams->toString()); + self::assertSame(' j ', $searchParams->get('i')); + + $searchParams->set('e', 'updated'); + self::assertSame('?e=updated&g=h&i=+j+', $url->search); + self::assertSame('e=updated&g=h&i=+j+', $url->searchParams->__toString()); + + $url2 = new URL('http://example.org/file??a=b&c=d', 'about:blank'); + self::assertSame('??a=b&c=d', $url2->search); + self::assertSame('%3Fa=b&c=d', $url2->searchParams->toString()); + + $url2->href = 'http://example.org/file??a=b'; + self::assertSame('??a=b', $url2->search); + self::assertSame('%3Fa=b', $url2->searchParams->toString()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSettersTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSettersTest.php new file mode 100644 index 0000000000..67bbf8ef04 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLSettersTest.php @@ -0,0 +1,37 @@ + $tests) { + if ($key === 'comment') { + continue; + } + + foreach ($tests as $inputs) { + unset($inputs['comment']); + $inputs['setter'] = $key; + + yield [$inputs]; + } + } + } + + public function testSetters(array $input): void + { + $url = new URL($input['href']); + $url->{$input['setter']} = $input['new_value']; + foreach ($input['expected'] as $attribute => $value) { + self::assertSame($value, $url->$attribute, $attribute); + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLStaticsCanParseTest.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLStaticsCanParseTest.php new file mode 100644 index 0000000000..b9c40d9e1d --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/URLStaticsCanParseTest.php @@ -0,0 +1,20 @@ +href) . '"', json_encode($a, 0)); + self::assertSame('"' . $a->href . '"', $a->toJSON()); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/WhatwgTestCase.php b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/WhatwgTestCase.php new file mode 100644 index 0000000000..c445d5f81f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/rowbot/url/tests/WhatWg/WhatwgTestCase.php @@ -0,0 +1,65 @@ +get($url, static function () use ($url): string { + self::$client = self::$client ?? new Client([ + 'base_uri' => self::WHATWG_BASE_URI, + 'http_errors' => true, + ]); + $response = self::$client->get($url); + + // Replace all unpaired surrogate escape sequences with a \uFFFD escape sequence to avoid + // json_decode() having a stroke and emitting a JSON_ERROR_UTF16 error causing the decode + // to fail + $body = preg_replace( + '/ + (?(DEFINE) + (?\\\u[Dd][89AaBb][[:xdigit:]][[:xdigit:]]) + (?\\\u[Dd][C-Fc-f][[:xdigit:]][[:xdigit:]]) + ) + + # Match a low surrogate not preceded by a high surrogate + (?getBody() + ); + + // Remove comments and check to make sure it is valid JSON. + $json = array_filter(json_decode($body, true, self::JSON_DEPTH, 0), 'is_array'); + + return json_encode($json, 0); + }); + + return json_decode($data, true, self::JSON_DEPTH, 0); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/CHANGELOG.md b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/CHANGELOG.md new file mode 100644 index 0000000000..24464d2685 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/CHANGELOG.md @@ -0,0 +1,4 @@ +# CHANGELOG + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/LICENSE b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/LICENSE new file mode 100644 index 0000000000..0ed3a24655 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/README.md b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/README.md new file mode 100644 index 0000000000..c892e050d3 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/README.md @@ -0,0 +1,27 @@ +# Symfony Deprecation Contracts + +A generic function and convention to trigger deprecation notices. + +This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices. + +By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component, +the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments. + +The function requires at least 3 arguments: + +- the name of the Composer package that is triggering the deprecation +- the version of the package that introduced the deprecation +- the message of the deprecation +- more arguments can be provided: they will be inserted in the message using `printf()` formatting + +Example: + +```php +trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin'); +``` + +This will generate the following message: +`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.` + +While not recommended, the deprecation notices can be completely ignored by declaring an empty +`function trigger_deprecation() {}` in your application. diff --git a/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/composer.json b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/composer.json new file mode 100644 index 0000000000..0615c6016a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/deprecation-contracts", + "type": "library", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.1" + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/function.php b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/function.php new file mode 100644 index 0000000000..2d56512ba3 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/deprecation-contracts/function.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (!function_exists('trigger_deprecation')) { + /** + * Triggers a silenced deprecation notice. + * + * @param string $package The name of the Composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The message of the deprecation + * @param mixed ...$args Values to insert in the message using printf() formatting + * + * @author Nicolas Grekas + */ + function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void + { + @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/Ctype.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/Ctype.php new file mode 100644 index 0000000000..ba75a2c95f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/Ctype.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Ctype; + +/** + * Ctype implementation through regex. + * + * @internal + * + * @author Gert de Pagter + */ +final class Ctype +{ + /** + * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. + * + * @see https://php.net/ctype-alnum + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alnum($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is a letter, FALSE otherwise. + * + * @see https://php.net/ctype-alpha + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alpha($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); + } + + /** + * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. + * + * @see https://php.net/ctype-cntrl + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_cntrl($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); + } + + /** + * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. + * + * @see https://php.net/ctype-digit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_digit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. + * + * @see https://php.net/ctype-graph + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_graph($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); + } + + /** + * Returns TRUE if every character in text is a lowercase letter. + * + * @see https://php.net/ctype-lower + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_lower($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); + } + + /** + * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. + * + * @see https://php.net/ctype-print + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_print($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); + } + + /** + * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. + * + * @see https://php.net/ctype-punct + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_punct($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); + } + + /** + * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. + * + * @see https://php.net/ctype-space + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_space($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); + } + + /** + * Returns TRUE if every character in text is an uppercase letter. + * + * @see https://php.net/ctype-upper + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_upper($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); + } + + /** + * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. + * + * @see https://php.net/ctype-xdigit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_xdigit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); + } + + /** + * Converts integers to their char versions according to normal ctype behaviour, if needed. + * + * If an integer between -128 and 255 inclusive is provided, + * it is interpreted as the ASCII value of a single character + * (negative values have 256 added in order to allow characters in the Extended ASCII range). + * Any other integer is interpreted as a string containing the decimal digits of the integer. + * + * @param mixed $int + * @param string $function + * + * @return mixed + */ + private static function convert_int_to_char_for_ctype($int, $function) + { + if (!\is_int($int)) { + return $int; + } + + if ($int < -128 || $int > 255) { + return (string) $int; + } + + if (\PHP_VERSION_ID >= 80100) { + @trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED); + } + + if ($int < 0) { + $int += 256; + } + + return \chr($int); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/LICENSE b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/LICENSE new file mode 100644 index 0000000000..7536caeae8 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/README.md b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/README.md new file mode 100644 index 0000000000..37b2b7d5c4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/README.md @@ -0,0 +1,10 @@ +# Symfony Polyfill / Ctype + +This component provides `ctype_*` functions to users who run php versions without the ctype extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +# License + +This library is released under the [MIT license](LICENSE). diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/bootstrap.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/bootstrap.php new file mode 100644 index 0000000000..d54524b31b --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/bootstrap.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('ctype_alnum')) { + function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit($text) { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph($text) { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower($text) { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print($text) { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct($text) { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space($text) { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper($text) { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/bootstrap80.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/bootstrap80.php new file mode 100644 index 0000000000..ab2f8611da --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/bootstrap80.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (!function_exists('ctype_alnum')) { + function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/composer.json b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/composer.json new file mode 100644 index 0000000000..76f93942e8 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-ctype/composer.json @@ -0,0 +1,47 @@ +{ + "name": "symfony/polyfill-ctype", + "type": "library", + "description": "Symfony polyfill for ctype functions", + "keywords": [ + "polyfill", + "compatibility", + "portable", + "ctype" + ], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/LICENSE b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/LICENSE new file mode 100644 index 0000000000..6e3afce692 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Normalizer.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Normalizer.php new file mode 100644 index 0000000000..81704ab376 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Normalizer.php @@ -0,0 +1,310 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Intl\Normalizer; + +/** + * Normalizer is a PHP fallback implementation of the Normalizer class provided by the intl extension. + * + * It has been validated with Unicode 6.3 Normalization Conformance Test. + * See http://www.unicode.org/reports/tr15/ for detailed info about Unicode normalizations. + * + * @author Nicolas Grekas + * + * @internal + */ +class Normalizer +{ + public const FORM_D = \Normalizer::FORM_D; + public const FORM_KD = \Normalizer::FORM_KD; + public const FORM_C = \Normalizer::FORM_C; + public const FORM_KC = \Normalizer::FORM_KC; + public const NFD = \Normalizer::NFD; + public const NFKD = \Normalizer::NFKD; + public const NFC = \Normalizer::NFC; + public const NFKC = \Normalizer::NFKC; + + private static $C; + private static $D; + private static $KD; + private static $cC; + private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; + private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; + + public static function isNormalized(string $s, int $form = self::FORM_C) + { + if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) { + return false; + } + if (!isset($s[strspn($s, self::$ASCII)])) { + return true; + } + if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) { + return true; + } + + return self::normalize($s, $form) === $s; + } + + public static function normalize(string $s, int $form = self::FORM_C) + { + if (!preg_match('//u', $s)) { + return false; + } + + switch ($form) { + case self::NFC: $C = true; $K = false; break; + case self::NFD: $C = false; $K = false; break; + case self::NFKC: $C = true; $K = true; break; + case self::NFKD: $C = false; $K = true; break; + default: + if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) { + return $s; + } + + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form'); + } + + if ('' === $s) { + return ''; + } + + if ($K && null === self::$KD) { + self::$KD = self::getData('compatibilityDecomposition'); + } + + if (null === self::$D) { + self::$D = self::getData('canonicalDecomposition'); + self::$cC = self::getData('combiningClass'); + } + + if (null !== $mbEncoding = (2 /* MB_OVERLOAD_STRING */ & (int) \ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) { + mb_internal_encoding('8bit'); + } + + $r = self::decompose($s, $K); + + if ($C) { + if (null === self::$C) { + self::$C = self::getData('canonicalComposition'); + } + + $r = self::recompose($r); + } + if (null !== $mbEncoding) { + mb_internal_encoding($mbEncoding); + } + + return $r; + } + + private static function recompose($s) + { + $ASCII = self::$ASCII; + $compMap = self::$C; + $combClass = self::$cC; + $ulenMask = self::$ulenMask; + + $result = $tail = ''; + + $i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"]; + $len = \strlen($s); + + $lastUchr = substr($s, 0, $i); + $lastUcls = isset($combClass[$lastUchr]) ? 256 : 0; + + while ($i < $len) { + if ($s[$i] < "\x80") { + // ASCII chars + + if ($tail) { + $lastUchr .= $tail; + $tail = ''; + } + + if ($j = strspn($s, $ASCII, $i + 1)) { + $lastUchr .= substr($s, $i, $j); + $i += $j; + } + + $result .= $lastUchr; + $lastUchr = $s[$i]; + $lastUcls = 0; + ++$i; + continue; + } + + $ulen = $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + + if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr + || $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr + || $lastUcls) { + // Table lookup and combining chars composition + + $ucls = $combClass[$uchr] ?? 0; + + if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) { + $lastUchr = $compMap[$lastUchr.$uchr]; + } elseif ($lastUcls = $ucls) { + $tail .= $uchr; + } else { + if ($tail) { + $lastUchr .= $tail; + $tail = ''; + } + + $result .= $lastUchr; + $lastUchr = $uchr; + } + } else { + // Hangul chars + + $L = \ord($lastUchr[2]) - 0x80; + $V = \ord($uchr[2]) - 0xA1; + $T = 0; + + $uchr = substr($s, $i + $ulen, 3); + + if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") { + $T = \ord($uchr[2]) - 0xA7; + 0 > $T && $T += 0x40; + $ulen += 3; + } + + $L = 0xAC00 + ($L * 21 + $V) * 28 + $T; + $lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F); + } + + $i += $ulen; + } + + return $result.$lastUchr.$tail; + } + + private static function decompose($s, $c) + { + $result = ''; + + $ASCII = self::$ASCII; + $decompMap = self::$D; + $combClass = self::$cC; + $ulenMask = self::$ulenMask; + if ($c) { + $compatMap = self::$KD; + } + + $c = []; + $i = 0; + $len = \strlen($s); + + while ($i < $len) { + if ($s[$i] < "\x80") { + // ASCII chars + + if ($c) { + ksort($c); + $result .= implode('', $c); + $c = []; + } + + $j = 1 + strspn($s, $ASCII, $i + 1); + $result .= substr($s, $i, $j); + $i += $j; + continue; + } + + $ulen = $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) { + // Table lookup + + if ($uchr !== $j = $compatMap[$uchr] ?? ($decompMap[$uchr] ?? $uchr)) { + $uchr = $j; + + $j = \strlen($uchr); + $ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"]; + + if ($ulen != $j) { + // Put trailing chars in $s + + $j -= $ulen; + $i -= $j; + + if (0 > $i) { + $s = str_repeat(' ', -$i).$s; + $len -= $i; + $i = 0; + } + + while ($j--) { + $s[$i + $j] = $uchr[$ulen + $j]; + } + + $uchr = substr($uchr, 0, $ulen); + } + } + if (isset($combClass[$uchr])) { + // Combining chars, for sorting + + if (!isset($c[$combClass[$uchr]])) { + $c[$combClass[$uchr]] = ''; + } + $c[$combClass[$uchr]] .= $uchr; + continue; + } + } else { + // Hangul chars + + $uchr = unpack('C*', $uchr); + $j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80; + + $uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588)) + ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28)); + + if ($j %= 28) { + $uchr .= $j < 25 + ? ("\xE1\x86".\chr(0xA7 + $j)) + : ("\xE1\x87".\chr(0x67 + $j)); + } + } + if ($c) { + ksort($c); + $result .= implode('', $c); + $c = []; + } + + $result .= $uchr; + } + + if ($c) { + ksort($c); + $result .= implode('', $c); + } + + return $result; + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/README.md b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/README.md new file mode 100644 index 0000000000..cf4235e3b4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/README.md @@ -0,0 +1,12 @@ +# Symfony Polyfill / Intl: Normalizer + +This component provides a fallback implementation for the +[`Normalizer`](https://php.net/Normalizer) class provided +by the [Intl](https://php.net/intl) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +# License + +This library is released under the [MIT license](LICENSE). diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php new file mode 100644 index 0000000000..0fdfc890a2 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php @@ -0,0 +1,17 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '΅' => '΅', + 'Ά' => 'Ά', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ὲ' => 'ὲ', + 'ὴ' => 'ὴ', + 'ὶ' => 'ὶ', + 'ὸ' => 'ὸ', + 'ὺ' => 'ὺ', + 'ὼ' => 'ὼ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'ᾼ' => 'ᾼ', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Ὴ' => 'Ὴ', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ὼ' => 'Ὼ', + 'ῼ' => 'ῼ', + '↚' => '↚', + '↛' => '↛', + '↮' => '↮', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '∄' => '∄', + '∉' => '∉', + '∌' => '∌', + '∤' => '∤', + '∦' => '∦', + '≁' => '≁', + '≄' => '≄', + '≇' => '≇', + '≉' => '≉', + '≠' => '≠', + '≢' => '≢', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≴' => '≴', + '≵' => '≵', + '≸' => '≸', + '≹' => '≹', + '⊀' => '⊀', + '⊁' => '⊁', + '⊄' => '⊄', + '⊅' => '⊅', + '⊈' => '⊈', + '⊉' => '⊉', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + 'が' => 'が', + 'ぎ' => 'ぎ', + 'ぐ' => 'ぐ', + 'げ' => 'げ', + 'ご' => 'ご', + 'ざ' => 'ざ', + 'じ' => 'じ', + 'ず' => 'ず', + 'ぜ' => 'ぜ', + 'ぞ' => 'ぞ', + 'だ' => 'だ', + 'ぢ' => 'ぢ', + 'づ' => 'づ', + 'で' => 'で', + 'ど' => 'ど', + 'ば' => 'ば', + 'ぱ' => 'ぱ', + 'び' => 'び', + 'ぴ' => 'ぴ', + 'ぶ' => 'ぶ', + 'ぷ' => 'ぷ', + 'べ' => 'べ', + 'ぺ' => 'ぺ', + 'ぼ' => 'ぼ', + 'ぽ' => 'ぽ', + 'ゔ' => 'ゔ', + 'ゞ' => 'ゞ', + 'ガ' => 'ガ', + 'ギ' => 'ギ', + 'グ' => 'グ', + 'ゲ' => 'ゲ', + 'ゴ' => 'ゴ', + 'ザ' => 'ザ', + 'ジ' => 'ジ', + 'ズ' => 'ズ', + 'ゼ' => 'ゼ', + 'ゾ' => 'ゾ', + 'ダ' => 'ダ', + 'ヂ' => 'ヂ', + 'ヅ' => 'ヅ', + 'デ' => 'デ', + 'ド' => 'ド', + 'バ' => 'バ', + 'パ' => 'パ', + 'ビ' => 'ビ', + 'ピ' => 'ピ', + 'ブ' => 'ブ', + 'プ' => 'プ', + 'ベ' => 'ベ', + 'ペ' => 'ペ', + 'ボ' => 'ボ', + 'ポ' => 'ポ', + 'ヴ' => 'ヴ', + 'ヷ' => 'ヷ', + 'ヸ' => 'ヸ', + 'ヹ' => 'ヹ', + 'ヺ' => 'ヺ', + 'ヾ' => 'ヾ', + '𑂚' => '𑂚', + '𑂜' => '𑂜', + '𑂫' => '𑂫', + '𑄮' => '𑄮', + '𑄯' => '𑄯', + '𑍋' => '𑍋', + '𑍌' => '𑍌', + '𑒻' => '𑒻', + '𑒼' => '𑒼', + '𑒾' => '𑒾', + '𑖺' => '𑖺', + '𑖻' => '𑖻', + '𑤸' => '𑤸', +); diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php new file mode 100644 index 0000000000..5a3e8e0969 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php @@ -0,0 +1,2065 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '̀' => '̀', + '́' => '́', + '̓' => '̓', + '̈́' => '̈́', + 'ʹ' => 'ʹ', + ';' => ';', + '΅' => '΅', + 'Ά' => 'Ά', + '·' => '·', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'क़' => 'क़', + 'ख़' => 'ख़', + 'ग़' => 'ग़', + 'ज़' => 'ज़', + 'ड़' => 'ड़', + 'ढ़' => 'ढ़', + 'फ़' => 'फ़', + 'य़' => 'य़', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ড়' => 'ড়', + 'ঢ়' => 'ঢ়', + 'য়' => 'য়', + 'ਲ਼' => 'ਲ਼', + 'ਸ਼' => 'ਸ਼', + 'ਖ਼' => 'ਖ਼', + 'ਗ਼' => 'ਗ਼', + 'ਜ਼' => 'ਜ਼', + 'ਫ਼' => 'ਫ਼', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ଡ଼' => 'ଡ଼', + 'ଢ଼' => 'ଢ଼', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'གྷ' => 'གྷ', + 'ཌྷ' => 'ཌྷ', + 'དྷ' => 'དྷ', + 'བྷ' => 'བྷ', + 'ཛྷ' => 'ཛྷ', + 'ཀྵ' => 'ཀྵ', + 'ཱི' => 'ཱི', + 'ཱུ' => 'ཱུ', + 'ྲྀ' => 'ྲྀ', + 'ླྀ' => 'ླྀ', + 'ཱྀ' => 'ཱྀ', + 'ྒྷ' => 'ྒྷ', + 'ྜྷ' => 'ྜྷ', + 'ྡྷ' => 'ྡྷ', + 'ྦྷ' => 'ྦྷ', + 'ྫྷ' => 'ྫྷ', + 'ྐྵ' => 'ྐྵ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ά' => 'ά', + 'ὲ' => 'ὲ', + 'έ' => 'έ', + 'ὴ' => 'ὴ', + 'ή' => 'ή', + 'ὶ' => 'ὶ', + 'ί' => 'ί', + 'ὸ' => 'ὸ', + 'ό' => 'ό', + 'ὺ' => 'ὺ', + 'ύ' => 'ύ', + 'ὼ' => 'ὼ', + 'ώ' => 'ώ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'Ά' => 'Ά', + 'ᾼ' => 'ᾼ', + 'ι' => 'ι', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Έ' => 'Έ', + 'Ὴ' => 'Ὴ', + 'Ή' => 'Ή', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ΐ' => 'ΐ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + 'Ί' => 'Ί', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ΰ' => 'ΰ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ύ' => 'Ύ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + '΅' => '΅', + '`' => '`', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ό' => 'Ό', + 'Ὼ' => 'Ὼ', + 'Ώ' => 'Ώ', + 'ῼ' => 'ῼ', + '´' => '´', + ' ' => ' ', + ' ' => ' ', + 'Ω' => 'Ω', + 'K' => 'K', + 'Å' => 'Å', + '↚' => '↚', + '↛' => '↛', + '↮' => '↮', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '∄' => '∄', + '∉' => '∉', + '∌' => '∌', + '∤' => '∤', + '∦' => '∦', + '≁' => '≁', + '≄' => '≄', + '≇' => '≇', + '≉' => '≉', + '≠' => '≠', + '≢' => '≢', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≴' => '≴', + '≵' => '≵', + '≸' => '≸', + '≹' => '≹', + '⊀' => '⊀', + '⊁' => '⊁', + '⊄' => '⊄', + '⊅' => '⊅', + '⊈' => '⊈', + '⊉' => '⊉', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + '〈' => '〈', + '〉' => '〉', + '⫝̸' => '⫝̸', + 'が' => 'が', + 'ぎ' => 'ぎ', + 'ぐ' => 'ぐ', + 'げ' => 'げ', + 'ご' => 'ご', + 'ざ' => 'ざ', + 'じ' => 'じ', + 'ず' => 'ず', + 'ぜ' => 'ぜ', + 'ぞ' => 'ぞ', + 'だ' => 'だ', + 'ぢ' => 'ぢ', + 'づ' => 'づ', + 'で' => 'で', + 'ど' => 'ど', + 'ば' => 'ば', + 'ぱ' => 'ぱ', + 'び' => 'び', + 'ぴ' => 'ぴ', + 'ぶ' => 'ぶ', + 'ぷ' => 'ぷ', + 'べ' => 'べ', + 'ぺ' => 'ぺ', + 'ぼ' => 'ぼ', + 'ぽ' => 'ぽ', + 'ゔ' => 'ゔ', + 'ゞ' => 'ゞ', + 'ガ' => 'ガ', + 'ギ' => 'ギ', + 'グ' => 'グ', + 'ゲ' => 'ゲ', + 'ゴ' => 'ゴ', + 'ザ' => 'ザ', + 'ジ' => 'ジ', + 'ズ' => 'ズ', + 'ゼ' => 'ゼ', + 'ゾ' => 'ゾ', + 'ダ' => 'ダ', + 'ヂ' => 'ヂ', + 'ヅ' => 'ヅ', + 'デ' => 'デ', + 'ド' => 'ド', + 'バ' => 'バ', + 'パ' => 'パ', + 'ビ' => 'ビ', + 'ピ' => 'ピ', + 'ブ' => 'ブ', + 'プ' => 'プ', + 'ベ' => 'ベ', + 'ペ' => 'ペ', + 'ボ' => 'ボ', + 'ポ' => 'ポ', + 'ヴ' => 'ヴ', + 'ヷ' => 'ヷ', + 'ヸ' => 'ヸ', + 'ヹ' => 'ヹ', + 'ヺ' => 'ヺ', + 'ヾ' => 'ヾ', + '豈' => '豈', + '更' => '更', + '車' => '車', + '賈' => '賈', + '滑' => '滑', + '串' => '串', + '句' => '句', + '龜' => '龜', + '龜' => '龜', + '契' => '契', + '金' => '金', + '喇' => '喇', + '奈' => '奈', + '懶' => '懶', + '癩' => '癩', + '羅' => '羅', + '蘿' => '蘿', + '螺' => '螺', + '裸' => '裸', + '邏' => '邏', + '樂' => '樂', + '洛' => '洛', + '烙' => '烙', + '珞' => '珞', + '落' => '落', + '酪' => '酪', + '駱' => '駱', + '亂' => '亂', + '卵' => '卵', + '欄' => '欄', + '爛' => '爛', + '蘭' => '蘭', + '鸞' => '鸞', + '嵐' => '嵐', + '濫' => '濫', + '藍' => '藍', + '襤' => '襤', + '拉' => '拉', + '臘' => '臘', + '蠟' => '蠟', + '廊' => '廊', + '朗' => '朗', + '浪' => '浪', + '狼' => '狼', + '郎' => '郎', + '來' => '來', + '冷' => '冷', + '勞' => '勞', + '擄' => '擄', + '櫓' => '櫓', + '爐' => '爐', + '盧' => '盧', + '老' => '老', + '蘆' => '蘆', + '虜' => '虜', + '路' => '路', + '露' => '露', + '魯' => '魯', + '鷺' => '鷺', + '碌' => '碌', + '祿' => '祿', + '綠' => '綠', + '菉' => '菉', + '錄' => '錄', + '鹿' => '鹿', + '論' => '論', + '壟' => '壟', + '弄' => '弄', + '籠' => '籠', + '聾' => '聾', + '牢' => '牢', + '磊' => '磊', + '賂' => '賂', + '雷' => '雷', + '壘' => '壘', + '屢' => '屢', + '樓' => '樓', + '淚' => '淚', + '漏' => '漏', + '累' => '累', + '縷' => '縷', + '陋' => '陋', + '勒' => '勒', + '肋' => '肋', + '凜' => '凜', + '凌' => '凌', + '稜' => '稜', + '綾' => '綾', + '菱' => '菱', + '陵' => '陵', + '讀' => '讀', + '拏' => '拏', + '樂' => '樂', + '諾' => '諾', + '丹' => '丹', + '寧' => '寧', + '怒' => '怒', + '率' => '率', + '異' => '異', + '北' => '北', + '磻' => '磻', + '便' => '便', + '復' => '復', + '不' => '不', + '泌' => '泌', + '數' => '數', + '索' => '索', + '參' => '參', + '塞' => '塞', + '省' => '省', + '葉' => '葉', + '說' => '說', + '殺' => '殺', + '辰' => '辰', + '沈' => '沈', + '拾' => '拾', + '若' => '若', + '掠' => '掠', + '略' => '略', + '亮' => '亮', + '兩' => '兩', + '凉' => '凉', + '梁' => '梁', + '糧' => '糧', + '良' => '良', + '諒' => '諒', + '量' => '量', + '勵' => '勵', + '呂' => '呂', + '女' => '女', + '廬' => '廬', + '旅' => '旅', + '濾' => '濾', + '礪' => '礪', + '閭' => '閭', + '驪' => '驪', + '麗' => '麗', + '黎' => '黎', + '力' => '力', + '曆' => '曆', + '歷' => '歷', + '轢' => '轢', + '年' => '年', + '憐' => '憐', + '戀' => '戀', + '撚' => '撚', + '漣' => '漣', + '煉' => '煉', + '璉' => '璉', + '秊' => '秊', + '練' => '練', + '聯' => '聯', + '輦' => '輦', + '蓮' => '蓮', + '連' => '連', + '鍊' => '鍊', + '列' => '列', + '劣' => '劣', + '咽' => '咽', + '烈' => '烈', + '裂' => '裂', + '說' => '說', + '廉' => '廉', + '念' => '念', + '捻' => '捻', + '殮' => '殮', + '簾' => '簾', + '獵' => '獵', + '令' => '令', + '囹' => '囹', + '寧' => '寧', + '嶺' => '嶺', + '怜' => '怜', + '玲' => '玲', + '瑩' => '瑩', + '羚' => '羚', + '聆' => '聆', + '鈴' => '鈴', + '零' => '零', + '靈' => '靈', + '領' => '領', + '例' => '例', + '禮' => '禮', + '醴' => '醴', + '隸' => '隸', + '惡' => '惡', + '了' => '了', + '僚' => '僚', + '寮' => '寮', + '尿' => '尿', + '料' => '料', + '樂' => '樂', + '燎' => '燎', + '療' => '療', + '蓼' => '蓼', + '遼' => '遼', + '龍' => '龍', + '暈' => '暈', + '阮' => '阮', + '劉' => '劉', + '杻' => '杻', + '柳' => '柳', + '流' => '流', + '溜' => '溜', + '琉' => '琉', + '留' => '留', + '硫' => '硫', + '紐' => '紐', + '類' => '類', + '六' => '六', + '戮' => '戮', + '陸' => '陸', + '倫' => '倫', + '崙' => '崙', + '淪' => '淪', + '輪' => '輪', + '律' => '律', + '慄' => '慄', + '栗' => '栗', + '率' => '率', + '隆' => '隆', + '利' => '利', + '吏' => '吏', + '履' => '履', + '易' => '易', + '李' => '李', + '梨' => '梨', + '泥' => '泥', + '理' => '理', + '痢' => '痢', + '罹' => '罹', + '裏' => '裏', + '裡' => '裡', + '里' => '里', + '離' => '離', + '匿' => '匿', + '溺' => '溺', + '吝' => '吝', + '燐' => '燐', + '璘' => '璘', + '藺' => '藺', + '隣' => '隣', + '鱗' => '鱗', + '麟' => '麟', + '林' => '林', + '淋' => '淋', + '臨' => '臨', + '立' => '立', + '笠' => '笠', + '粒' => '粒', + '狀' => '狀', + '炙' => '炙', + '識' => '識', + '什' => '什', + '茶' => '茶', + '刺' => '刺', + '切' => '切', + '度' => '度', + '拓' => '拓', + '糖' => '糖', + '宅' => '宅', + '洞' => '洞', + '暴' => '暴', + '輻' => '輻', + '行' => '行', + '降' => '降', + '見' => '見', + '廓' => '廓', + '兀' => '兀', + '嗀' => '嗀', + '塚' => '塚', + '晴' => '晴', + '凞' => '凞', + '猪' => '猪', + '益' => '益', + '礼' => '礼', + '神' => '神', + '祥' => '祥', + '福' => '福', + '靖' => '靖', + '精' => '精', + '羽' => '羽', + '蘒' => '蘒', + '諸' => '諸', + '逸' => '逸', + '都' => '都', + '飯' => '飯', + '飼' => '飼', + '館' => '館', + '鶴' => '鶴', + '郞' => '郞', + '隷' => '隷', + '侮' => '侮', + '僧' => '僧', + '免' => '免', + '勉' => '勉', + '勤' => '勤', + '卑' => '卑', + '喝' => '喝', + '嘆' => '嘆', + '器' => '器', + '塀' => '塀', + '墨' => '墨', + '層' => '層', + '屮' => '屮', + '悔' => '悔', + '慨' => '慨', + '憎' => '憎', + '懲' => '懲', + '敏' => '敏', + '既' => '既', + '暑' => '暑', + '梅' => '梅', + '海' => '海', + '渚' => '渚', + '漢' => '漢', + '煮' => '煮', + '爫' => '爫', + '琢' => '琢', + '碑' => '碑', + '社' => '社', + '祉' => '祉', + '祈' => '祈', + '祐' => '祐', + '祖' => '祖', + '祝' => '祝', + '禍' => '禍', + '禎' => '禎', + '穀' => '穀', + '突' => '突', + '節' => '節', + '練' => '練', + '縉' => '縉', + '繁' => '繁', + '署' => '署', + '者' => '者', + '臭' => '臭', + '艹' => '艹', + '艹' => '艹', + '著' => '著', + '褐' => '褐', + '視' => '視', + '謁' => '謁', + '謹' => '謹', + '賓' => '賓', + '贈' => '贈', + '辶' => '辶', + '逸' => '逸', + '難' => '難', + '響' => '響', + '頻' => '頻', + '恵' => '恵', + '𤋮' => '𤋮', + '舘' => '舘', + '並' => '並', + '况' => '况', + '全' => '全', + '侀' => '侀', + '充' => '充', + '冀' => '冀', + '勇' => '勇', + '勺' => '勺', + '喝' => '喝', + '啕' => '啕', + '喙' => '喙', + '嗢' => '嗢', + '塚' => '塚', + '墳' => '墳', + '奄' => '奄', + '奔' => '奔', + '婢' => '婢', + '嬨' => '嬨', + '廒' => '廒', + '廙' => '廙', + '彩' => '彩', + '徭' => '徭', + '惘' => '惘', + '慎' => '慎', + '愈' => '愈', + '憎' => '憎', + '慠' => '慠', + '懲' => '懲', + '戴' => '戴', + '揄' => '揄', + '搜' => '搜', + '摒' => '摒', + '敖' => '敖', + '晴' => '晴', + '朗' => '朗', + '望' => '望', + '杖' => '杖', + '歹' => '歹', + '殺' => '殺', + '流' => '流', + '滛' => '滛', + '滋' => '滋', + '漢' => '漢', + '瀞' => '瀞', + '煮' => '煮', + '瞧' => '瞧', + '爵' => '爵', + '犯' => '犯', + '猪' => '猪', + '瑱' => '瑱', + '甆' => '甆', + '画' => '画', + '瘝' => '瘝', + '瘟' => '瘟', + '益' => '益', + '盛' => '盛', + '直' => '直', + '睊' => '睊', + '着' => '着', + '磌' => '磌', + '窱' => '窱', + '節' => '節', + '类' => '类', + '絛' => '絛', + '練' => '練', + '缾' => '缾', + '者' => '者', + '荒' => '荒', + '華' => '華', + '蝹' => '蝹', + '襁' => '襁', + '覆' => '覆', + '視' => '視', + '調' => '調', + '諸' => '諸', + '請' => '請', + '謁' => '謁', + '諾' => '諾', + '諭' => '諭', + '謹' => '謹', + '變' => '變', + '贈' => '贈', + '輸' => '輸', + '遲' => '遲', + '醙' => '醙', + '鉶' => '鉶', + '陼' => '陼', + '難' => '難', + '靖' => '靖', + '韛' => '韛', + '響' => '響', + '頋' => '頋', + '頻' => '頻', + '鬒' => '鬒', + '龜' => '龜', + '𢡊' => '𢡊', + '𢡄' => '𢡄', + '𣏕' => '𣏕', + '㮝' => '㮝', + '䀘' => '䀘', + '䀹' => '䀹', + '𥉉' => '𥉉', + '𥳐' => '𥳐', + '𧻓' => '𧻓', + '齃' => '齃', + '龎' => '龎', + 'יִ' => 'יִ', + 'ײַ' => 'ײַ', + 'שׁ' => 'שׁ', + 'שׂ' => 'שׂ', + 'שּׁ' => 'שּׁ', + 'שּׂ' => 'שּׂ', + 'אַ' => 'אַ', + 'אָ' => 'אָ', + 'אּ' => 'אּ', + 'בּ' => 'בּ', + 'גּ' => 'גּ', + 'דּ' => 'דּ', + 'הּ' => 'הּ', + 'וּ' => 'וּ', + 'זּ' => 'זּ', + 'טּ' => 'טּ', + 'יּ' => 'יּ', + 'ךּ' => 'ךּ', + 'כּ' => 'כּ', + 'לּ' => 'לּ', + 'מּ' => 'מּ', + 'נּ' => 'נּ', + 'סּ' => 'סּ', + 'ףּ' => 'ףּ', + 'פּ' => 'פּ', + 'צּ' => 'צּ', + 'קּ' => 'קּ', + 'רּ' => 'רּ', + 'שּ' => 'שּ', + 'תּ' => 'תּ', + 'וֹ' => 'וֹ', + 'בֿ' => 'בֿ', + 'כֿ' => 'כֿ', + 'פֿ' => 'פֿ', + '𑂚' => '𑂚', + '𑂜' => '𑂜', + '𑂫' => '𑂫', + '𑄮' => '𑄮', + '𑄯' => '𑄯', + '𑍋' => '𑍋', + '𑍌' => '𑍌', + '𑒻' => '𑒻', + '𑒼' => '𑒼', + '𑒾' => '𑒾', + '𑖺' => '𑖺', + '𑖻' => '𑖻', + '𑤸' => '𑤸', + '𝅗𝅥' => '𝅗𝅥', + '𝅘𝅥' => '𝅘𝅥', + '𝅘𝅥𝅮' => '𝅘𝅥𝅮', + '𝅘𝅥𝅯' => '𝅘𝅥𝅯', + '𝅘𝅥𝅰' => '𝅘𝅥𝅰', + '𝅘𝅥𝅱' => '𝅘𝅥𝅱', + '𝅘𝅥𝅲' => '𝅘𝅥𝅲', + '𝆹𝅥' => '𝆹𝅥', + '𝆺𝅥' => '𝆺𝅥', + '𝆹𝅥𝅮' => '𝆹𝅥𝅮', + '𝆺𝅥𝅮' => '𝆺𝅥𝅮', + '𝆹𝅥𝅯' => '𝆹𝅥𝅯', + '𝆺𝅥𝅯' => '𝆺𝅥𝅯', + '丽' => '丽', + '丸' => '丸', + '乁' => '乁', + '𠄢' => '𠄢', + '你' => '你', + '侮' => '侮', + '侻' => '侻', + '倂' => '倂', + '偺' => '偺', + '備' => '備', + '僧' => '僧', + '像' => '像', + '㒞' => '㒞', + '𠘺' => '𠘺', + '免' => '免', + '兔' => '兔', + '兤' => '兤', + '具' => '具', + '𠔜' => '𠔜', + '㒹' => '㒹', + '內' => '內', + '再' => '再', + '𠕋' => '𠕋', + '冗' => '冗', + '冤' => '冤', + '仌' => '仌', + '冬' => '冬', + '况' => '况', + '𩇟' => '𩇟', + '凵' => '凵', + '刃' => '刃', + '㓟' => '㓟', + '刻' => '刻', + '剆' => '剆', + '割' => '割', + '剷' => '剷', + '㔕' => '㔕', + '勇' => '勇', + '勉' => '勉', + '勤' => '勤', + '勺' => '勺', + '包' => '包', + '匆' => '匆', + '北' => '北', + '卉' => '卉', + '卑' => '卑', + '博' => '博', + '即' => '即', + '卽' => '卽', + '卿' => '卿', + '卿' => '卿', + '卿' => '卿', + '𠨬' => '𠨬', + '灰' => '灰', + '及' => '及', + '叟' => '叟', + '𠭣' => '𠭣', + '叫' => '叫', + '叱' => '叱', + '吆' => '吆', + '咞' => '咞', + '吸' => '吸', + '呈' => '呈', + '周' => '周', + '咢' => '咢', + '哶' => '哶', + '唐' => '唐', + '啓' => '啓', + '啣' => '啣', + '善' => '善', + '善' => '善', + '喙' => '喙', + '喫' => '喫', + '喳' => '喳', + '嗂' => '嗂', + '圖' => '圖', + '嘆' => '嘆', + '圗' => '圗', + '噑' => '噑', + '噴' => '噴', + '切' => '切', + '壮' => '壮', + '城' => '城', + '埴' => '埴', + '堍' => '堍', + '型' => '型', + '堲' => '堲', + '報' => '報', + '墬' => '墬', + '𡓤' => '𡓤', + '売' => '売', + '壷' => '壷', + '夆' => '夆', + '多' => '多', + '夢' => '夢', + '奢' => '奢', + '𡚨' => '𡚨', + '𡛪' => '𡛪', + '姬' => '姬', + '娛' => '娛', + '娧' => '娧', + '姘' => '姘', + '婦' => '婦', + '㛮' => '㛮', + '㛼' => '㛼', + '嬈' => '嬈', + '嬾' => '嬾', + '嬾' => '嬾', + '𡧈' => '𡧈', + '寃' => '寃', + '寘' => '寘', + '寧' => '寧', + '寳' => '寳', + '𡬘' => '𡬘', + '寿' => '寿', + '将' => '将', + '当' => '当', + '尢' => '尢', + '㞁' => '㞁', + '屠' => '屠', + '屮' => '屮', + '峀' => '峀', + '岍' => '岍', + '𡷤' => '𡷤', + '嵃' => '嵃', + '𡷦' => '𡷦', + '嵮' => '嵮', + '嵫' => '嵫', + '嵼' => '嵼', + '巡' => '巡', + '巢' => '巢', + '㠯' => '㠯', + '巽' => '巽', + '帨' => '帨', + '帽' => '帽', + '幩' => '幩', + '㡢' => '㡢', + '𢆃' => '𢆃', + '㡼' => '㡼', + '庰' => '庰', + '庳' => '庳', + '庶' => '庶', + '廊' => '廊', + '𪎒' => '𪎒', + '廾' => '廾', + '𢌱' => '𢌱', + '𢌱' => '𢌱', + '舁' => '舁', + '弢' => '弢', + '弢' => '弢', + '㣇' => '㣇', + '𣊸' => '𣊸', + '𦇚' => '𦇚', + '形' => '形', + '彫' => '彫', + '㣣' => '㣣', + '徚' => '徚', + '忍' => '忍', + '志' => '志', + '忹' => '忹', + '悁' => '悁', + '㤺' => '㤺', + '㤜' => '㤜', + '悔' => '悔', + '𢛔' => '𢛔', + '惇' => '惇', + '慈' => '慈', + '慌' => '慌', + '慎' => '慎', + '慌' => '慌', + '慺' => '慺', + '憎' => '憎', + '憲' => '憲', + '憤' => '憤', + '憯' => '憯', + '懞' => '懞', + '懲' => '懲', + '懶' => '懶', + '成' => '成', + '戛' => '戛', + '扝' => '扝', + '抱' => '抱', + '拔' => '拔', + '捐' => '捐', + '𢬌' => '𢬌', + '挽' => '挽', + '拼' => '拼', + '捨' => '捨', + '掃' => '掃', + '揤' => '揤', + '𢯱' => '𢯱', + '搢' => '搢', + '揅' => '揅', + '掩' => '掩', + '㨮' => '㨮', + '摩' => '摩', + '摾' => '摾', + '撝' => '撝', + '摷' => '摷', + '㩬' => '㩬', + '敏' => '敏', + '敬' => '敬', + '𣀊' => '𣀊', + '旣' => '旣', + '書' => '書', + '晉' => '晉', + '㬙' => '㬙', + '暑' => '暑', + '㬈' => '㬈', + '㫤' => '㫤', + '冒' => '冒', + '冕' => '冕', + '最' => '最', + '暜' => '暜', + '肭' => '肭', + '䏙' => '䏙', + '朗' => '朗', + '望' => '望', + '朡' => '朡', + '杞' => '杞', + '杓' => '杓', + '𣏃' => '𣏃', + '㭉' => '㭉', + '柺' => '柺', + '枅' => '枅', + '桒' => '桒', + '梅' => '梅', + '𣑭' => '𣑭', + '梎' => '梎', + '栟' => '栟', + '椔' => '椔', + '㮝' => '㮝', + '楂' => '楂', + '榣' => '榣', + '槪' => '槪', + '檨' => '檨', + '𣚣' => '𣚣', + '櫛' => '櫛', + '㰘' => '㰘', + '次' => '次', + '𣢧' => '𣢧', + '歔' => '歔', + '㱎' => '㱎', + '歲' => '歲', + '殟' => '殟', + '殺' => '殺', + '殻' => '殻', + '𣪍' => '𣪍', + '𡴋' => '𡴋', + '𣫺' => '𣫺', + '汎' => '汎', + '𣲼' => '𣲼', + '沿' => '沿', + '泍' => '泍', + '汧' => '汧', + '洖' => '洖', + '派' => '派', + '海' => '海', + '流' => '流', + '浩' => '浩', + '浸' => '浸', + '涅' => '涅', + '𣴞' => '𣴞', + '洴' => '洴', + '港' => '港', + '湮' => '湮', + '㴳' => '㴳', + '滋' => '滋', + '滇' => '滇', + '𣻑' => '𣻑', + '淹' => '淹', + '潮' => '潮', + '𣽞' => '𣽞', + '𣾎' => '𣾎', + '濆' => '濆', + '瀹' => '瀹', + '瀞' => '瀞', + '瀛' => '瀛', + '㶖' => '㶖', + '灊' => '灊', + '災' => '災', + '灷' => '灷', + '炭' => '炭', + '𠔥' => '𠔥', + '煅' => '煅', + '𤉣' => '𤉣', + '熜' => '熜', + '𤎫' => '𤎫', + '爨' => '爨', + '爵' => '爵', + '牐' => '牐', + '𤘈' => '𤘈', + '犀' => '犀', + '犕' => '犕', + '𤜵' => '𤜵', + '𤠔' => '𤠔', + '獺' => '獺', + '王' => '王', + '㺬' => '㺬', + '玥' => '玥', + '㺸' => '㺸', + '㺸' => '㺸', + '瑇' => '瑇', + '瑜' => '瑜', + '瑱' => '瑱', + '璅' => '璅', + '瓊' => '瓊', + '㼛' => '㼛', + '甤' => '甤', + '𤰶' => '𤰶', + '甾' => '甾', + '𤲒' => '𤲒', + '異' => '異', + '𢆟' => '𢆟', + '瘐' => '瘐', + '𤾡' => '𤾡', + '𤾸' => '𤾸', + '𥁄' => '𥁄', + '㿼' => '㿼', + '䀈' => '䀈', + '直' => '直', + '𥃳' => '𥃳', + '𥃲' => '𥃲', + '𥄙' => '𥄙', + '𥄳' => '𥄳', + '眞' => '眞', + '真' => '真', + '真' => '真', + '睊' => '睊', + '䀹' => '䀹', + '瞋' => '瞋', + '䁆' => '䁆', + '䂖' => '䂖', + '𥐝' => '𥐝', + '硎' => '硎', + '碌' => '碌', + '磌' => '磌', + '䃣' => '䃣', + '𥘦' => '𥘦', + '祖' => '祖', + '𥚚' => '𥚚', + '𥛅' => '𥛅', + '福' => '福', + '秫' => '秫', + '䄯' => '䄯', + '穀' => '穀', + '穊' => '穊', + '穏' => '穏', + '𥥼' => '𥥼', + '𥪧' => '𥪧', + '𥪧' => '𥪧', + '竮' => '竮', + '䈂' => '䈂', + '𥮫' => '𥮫', + '篆' => '篆', + '築' => '築', + '䈧' => '䈧', + '𥲀' => '𥲀', + '糒' => '糒', + '䊠' => '䊠', + '糨' => '糨', + '糣' => '糣', + '紀' => '紀', + '𥾆' => '𥾆', + '絣' => '絣', + '䌁' => '䌁', + '緇' => '緇', + '縂' => '縂', + '繅' => '繅', + '䌴' => '䌴', + '𦈨' => '𦈨', + '𦉇' => '𦉇', + '䍙' => '䍙', + '𦋙' => '𦋙', + '罺' => '罺', + '𦌾' => '𦌾', + '羕' => '羕', + '翺' => '翺', + '者' => '者', + '𦓚' => '𦓚', + '𦔣' => '𦔣', + '聠' => '聠', + '𦖨' => '𦖨', + '聰' => '聰', + '𣍟' => '𣍟', + '䏕' => '䏕', + '育' => '育', + '脃' => '脃', + '䐋' => '䐋', + '脾' => '脾', + '媵' => '媵', + '𦞧' => '𦞧', + '𦞵' => '𦞵', + '𣎓' => '𣎓', + '𣎜' => '𣎜', + '舁' => '舁', + '舄' => '舄', + '辞' => '辞', + '䑫' => '䑫', + '芑' => '芑', + '芋' => '芋', + '芝' => '芝', + '劳' => '劳', + '花' => '花', + '芳' => '芳', + '芽' => '芽', + '苦' => '苦', + '𦬼' => '𦬼', + '若' => '若', + '茝' => '茝', + '荣' => '荣', + '莭' => '莭', + '茣' => '茣', + '莽' => '莽', + '菧' => '菧', + '著' => '著', + '荓' => '荓', + '菊' => '菊', + '菌' => '菌', + '菜' => '菜', + '𦰶' => '𦰶', + '𦵫' => '𦵫', + '𦳕' => '𦳕', + '䔫' => '䔫', + '蓱' => '蓱', + '蓳' => '蓳', + '蔖' => '蔖', + '𧏊' => '𧏊', + '蕤' => '蕤', + '𦼬' => '𦼬', + '䕝' => '䕝', + '䕡' => '䕡', + '𦾱' => '𦾱', + '𧃒' => '𧃒', + '䕫' => '䕫', + '虐' => '虐', + '虜' => '虜', + '虧' => '虧', + '虩' => '虩', + '蚩' => '蚩', + '蚈' => '蚈', + '蜎' => '蜎', + '蛢' => '蛢', + '蝹' => '蝹', + '蜨' => '蜨', + '蝫' => '蝫', + '螆' => '螆', + '䗗' => '䗗', + '蟡' => '蟡', + '蠁' => '蠁', + '䗹' => '䗹', + '衠' => '衠', + '衣' => '衣', + '𧙧' => '𧙧', + '裗' => '裗', + '裞' => '裞', + '䘵' => '䘵', + '裺' => '裺', + '㒻' => '㒻', + '𧢮' => '𧢮', + '𧥦' => '𧥦', + '䚾' => '䚾', + '䛇' => '䛇', + '誠' => '誠', + '諭' => '諭', + '變' => '變', + '豕' => '豕', + '𧲨' => '𧲨', + '貫' => '貫', + '賁' => '賁', + '贛' => '贛', + '起' => '起', + '𧼯' => '𧼯', + '𠠄' => '𠠄', + '跋' => '跋', + '趼' => '趼', + '跰' => '跰', + '𠣞' => '𠣞', + '軔' => '軔', + '輸' => '輸', + '𨗒' => '𨗒', + '𨗭' => '𨗭', + '邔' => '邔', + '郱' => '郱', + '鄑' => '鄑', + '𨜮' => '𨜮', + '鄛' => '鄛', + '鈸' => '鈸', + '鋗' => '鋗', + '鋘' => '鋘', + '鉼' => '鉼', + '鏹' => '鏹', + '鐕' => '鐕', + '𨯺' => '𨯺', + '開' => '開', + '䦕' => '䦕', + '閷' => '閷', + '𨵷' => '𨵷', + '䧦' => '䧦', + '雃' => '雃', + '嶲' => '嶲', + '霣' => '霣', + '𩅅' => '𩅅', + '𩈚' => '𩈚', + '䩮' => '䩮', + '䩶' => '䩶', + '韠' => '韠', + '𩐊' => '𩐊', + '䪲' => '䪲', + '𩒖' => '𩒖', + '頋' => '頋', + '頋' => '頋', + '頩' => '頩', + '𩖶' => '𩖶', + '飢' => '飢', + '䬳' => '䬳', + '餩' => '餩', + '馧' => '馧', + '駂' => '駂', + '駾' => '駾', + '䯎' => '䯎', + '𩬰' => '𩬰', + '鬒' => '鬒', + '鱀' => '鱀', + '鳽' => '鳽', + '䳎' => '䳎', + '䳭' => '䳭', + '鵧' => '鵧', + '𪃎' => '𪃎', + '䳸' => '䳸', + '𪄅' => '𪄅', + '𪈎' => '𪈎', + '𪊑' => '𪊑', + '麻' => '麻', + '䵖' => '䵖', + '黹' => '黹', + '黾' => '黾', + '鼅' => '鼅', + '鼏' => '鼏', + '鼖' => '鼖', + '鼻' => '鼻', + '𪘀' => '𪘀', +); diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php new file mode 100644 index 0000000000..ec90f36eb6 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php @@ -0,0 +1,876 @@ + 230, + '́' => 230, + '̂' => 230, + '̃' => 230, + '̄' => 230, + '̅' => 230, + '̆' => 230, + '̇' => 230, + '̈' => 230, + '̉' => 230, + '̊' => 230, + '̋' => 230, + '̌' => 230, + '̍' => 230, + '̎' => 230, + '̏' => 230, + '̐' => 230, + '̑' => 230, + '̒' => 230, + '̓' => 230, + '̔' => 230, + '̕' => 232, + '̖' => 220, + '̗' => 220, + '̘' => 220, + '̙' => 220, + '̚' => 232, + '̛' => 216, + '̜' => 220, + '̝' => 220, + '̞' => 220, + '̟' => 220, + '̠' => 220, + '̡' => 202, + '̢' => 202, + '̣' => 220, + '̤' => 220, + '̥' => 220, + '̦' => 220, + '̧' => 202, + '̨' => 202, + '̩' => 220, + '̪' => 220, + '̫' => 220, + '̬' => 220, + '̭' => 220, + '̮' => 220, + '̯' => 220, + '̰' => 220, + '̱' => 220, + '̲' => 220, + '̳' => 220, + '̴' => 1, + '̵' => 1, + '̶' => 1, + '̷' => 1, + '̸' => 1, + '̹' => 220, + '̺' => 220, + '̻' => 220, + '̼' => 220, + '̽' => 230, + '̾' => 230, + '̿' => 230, + '̀' => 230, + '́' => 230, + '͂' => 230, + '̓' => 230, + '̈́' => 230, + 'ͅ' => 240, + '͆' => 230, + '͇' => 220, + '͈' => 220, + '͉' => 220, + '͊' => 230, + '͋' => 230, + '͌' => 230, + '͍' => 220, + '͎' => 220, + '͐' => 230, + '͑' => 230, + '͒' => 230, + '͓' => 220, + '͔' => 220, + '͕' => 220, + '͖' => 220, + '͗' => 230, + '͘' => 232, + '͙' => 220, + '͚' => 220, + '͛' => 230, + '͜' => 233, + '͝' => 234, + '͞' => 234, + '͟' => 233, + '͠' => 234, + '͡' => 234, + '͢' => 233, + 'ͣ' => 230, + 'ͤ' => 230, + 'ͥ' => 230, + 'ͦ' => 230, + 'ͧ' => 230, + 'ͨ' => 230, + 'ͩ' => 230, + 'ͪ' => 230, + 'ͫ' => 230, + 'ͬ' => 230, + 'ͭ' => 230, + 'ͮ' => 230, + 'ͯ' => 230, + '҃' => 230, + '҄' => 230, + '҅' => 230, + '҆' => 230, + '҇' => 230, + '֑' => 220, + '֒' => 230, + '֓' => 230, + '֔' => 230, + '֕' => 230, + '֖' => 220, + '֗' => 230, + '֘' => 230, + '֙' => 230, + '֚' => 222, + '֛' => 220, + '֜' => 230, + '֝' => 230, + '֞' => 230, + '֟' => 230, + '֠' => 230, + '֡' => 230, + '֢' => 220, + '֣' => 220, + '֤' => 220, + '֥' => 220, + '֦' => 220, + '֧' => 220, + '֨' => 230, + '֩' => 230, + '֪' => 220, + '֫' => 230, + '֬' => 230, + '֭' => 222, + '֮' => 228, + '֯' => 230, + 'ְ' => 10, + 'ֱ' => 11, + 'ֲ' => 12, + 'ֳ' => 13, + 'ִ' => 14, + 'ֵ' => 15, + 'ֶ' => 16, + 'ַ' => 17, + 'ָ' => 18, + 'ֹ' => 19, + 'ֺ' => 19, + 'ֻ' => 20, + 'ּ' => 21, + 'ֽ' => 22, + 'ֿ' => 23, + 'ׁ' => 24, + 'ׂ' => 25, + 'ׄ' => 230, + 'ׅ' => 220, + 'ׇ' => 18, + 'ؐ' => 230, + 'ؑ' => 230, + 'ؒ' => 230, + 'ؓ' => 230, + 'ؔ' => 230, + 'ؕ' => 230, + 'ؖ' => 230, + 'ؗ' => 230, + 'ؘ' => 30, + 'ؙ' => 31, + 'ؚ' => 32, + 'ً' => 27, + 'ٌ' => 28, + 'ٍ' => 29, + 'َ' => 30, + 'ُ' => 31, + 'ِ' => 32, + 'ّ' => 33, + 'ْ' => 34, + 'ٓ' => 230, + 'ٔ' => 230, + 'ٕ' => 220, + 'ٖ' => 220, + 'ٗ' => 230, + '٘' => 230, + 'ٙ' => 230, + 'ٚ' => 230, + 'ٛ' => 230, + 'ٜ' => 220, + 'ٝ' => 230, + 'ٞ' => 230, + 'ٟ' => 220, + 'ٰ' => 35, + 'ۖ' => 230, + 'ۗ' => 230, + 'ۘ' => 230, + 'ۙ' => 230, + 'ۚ' => 230, + 'ۛ' => 230, + 'ۜ' => 230, + '۟' => 230, + '۠' => 230, + 'ۡ' => 230, + 'ۢ' => 230, + 'ۣ' => 220, + 'ۤ' => 230, + 'ۧ' => 230, + 'ۨ' => 230, + '۪' => 220, + '۫' => 230, + '۬' => 230, + 'ۭ' => 220, + 'ܑ' => 36, + 'ܰ' => 230, + 'ܱ' => 220, + 'ܲ' => 230, + 'ܳ' => 230, + 'ܴ' => 220, + 'ܵ' => 230, + 'ܶ' => 230, + 'ܷ' => 220, + 'ܸ' => 220, + 'ܹ' => 220, + 'ܺ' => 230, + 'ܻ' => 220, + 'ܼ' => 220, + 'ܽ' => 230, + 'ܾ' => 220, + 'ܿ' => 230, + '݀' => 230, + '݁' => 230, + '݂' => 220, + '݃' => 230, + '݄' => 220, + '݅' => 230, + '݆' => 220, + '݇' => 230, + '݈' => 220, + '݉' => 230, + '݊' => 230, + '߫' => 230, + '߬' => 230, + '߭' => 230, + '߮' => 230, + '߯' => 230, + '߰' => 230, + '߱' => 230, + '߲' => 220, + '߳' => 230, + '߽' => 220, + 'ࠖ' => 230, + 'ࠗ' => 230, + '࠘' => 230, + '࠙' => 230, + 'ࠛ' => 230, + 'ࠜ' => 230, + 'ࠝ' => 230, + 'ࠞ' => 230, + 'ࠟ' => 230, + 'ࠠ' => 230, + 'ࠡ' => 230, + 'ࠢ' => 230, + 'ࠣ' => 230, + 'ࠥ' => 230, + 'ࠦ' => 230, + 'ࠧ' => 230, + 'ࠩ' => 230, + 'ࠪ' => 230, + 'ࠫ' => 230, + 'ࠬ' => 230, + '࠭' => 230, + '࡙' => 220, + '࡚' => 220, + '࡛' => 220, + '࣓' => 220, + 'ࣔ' => 230, + 'ࣕ' => 230, + 'ࣖ' => 230, + 'ࣗ' => 230, + 'ࣘ' => 230, + 'ࣙ' => 230, + 'ࣚ' => 230, + 'ࣛ' => 230, + 'ࣜ' => 230, + 'ࣝ' => 230, + 'ࣞ' => 230, + 'ࣟ' => 230, + '࣠' => 230, + '࣡' => 230, + 'ࣣ' => 220, + 'ࣤ' => 230, + 'ࣥ' => 230, + 'ࣦ' => 220, + 'ࣧ' => 230, + 'ࣨ' => 230, + 'ࣩ' => 220, + '࣪' => 230, + '࣫' => 230, + '࣬' => 230, + '࣭' => 220, + '࣮' => 220, + '࣯' => 220, + 'ࣰ' => 27, + 'ࣱ' => 28, + 'ࣲ' => 29, + 'ࣳ' => 230, + 'ࣴ' => 230, + 'ࣵ' => 230, + 'ࣶ' => 220, + 'ࣷ' => 230, + 'ࣸ' => 230, + 'ࣹ' => 220, + 'ࣺ' => 220, + 'ࣻ' => 230, + 'ࣼ' => 230, + 'ࣽ' => 230, + 'ࣾ' => 230, + 'ࣿ' => 230, + '़' => 7, + '्' => 9, + '॑' => 230, + '॒' => 220, + '॓' => 230, + '॔' => 230, + '়' => 7, + '্' => 9, + '৾' => 230, + '਼' => 7, + '੍' => 9, + '઼' => 7, + '્' => 9, + '଼' => 7, + '୍' => 9, + '்' => 9, + '్' => 9, + 'ౕ' => 84, + 'ౖ' => 91, + '಼' => 7, + '್' => 9, + '഻' => 9, + '഼' => 9, + '്' => 9, + '්' => 9, + 'ุ' => 103, + 'ู' => 103, + 'ฺ' => 9, + '่' => 107, + '้' => 107, + '๊' => 107, + '๋' => 107, + 'ຸ' => 118, + 'ູ' => 118, + '຺' => 9, + '່' => 122, + '້' => 122, + '໊' => 122, + '໋' => 122, + '༘' => 220, + '༙' => 220, + '༵' => 220, + '༷' => 220, + '༹' => 216, + 'ཱ' => 129, + 'ི' => 130, + 'ུ' => 132, + 'ེ' => 130, + 'ཻ' => 130, + 'ོ' => 130, + 'ཽ' => 130, + 'ྀ' => 130, + 'ྂ' => 230, + 'ྃ' => 230, + '྄' => 9, + '྆' => 230, + '྇' => 230, + '࿆' => 220, + '့' => 7, + '္' => 9, + '်' => 9, + 'ႍ' => 220, + '፝' => 230, + '፞' => 230, + '፟' => 230, + '᜔' => 9, + '᜴' => 9, + '្' => 9, + '៝' => 230, + 'ᢩ' => 228, + '᤹' => 222, + '᤺' => 230, + '᤻' => 220, + 'ᨗ' => 230, + 'ᨘ' => 220, + '᩠' => 9, + '᩵' => 230, + '᩶' => 230, + '᩷' => 230, + '᩸' => 230, + '᩹' => 230, + '᩺' => 230, + '᩻' => 230, + '᩼' => 230, + '᩿' => 220, + '᪰' => 230, + '᪱' => 230, + '᪲' => 230, + '᪳' => 230, + '᪴' => 230, + '᪵' => 220, + '᪶' => 220, + '᪷' => 220, + '᪸' => 220, + '᪹' => 220, + '᪺' => 220, + '᪻' => 230, + '᪼' => 230, + '᪽' => 220, + 'ᪿ' => 220, + 'ᫀ' => 220, + '᬴' => 7, + '᭄' => 9, + '᭫' => 230, + '᭬' => 220, + '᭭' => 230, + '᭮' => 230, + '᭯' => 230, + '᭰' => 230, + '᭱' => 230, + '᭲' => 230, + '᭳' => 230, + '᮪' => 9, + '᮫' => 9, + '᯦' => 7, + '᯲' => 9, + '᯳' => 9, + '᰷' => 7, + '᳐' => 230, + '᳑' => 230, + '᳒' => 230, + '᳔' => 1, + '᳕' => 220, + '᳖' => 220, + '᳗' => 220, + '᳘' => 220, + '᳙' => 220, + '᳚' => 230, + '᳛' => 230, + '᳜' => 220, + '᳝' => 220, + '᳞' => 220, + '᳟' => 220, + '᳠' => 230, + '᳢' => 1, + '᳣' => 1, + '᳤' => 1, + '᳥' => 1, + '᳦' => 1, + '᳧' => 1, + '᳨' => 1, + '᳭' => 220, + '᳴' => 230, + '᳸' => 230, + '᳹' => 230, + '᷀' => 230, + '᷁' => 230, + '᷂' => 220, + '᷃' => 230, + '᷄' => 230, + '᷅' => 230, + '᷆' => 230, + '᷇' => 230, + '᷈' => 230, + '᷉' => 230, + '᷊' => 220, + '᷋' => 230, + '᷌' => 230, + '᷍' => 234, + '᷎' => 214, + '᷏' => 220, + '᷐' => 202, + '᷑' => 230, + '᷒' => 230, + 'ᷓ' => 230, + 'ᷔ' => 230, + 'ᷕ' => 230, + 'ᷖ' => 230, + 'ᷗ' => 230, + 'ᷘ' => 230, + 'ᷙ' => 230, + 'ᷚ' => 230, + 'ᷛ' => 230, + 'ᷜ' => 230, + 'ᷝ' => 230, + 'ᷞ' => 230, + 'ᷟ' => 230, + 'ᷠ' => 230, + 'ᷡ' => 230, + 'ᷢ' => 230, + 'ᷣ' => 230, + 'ᷤ' => 230, + 'ᷥ' => 230, + 'ᷦ' => 230, + 'ᷧ' => 230, + 'ᷨ' => 230, + 'ᷩ' => 230, + 'ᷪ' => 230, + 'ᷫ' => 230, + 'ᷬ' => 230, + 'ᷭ' => 230, + 'ᷮ' => 230, + 'ᷯ' => 230, + 'ᷰ' => 230, + 'ᷱ' => 230, + 'ᷲ' => 230, + 'ᷳ' => 230, + 'ᷴ' => 230, + '᷵' => 230, + '᷶' => 232, + '᷷' => 228, + '᷸' => 228, + '᷹' => 220, + '᷻' => 230, + '᷼' => 233, + '᷽' => 220, + '᷾' => 230, + '᷿' => 220, + '⃐' => 230, + '⃑' => 230, + '⃒' => 1, + '⃓' => 1, + '⃔' => 230, + '⃕' => 230, + '⃖' => 230, + '⃗' => 230, + '⃘' => 1, + '⃙' => 1, + '⃚' => 1, + '⃛' => 230, + '⃜' => 230, + '⃡' => 230, + '⃥' => 1, + '⃦' => 1, + '⃧' => 230, + '⃨' => 220, + '⃩' => 230, + '⃪' => 1, + '⃫' => 1, + '⃬' => 220, + '⃭' => 220, + '⃮' => 220, + '⃯' => 220, + '⃰' => 230, + '⳯' => 230, + '⳰' => 230, + '⳱' => 230, + '⵿' => 9, + 'ⷠ' => 230, + 'ⷡ' => 230, + 'ⷢ' => 230, + 'ⷣ' => 230, + 'ⷤ' => 230, + 'ⷥ' => 230, + 'ⷦ' => 230, + 'ⷧ' => 230, + 'ⷨ' => 230, + 'ⷩ' => 230, + 'ⷪ' => 230, + 'ⷫ' => 230, + 'ⷬ' => 230, + 'ⷭ' => 230, + 'ⷮ' => 230, + 'ⷯ' => 230, + 'ⷰ' => 230, + 'ⷱ' => 230, + 'ⷲ' => 230, + 'ⷳ' => 230, + 'ⷴ' => 230, + 'ⷵ' => 230, + 'ⷶ' => 230, + 'ⷷ' => 230, + 'ⷸ' => 230, + 'ⷹ' => 230, + 'ⷺ' => 230, + 'ⷻ' => 230, + 'ⷼ' => 230, + 'ⷽ' => 230, + 'ⷾ' => 230, + 'ⷿ' => 230, + '〪' => 218, + '〫' => 228, + '〬' => 232, + '〭' => 222, + '〮' => 224, + '〯' => 224, + '゙' => 8, + '゚' => 8, + '꙯' => 230, + 'ꙴ' => 230, + 'ꙵ' => 230, + 'ꙶ' => 230, + 'ꙷ' => 230, + 'ꙸ' => 230, + 'ꙹ' => 230, + 'ꙺ' => 230, + 'ꙻ' => 230, + '꙼' => 230, + '꙽' => 230, + 'ꚞ' => 230, + 'ꚟ' => 230, + '꛰' => 230, + '꛱' => 230, + '꠆' => 9, + '꠬' => 9, + '꣄' => 9, + '꣠' => 230, + '꣡' => 230, + '꣢' => 230, + '꣣' => 230, + '꣤' => 230, + '꣥' => 230, + '꣦' => 230, + '꣧' => 230, + '꣨' => 230, + '꣩' => 230, + '꣪' => 230, + '꣫' => 230, + '꣬' => 230, + '꣭' => 230, + '꣮' => 230, + '꣯' => 230, + '꣰' => 230, + '꣱' => 230, + '꤫' => 220, + '꤬' => 220, + '꤭' => 220, + '꥓' => 9, + '꦳' => 7, + '꧀' => 9, + 'ꪰ' => 230, + 'ꪲ' => 230, + 'ꪳ' => 230, + 'ꪴ' => 220, + 'ꪷ' => 230, + 'ꪸ' => 230, + 'ꪾ' => 230, + '꪿' => 230, + '꫁' => 230, + '꫶' => 9, + '꯭' => 9, + 'ﬞ' => 26, + '︠' => 230, + '︡' => 230, + '︢' => 230, + '︣' => 230, + '︤' => 230, + '︥' => 230, + '︦' => 230, + '︧' => 220, + '︨' => 220, + '︩' => 220, + '︪' => 220, + '︫' => 220, + '︬' => 220, + '︭' => 220, + '︮' => 230, + '︯' => 230, + '𐇽' => 220, + '𐋠' => 220, + '𐍶' => 230, + '𐍷' => 230, + '𐍸' => 230, + '𐍹' => 230, + '𐍺' => 230, + '𐨍' => 220, + '𐨏' => 230, + '𐨸' => 230, + '𐨹' => 1, + '𐨺' => 220, + '𐨿' => 9, + '𐫥' => 230, + '𐫦' => 220, + '𐴤' => 230, + '𐴥' => 230, + '𐴦' => 230, + '𐴧' => 230, + '𐺫' => 230, + '𐺬' => 230, + '𐽆' => 220, + '𐽇' => 220, + '𐽈' => 230, + '𐽉' => 230, + '𐽊' => 230, + '𐽋' => 220, + '𐽌' => 230, + '𐽍' => 220, + '𐽎' => 220, + '𐽏' => 220, + '𐽐' => 220, + '𑁆' => 9, + '𑁿' => 9, + '𑂹' => 9, + '𑂺' => 7, + '𑄀' => 230, + '𑄁' => 230, + '𑄂' => 230, + '𑄳' => 9, + '𑄴' => 9, + '𑅳' => 7, + '𑇀' => 9, + '𑇊' => 7, + '𑈵' => 9, + '𑈶' => 7, + '𑋩' => 7, + '𑋪' => 9, + '𑌻' => 7, + '𑌼' => 7, + '𑍍' => 9, + '𑍦' => 230, + '𑍧' => 230, + '𑍨' => 230, + '𑍩' => 230, + '𑍪' => 230, + '𑍫' => 230, + '𑍬' => 230, + '𑍰' => 230, + '𑍱' => 230, + '𑍲' => 230, + '𑍳' => 230, + '𑍴' => 230, + '𑑂' => 9, + '𑑆' => 7, + '𑑞' => 230, + '𑓂' => 9, + '𑓃' => 7, + '𑖿' => 9, + '𑗀' => 7, + '𑘿' => 9, + '𑚶' => 9, + '𑚷' => 7, + '𑜫' => 9, + '𑠹' => 9, + '𑠺' => 7, + '𑤽' => 9, + '𑤾' => 9, + '𑥃' => 7, + '𑧠' => 9, + '𑨴' => 9, + '𑩇' => 9, + '𑪙' => 9, + '𑰿' => 9, + '𑵂' => 7, + '𑵄' => 9, + '𑵅' => 9, + '𑶗' => 9, + '𖫰' => 1, + '𖫱' => 1, + '𖫲' => 1, + '𖫳' => 1, + '𖫴' => 1, + '𖬰' => 230, + '𖬱' => 230, + '𖬲' => 230, + '𖬳' => 230, + '𖬴' => 230, + '𖬵' => 230, + '𖬶' => 230, + '𖿰' => 6, + '𖿱' => 6, + '𛲞' => 1, + '𝅥' => 216, + '𝅦' => 216, + '𝅧' => 1, + '𝅨' => 1, + '𝅩' => 1, + '𝅭' => 226, + '𝅮' => 216, + '𝅯' => 216, + '𝅰' => 216, + '𝅱' => 216, + '𝅲' => 216, + '𝅻' => 220, + '𝅼' => 220, + '𝅽' => 220, + '𝅾' => 220, + '𝅿' => 220, + '𝆀' => 220, + '𝆁' => 220, + '𝆂' => 220, + '𝆅' => 230, + '𝆆' => 230, + '𝆇' => 230, + '𝆈' => 230, + '𝆉' => 230, + '𝆊' => 220, + '𝆋' => 220, + '𝆪' => 230, + '𝆫' => 230, + '𝆬' => 230, + '𝆭' => 230, + '𝉂' => 230, + '𝉃' => 230, + '𝉄' => 230, + '𞀀' => 230, + '𞀁' => 230, + '𞀂' => 230, + '𞀃' => 230, + '𞀄' => 230, + '𞀅' => 230, + '𞀆' => 230, + '𞀈' => 230, + '𞀉' => 230, + '𞀊' => 230, + '𞀋' => 230, + '𞀌' => 230, + '𞀍' => 230, + '𞀎' => 230, + '𞀏' => 230, + '𞀐' => 230, + '𞀑' => 230, + '𞀒' => 230, + '𞀓' => 230, + '𞀔' => 230, + '𞀕' => 230, + '𞀖' => 230, + '𞀗' => 230, + '𞀘' => 230, + '𞀛' => 230, + '𞀜' => 230, + '𞀝' => 230, + '𞀞' => 230, + '𞀟' => 230, + '𞀠' => 230, + '𞀡' => 230, + '𞀣' => 230, + '𞀤' => 230, + '𞀦' => 230, + '𞀧' => 230, + '𞀨' => 230, + '𞀩' => 230, + '𞀪' => 230, + '𞄰' => 230, + '𞄱' => 230, + '𞄲' => 230, + '𞄳' => 230, + '𞄴' => 230, + '𞄵' => 230, + '𞄶' => 230, + '𞋬' => 230, + '𞋭' => 230, + '𞋮' => 230, + '𞋯' => 230, + '𞣐' => 220, + '𞣑' => 220, + '𞣒' => 220, + '𞣓' => 220, + '𞣔' => 220, + '𞣕' => 220, + '𞣖' => 220, + '𞥄' => 230, + '𞥅' => 230, + '𞥆' => 230, + '𞥇' => 230, + '𞥈' => 230, + '𞥉' => 230, + '𞥊' => 7, +); diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php new file mode 100644 index 0000000000..1574902893 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php @@ -0,0 +1,3695 @@ + ' ', + '¨' => ' ̈', + 'ª' => 'a', + '¯' => ' ̄', + '²' => '2', + '³' => '3', + '´' => ' ́', + 'µ' => 'μ', + '¸' => ' ̧', + '¹' => '1', + 'º' => 'o', + '¼' => '1⁄4', + '½' => '1⁄2', + '¾' => '3⁄4', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'Ŀ' => 'L·', + 'ŀ' => 'l·', + 'ʼn' => 'ʼn', + 'ſ' => 's', + 'DŽ' => 'DŽ', + 'Dž' => 'Dž', + 'dž' => 'dž', + 'LJ' => 'LJ', + 'Lj' => 'Lj', + 'lj' => 'lj', + 'NJ' => 'NJ', + 'Nj' => 'Nj', + 'nj' => 'nj', + 'DZ' => 'DZ', + 'Dz' => 'Dz', + 'dz' => 'dz', + 'ʰ' => 'h', + 'ʱ' => 'ɦ', + 'ʲ' => 'j', + 'ʳ' => 'r', + 'ʴ' => 'ɹ', + 'ʵ' => 'ɻ', + 'ʶ' => 'ʁ', + 'ʷ' => 'w', + 'ʸ' => 'y', + '˘' => ' ̆', + '˙' => ' ̇', + '˚' => ' ̊', + '˛' => ' ̨', + '˜' => ' ̃', + '˝' => ' ̋', + 'ˠ' => 'ɣ', + 'ˡ' => 'l', + 'ˢ' => 's', + 'ˣ' => 'x', + 'ˤ' => 'ʕ', + 'ͺ' => ' ͅ', + '΄' => ' ́', + '΅' => ' ̈́', + 'ϐ' => 'β', + 'ϑ' => 'θ', + 'ϒ' => 'Υ', + 'ϓ' => 'Ύ', + 'ϔ' => 'Ϋ', + 'ϕ' => 'φ', + 'ϖ' => 'π', + 'ϰ' => 'κ', + 'ϱ' => 'ρ', + 'ϲ' => 'ς', + 'ϴ' => 'Θ', + 'ϵ' => 'ε', + 'Ϲ' => 'Σ', + 'և' => 'եւ', + 'ٵ' => 'اٴ', + 'ٶ' => 'وٴ', + 'ٷ' => 'ۇٴ', + 'ٸ' => 'يٴ', + 'ำ' => 'ํา', + 'ຳ' => 'ໍາ', + 'ໜ' => 'ຫນ', + 'ໝ' => 'ຫມ', + '༌' => '་', + 'ཷ' => 'ྲཱྀ', + 'ཹ' => 'ླཱྀ', + 'ჼ' => 'ნ', + 'ᴬ' => 'A', + 'ᴭ' => 'Æ', + 'ᴮ' => 'B', + 'ᴰ' => 'D', + 'ᴱ' => 'E', + 'ᴲ' => 'Ǝ', + 'ᴳ' => 'G', + 'ᴴ' => 'H', + 'ᴵ' => 'I', + 'ᴶ' => 'J', + 'ᴷ' => 'K', + 'ᴸ' => 'L', + 'ᴹ' => 'M', + 'ᴺ' => 'N', + 'ᴼ' => 'O', + 'ᴽ' => 'Ȣ', + 'ᴾ' => 'P', + 'ᴿ' => 'R', + 'ᵀ' => 'T', + 'ᵁ' => 'U', + 'ᵂ' => 'W', + 'ᵃ' => 'a', + 'ᵄ' => 'ɐ', + 'ᵅ' => 'ɑ', + 'ᵆ' => 'ᴂ', + 'ᵇ' => 'b', + 'ᵈ' => 'd', + 'ᵉ' => 'e', + 'ᵊ' => 'ə', + 'ᵋ' => 'ɛ', + 'ᵌ' => 'ɜ', + 'ᵍ' => 'g', + 'ᵏ' => 'k', + 'ᵐ' => 'm', + 'ᵑ' => 'ŋ', + 'ᵒ' => 'o', + 'ᵓ' => 'ɔ', + 'ᵔ' => 'ᴖ', + 'ᵕ' => 'ᴗ', + 'ᵖ' => 'p', + 'ᵗ' => 't', + 'ᵘ' => 'u', + 'ᵙ' => 'ᴝ', + 'ᵚ' => 'ɯ', + 'ᵛ' => 'v', + 'ᵜ' => 'ᴥ', + 'ᵝ' => 'β', + 'ᵞ' => 'γ', + 'ᵟ' => 'δ', + 'ᵠ' => 'φ', + 'ᵡ' => 'χ', + 'ᵢ' => 'i', + 'ᵣ' => 'r', + 'ᵤ' => 'u', + 'ᵥ' => 'v', + 'ᵦ' => 'β', + 'ᵧ' => 'γ', + 'ᵨ' => 'ρ', + 'ᵩ' => 'φ', + 'ᵪ' => 'χ', + 'ᵸ' => 'н', + 'ᶛ' => 'ɒ', + 'ᶜ' => 'c', + 'ᶝ' => 'ɕ', + 'ᶞ' => 'ð', + 'ᶟ' => 'ɜ', + 'ᶠ' => 'f', + 'ᶡ' => 'ɟ', + 'ᶢ' => 'ɡ', + 'ᶣ' => 'ɥ', + 'ᶤ' => 'ɨ', + 'ᶥ' => 'ɩ', + 'ᶦ' => 'ɪ', + 'ᶧ' => 'ᵻ', + 'ᶨ' => 'ʝ', + 'ᶩ' => 'ɭ', + 'ᶪ' => 'ᶅ', + 'ᶫ' => 'ʟ', + 'ᶬ' => 'ɱ', + 'ᶭ' => 'ɰ', + 'ᶮ' => 'ɲ', + 'ᶯ' => 'ɳ', + 'ᶰ' => 'ɴ', + 'ᶱ' => 'ɵ', + 'ᶲ' => 'ɸ', + 'ᶳ' => 'ʂ', + 'ᶴ' => 'ʃ', + 'ᶵ' => 'ƫ', + 'ᶶ' => 'ʉ', + 'ᶷ' => 'ʊ', + 'ᶸ' => 'ᴜ', + 'ᶹ' => 'ʋ', + 'ᶺ' => 'ʌ', + 'ᶻ' => 'z', + 'ᶼ' => 'ʐ', + 'ᶽ' => 'ʑ', + 'ᶾ' => 'ʒ', + 'ᶿ' => 'θ', + 'ẚ' => 'aʾ', + 'ẛ' => 'ṡ', + '᾽' => ' ̓', + '᾿' => ' ̓', + '῀' => ' ͂', + '῁' => ' ̈͂', + '῍' => ' ̓̀', + '῎' => ' ̓́', + '῏' => ' ̓͂', + '῝' => ' ̔̀', + '῞' => ' ̔́', + '῟' => ' ̔͂', + '῭' => ' ̈̀', + '΅' => ' ̈́', + '´' => ' ́', + '῾' => ' ̔', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + '‑' => '‐', + '‗' => ' ̳', + '․' => '.', + '‥' => '..', + '…' => '...', + ' ' => ' ', + '″' => '′′', + '‴' => '′′′', + '‶' => '‵‵', + '‷' => '‵‵‵', + '‼' => '!!', + '‾' => ' ̅', + '⁇' => '??', + '⁈' => '?!', + '⁉' => '!?', + '⁗' => '′′′′', + ' ' => ' ', + '⁰' => '0', + 'ⁱ' => 'i', + '⁴' => '4', + '⁵' => '5', + '⁶' => '6', + '⁷' => '7', + '⁸' => '8', + '⁹' => '9', + '⁺' => '+', + '⁻' => '−', + '⁼' => '=', + '⁽' => '(', + '⁾' => ')', + 'ⁿ' => 'n', + '₀' => '0', + '₁' => '1', + '₂' => '2', + '₃' => '3', + '₄' => '4', + '₅' => '5', + '₆' => '6', + '₇' => '7', + '₈' => '8', + '₉' => '9', + '₊' => '+', + '₋' => '−', + '₌' => '=', + '₍' => '(', + '₎' => ')', + 'ₐ' => 'a', + 'ₑ' => 'e', + 'ₒ' => 'o', + 'ₓ' => 'x', + 'ₔ' => 'ə', + 'ₕ' => 'h', + 'ₖ' => 'k', + 'ₗ' => 'l', + 'ₘ' => 'm', + 'ₙ' => 'n', + 'ₚ' => 'p', + 'ₛ' => 's', + 'ₜ' => 't', + '₨' => 'Rs', + '℀' => 'a/c', + '℁' => 'a/s', + 'ℂ' => 'C', + '℃' => '°C', + '℅' => 'c/o', + '℆' => 'c/u', + 'ℇ' => 'Ɛ', + '℉' => '°F', + 'ℊ' => 'g', + 'ℋ' => 'H', + 'ℌ' => 'H', + 'ℍ' => 'H', + 'ℎ' => 'h', + 'ℏ' => 'ħ', + 'ℐ' => 'I', + 'ℑ' => 'I', + 'ℒ' => 'L', + 'ℓ' => 'l', + 'ℕ' => 'N', + '№' => 'No', + 'ℙ' => 'P', + 'ℚ' => 'Q', + 'ℛ' => 'R', + 'ℜ' => 'R', + 'ℝ' => 'R', + '℠' => 'SM', + '℡' => 'TEL', + '™' => 'TM', + 'ℤ' => 'Z', + 'ℨ' => 'Z', + 'ℬ' => 'B', + 'ℭ' => 'C', + 'ℯ' => 'e', + 'ℰ' => 'E', + 'ℱ' => 'F', + 'ℳ' => 'M', + 'ℴ' => 'o', + 'ℵ' => 'א', + 'ℶ' => 'ב', + 'ℷ' => 'ג', + 'ℸ' => 'ד', + 'ℹ' => 'i', + '℻' => 'FAX', + 'ℼ' => 'π', + 'ℽ' => 'γ', + 'ℾ' => 'Γ', + 'ℿ' => 'Π', + '⅀' => '∑', + 'ⅅ' => 'D', + 'ⅆ' => 'd', + 'ⅇ' => 'e', + 'ⅈ' => 'i', + 'ⅉ' => 'j', + '⅐' => '1⁄7', + '⅑' => '1⁄9', + '⅒' => '1⁄10', + '⅓' => '1⁄3', + '⅔' => '2⁄3', + '⅕' => '1⁄5', + '⅖' => '2⁄5', + '⅗' => '3⁄5', + '⅘' => '4⁄5', + '⅙' => '1⁄6', + '⅚' => '5⁄6', + '⅛' => '1⁄8', + '⅜' => '3⁄8', + '⅝' => '5⁄8', + '⅞' => '7⁄8', + '⅟' => '1⁄', + 'Ⅰ' => 'I', + 'Ⅱ' => 'II', + 'Ⅲ' => 'III', + 'Ⅳ' => 'IV', + 'Ⅴ' => 'V', + 'Ⅵ' => 'VI', + 'Ⅶ' => 'VII', + 'Ⅷ' => 'VIII', + 'Ⅸ' => 'IX', + 'Ⅹ' => 'X', + 'Ⅺ' => 'XI', + 'Ⅻ' => 'XII', + 'Ⅼ' => 'L', + 'Ⅽ' => 'C', + 'Ⅾ' => 'D', + 'Ⅿ' => 'M', + 'ⅰ' => 'i', + 'ⅱ' => 'ii', + 'ⅲ' => 'iii', + 'ⅳ' => 'iv', + 'ⅴ' => 'v', + 'ⅵ' => 'vi', + 'ⅶ' => 'vii', + 'ⅷ' => 'viii', + 'ⅸ' => 'ix', + 'ⅹ' => 'x', + 'ⅺ' => 'xi', + 'ⅻ' => 'xii', + 'ⅼ' => 'l', + 'ⅽ' => 'c', + 'ⅾ' => 'd', + 'ⅿ' => 'm', + '↉' => '0⁄3', + '∬' => '∫∫', + '∭' => '∫∫∫', + '∯' => '∮∮', + '∰' => '∮∮∮', + '①' => '1', + '②' => '2', + '③' => '3', + '④' => '4', + '⑤' => '5', + '⑥' => '6', + '⑦' => '7', + '⑧' => '8', + '⑨' => '9', + '⑩' => '10', + '⑪' => '11', + '⑫' => '12', + '⑬' => '13', + '⑭' => '14', + '⑮' => '15', + '⑯' => '16', + '⑰' => '17', + '⑱' => '18', + '⑲' => '19', + '⑳' => '20', + '⑴' => '(1)', + '⑵' => '(2)', + '⑶' => '(3)', + '⑷' => '(4)', + '⑸' => '(5)', + '⑹' => '(6)', + '⑺' => '(7)', + '⑻' => '(8)', + '⑼' => '(9)', + '⑽' => '(10)', + '⑾' => '(11)', + '⑿' => '(12)', + '⒀' => '(13)', + '⒁' => '(14)', + '⒂' => '(15)', + '⒃' => '(16)', + '⒄' => '(17)', + '⒅' => '(18)', + '⒆' => '(19)', + '⒇' => '(20)', + '⒈' => '1.', + '⒉' => '2.', + '⒊' => '3.', + '⒋' => '4.', + '⒌' => '5.', + '⒍' => '6.', + '⒎' => '7.', + '⒏' => '8.', + '⒐' => '9.', + '⒑' => '10.', + '⒒' => '11.', + '⒓' => '12.', + '⒔' => '13.', + '⒕' => '14.', + '⒖' => '15.', + '⒗' => '16.', + '⒘' => '17.', + '⒙' => '18.', + '⒚' => '19.', + '⒛' => '20.', + '⒜' => '(a)', + '⒝' => '(b)', + '⒞' => '(c)', + '⒟' => '(d)', + '⒠' => '(e)', + '⒡' => '(f)', + '⒢' => '(g)', + '⒣' => '(h)', + '⒤' => '(i)', + '⒥' => '(j)', + '⒦' => '(k)', + '⒧' => '(l)', + '⒨' => '(m)', + '⒩' => '(n)', + '⒪' => '(o)', + '⒫' => '(p)', + '⒬' => '(q)', + '⒭' => '(r)', + '⒮' => '(s)', + '⒯' => '(t)', + '⒰' => '(u)', + '⒱' => '(v)', + '⒲' => '(w)', + '⒳' => '(x)', + '⒴' => '(y)', + '⒵' => '(z)', + 'Ⓐ' => 'A', + 'Ⓑ' => 'B', + 'Ⓒ' => 'C', + 'Ⓓ' => 'D', + 'Ⓔ' => 'E', + 'Ⓕ' => 'F', + 'Ⓖ' => 'G', + 'Ⓗ' => 'H', + 'Ⓘ' => 'I', + 'Ⓙ' => 'J', + 'Ⓚ' => 'K', + 'Ⓛ' => 'L', + 'Ⓜ' => 'M', + 'Ⓝ' => 'N', + 'Ⓞ' => 'O', + 'Ⓟ' => 'P', + 'Ⓠ' => 'Q', + 'Ⓡ' => 'R', + 'Ⓢ' => 'S', + 'Ⓣ' => 'T', + 'Ⓤ' => 'U', + 'Ⓥ' => 'V', + 'Ⓦ' => 'W', + 'Ⓧ' => 'X', + 'Ⓨ' => 'Y', + 'Ⓩ' => 'Z', + 'ⓐ' => 'a', + 'ⓑ' => 'b', + 'ⓒ' => 'c', + 'ⓓ' => 'd', + 'ⓔ' => 'e', + 'ⓕ' => 'f', + 'ⓖ' => 'g', + 'ⓗ' => 'h', + 'ⓘ' => 'i', + 'ⓙ' => 'j', + 'ⓚ' => 'k', + 'ⓛ' => 'l', + 'ⓜ' => 'm', + 'ⓝ' => 'n', + 'ⓞ' => 'o', + 'ⓟ' => 'p', + 'ⓠ' => 'q', + 'ⓡ' => 'r', + 'ⓢ' => 's', + 'ⓣ' => 't', + 'ⓤ' => 'u', + 'ⓥ' => 'v', + 'ⓦ' => 'w', + 'ⓧ' => 'x', + 'ⓨ' => 'y', + 'ⓩ' => 'z', + '⓪' => '0', + '⨌' => '∫∫∫∫', + '⩴' => '::=', + '⩵' => '==', + '⩶' => '===', + 'ⱼ' => 'j', + 'ⱽ' => 'V', + 'ⵯ' => 'ⵡ', + '⺟' => '母', + '⻳' => '龟', + '⼀' => '一', + '⼁' => '丨', + '⼂' => '丶', + '⼃' => '丿', + '⼄' => '乙', + '⼅' => '亅', + '⼆' => '二', + '⼇' => '亠', + '⼈' => '人', + '⼉' => '儿', + '⼊' => '入', + '⼋' => '八', + '⼌' => '冂', + '⼍' => '冖', + '⼎' => '冫', + '⼏' => '几', + '⼐' => '凵', + '⼑' => '刀', + '⼒' => '力', + '⼓' => '勹', + '⼔' => '匕', + '⼕' => '匚', + '⼖' => '匸', + '⼗' => '十', + '⼘' => '卜', + '⼙' => '卩', + '⼚' => '厂', + '⼛' => '厶', + '⼜' => '又', + '⼝' => '口', + '⼞' => '囗', + '⼟' => '土', + '⼠' => '士', + '⼡' => '夂', + '⼢' => '夊', + '⼣' => '夕', + '⼤' => '大', + '⼥' => '女', + '⼦' => '子', + '⼧' => '宀', + '⼨' => '寸', + '⼩' => '小', + '⼪' => '尢', + '⼫' => '尸', + '⼬' => '屮', + '⼭' => '山', + '⼮' => '巛', + '⼯' => '工', + '⼰' => '己', + '⼱' => '巾', + '⼲' => '干', + '⼳' => '幺', + '⼴' => '广', + '⼵' => '廴', + '⼶' => '廾', + '⼷' => '弋', + '⼸' => '弓', + '⼹' => '彐', + '⼺' => '彡', + '⼻' => '彳', + '⼼' => '心', + '⼽' => '戈', + '⼾' => '戶', + '⼿' => '手', + '⽀' => '支', + '⽁' => '攴', + '⽂' => '文', + '⽃' => '斗', + '⽄' => '斤', + '⽅' => '方', + '⽆' => '无', + '⽇' => '日', + '⽈' => '曰', + '⽉' => '月', + '⽊' => '木', + '⽋' => '欠', + '⽌' => '止', + '⽍' => '歹', + '⽎' => '殳', + '⽏' => '毋', + '⽐' => '比', + '⽑' => '毛', + '⽒' => '氏', + '⽓' => '气', + '⽔' => '水', + '⽕' => '火', + '⽖' => '爪', + '⽗' => '父', + '⽘' => '爻', + '⽙' => '爿', + '⽚' => '片', + '⽛' => '牙', + '⽜' => '牛', + '⽝' => '犬', + '⽞' => '玄', + '⽟' => '玉', + '⽠' => '瓜', + '⽡' => '瓦', + '⽢' => '甘', + '⽣' => '生', + '⽤' => '用', + '⽥' => '田', + '⽦' => '疋', + '⽧' => '疒', + '⽨' => '癶', + '⽩' => '白', + '⽪' => '皮', + '⽫' => '皿', + '⽬' => '目', + '⽭' => '矛', + '⽮' => '矢', + '⽯' => '石', + '⽰' => '示', + '⽱' => '禸', + '⽲' => '禾', + '⽳' => '穴', + '⽴' => '立', + '⽵' => '竹', + '⽶' => '米', + '⽷' => '糸', + '⽸' => '缶', + '⽹' => '网', + '⽺' => '羊', + '⽻' => '羽', + '⽼' => '老', + '⽽' => '而', + '⽾' => '耒', + '⽿' => '耳', + '⾀' => '聿', + '⾁' => '肉', + '⾂' => '臣', + '⾃' => '自', + '⾄' => '至', + '⾅' => '臼', + '⾆' => '舌', + '⾇' => '舛', + '⾈' => '舟', + '⾉' => '艮', + '⾊' => '色', + '⾋' => '艸', + '⾌' => '虍', + '⾍' => '虫', + '⾎' => '血', + '⾏' => '行', + '⾐' => '衣', + '⾑' => '襾', + '⾒' => '見', + '⾓' => '角', + '⾔' => '言', + '⾕' => '谷', + '⾖' => '豆', + '⾗' => '豕', + '⾘' => '豸', + '⾙' => '貝', + '⾚' => '赤', + '⾛' => '走', + '⾜' => '足', + '⾝' => '身', + '⾞' => '車', + '⾟' => '辛', + '⾠' => '辰', + '⾡' => '辵', + '⾢' => '邑', + '⾣' => '酉', + '⾤' => '釆', + '⾥' => '里', + '⾦' => '金', + '⾧' => '長', + '⾨' => '門', + '⾩' => '阜', + '⾪' => '隶', + '⾫' => '隹', + '⾬' => '雨', + '⾭' => '靑', + '⾮' => '非', + '⾯' => '面', + '⾰' => '革', + '⾱' => '韋', + '⾲' => '韭', + '⾳' => '音', + '⾴' => '頁', + '⾵' => '風', + '⾶' => '飛', + '⾷' => '食', + '⾸' => '首', + '⾹' => '香', + '⾺' => '馬', + '⾻' => '骨', + '⾼' => '高', + '⾽' => '髟', + '⾾' => '鬥', + '⾿' => '鬯', + '⿀' => '鬲', + '⿁' => '鬼', + '⿂' => '魚', + '⿃' => '鳥', + '⿄' => '鹵', + '⿅' => '鹿', + '⿆' => '麥', + '⿇' => '麻', + '⿈' => '黃', + '⿉' => '黍', + '⿊' => '黑', + '⿋' => '黹', + '⿌' => '黽', + '⿍' => '鼎', + '⿎' => '鼓', + '⿏' => '鼠', + '⿐' => '鼻', + '⿑' => '齊', + '⿒' => '齒', + '⿓' => '龍', + '⿔' => '龜', + '⿕' => '龠', + ' ' => ' ', + '〶' => '〒', + '〸' => '十', + '〹' => '卄', + '〺' => '卅', + '゛' => ' ゙', + '゜' => ' ゚', + 'ゟ' => 'より', + 'ヿ' => 'コト', + 'ㄱ' => 'ᄀ', + 'ㄲ' => 'ᄁ', + 'ㄳ' => 'ᆪ', + 'ㄴ' => 'ᄂ', + 'ㄵ' => 'ᆬ', + 'ㄶ' => 'ᆭ', + 'ㄷ' => 'ᄃ', + 'ㄸ' => 'ᄄ', + 'ㄹ' => 'ᄅ', + 'ㄺ' => 'ᆰ', + 'ㄻ' => 'ᆱ', + 'ㄼ' => 'ᆲ', + 'ㄽ' => 'ᆳ', + 'ㄾ' => 'ᆴ', + 'ㄿ' => 'ᆵ', + 'ㅀ' => 'ᄚ', + 'ㅁ' => 'ᄆ', + 'ㅂ' => 'ᄇ', + 'ㅃ' => 'ᄈ', + 'ㅄ' => 'ᄡ', + 'ㅅ' => 'ᄉ', + 'ㅆ' => 'ᄊ', + 'ㅇ' => 'ᄋ', + 'ㅈ' => 'ᄌ', + 'ㅉ' => 'ᄍ', + 'ㅊ' => 'ᄎ', + 'ㅋ' => 'ᄏ', + 'ㅌ' => 'ᄐ', + 'ㅍ' => 'ᄑ', + 'ㅎ' => 'ᄒ', + 'ㅏ' => 'ᅡ', + 'ㅐ' => 'ᅢ', + 'ㅑ' => 'ᅣ', + 'ㅒ' => 'ᅤ', + 'ㅓ' => 'ᅥ', + 'ㅔ' => 'ᅦ', + 'ㅕ' => 'ᅧ', + 'ㅖ' => 'ᅨ', + 'ㅗ' => 'ᅩ', + 'ㅘ' => 'ᅪ', + 'ㅙ' => 'ᅫ', + 'ㅚ' => 'ᅬ', + 'ㅛ' => 'ᅭ', + 'ㅜ' => 'ᅮ', + 'ㅝ' => 'ᅯ', + 'ㅞ' => 'ᅰ', + 'ㅟ' => 'ᅱ', + 'ㅠ' => 'ᅲ', + 'ㅡ' => 'ᅳ', + 'ㅢ' => 'ᅴ', + 'ㅣ' => 'ᅵ', + 'ㅤ' => 'ᅠ', + 'ㅥ' => 'ᄔ', + 'ㅦ' => 'ᄕ', + 'ㅧ' => 'ᇇ', + 'ㅨ' => 'ᇈ', + 'ㅩ' => 'ᇌ', + 'ㅪ' => 'ᇎ', + 'ㅫ' => 'ᇓ', + 'ㅬ' => 'ᇗ', + 'ㅭ' => 'ᇙ', + 'ㅮ' => 'ᄜ', + 'ㅯ' => 'ᇝ', + 'ㅰ' => 'ᇟ', + 'ㅱ' => 'ᄝ', + 'ㅲ' => 'ᄞ', + 'ㅳ' => 'ᄠ', + 'ㅴ' => 'ᄢ', + 'ㅵ' => 'ᄣ', + 'ㅶ' => 'ᄧ', + 'ㅷ' => 'ᄩ', + 'ㅸ' => 'ᄫ', + 'ㅹ' => 'ᄬ', + 'ㅺ' => 'ᄭ', + 'ㅻ' => 'ᄮ', + 'ㅼ' => 'ᄯ', + 'ㅽ' => 'ᄲ', + 'ㅾ' => 'ᄶ', + 'ㅿ' => 'ᅀ', + 'ㆀ' => 'ᅇ', + 'ㆁ' => 'ᅌ', + 'ㆂ' => 'ᇱ', + 'ㆃ' => 'ᇲ', + 'ㆄ' => 'ᅗ', + 'ㆅ' => 'ᅘ', + 'ㆆ' => 'ᅙ', + 'ㆇ' => 'ᆄ', + 'ㆈ' => 'ᆅ', + 'ㆉ' => 'ᆈ', + 'ㆊ' => 'ᆑ', + 'ㆋ' => 'ᆒ', + 'ㆌ' => 'ᆔ', + 'ㆍ' => 'ᆞ', + 'ㆎ' => 'ᆡ', + '㆒' => '一', + '㆓' => '二', + '㆔' => '三', + '㆕' => '四', + '㆖' => '上', + '㆗' => '中', + '㆘' => '下', + '㆙' => '甲', + '㆚' => '乙', + '㆛' => '丙', + '㆜' => '丁', + '㆝' => '天', + '㆞' => '地', + '㆟' => '人', + '㈀' => '(ᄀ)', + '㈁' => '(ᄂ)', + '㈂' => '(ᄃ)', + '㈃' => '(ᄅ)', + '㈄' => '(ᄆ)', + '㈅' => '(ᄇ)', + '㈆' => '(ᄉ)', + '㈇' => '(ᄋ)', + '㈈' => '(ᄌ)', + '㈉' => '(ᄎ)', + '㈊' => '(ᄏ)', + '㈋' => '(ᄐ)', + '㈌' => '(ᄑ)', + '㈍' => '(ᄒ)', + '㈎' => '(가)', + '㈏' => '(나)', + '㈐' => '(다)', + '㈑' => '(라)', + '㈒' => '(마)', + '㈓' => '(바)', + '㈔' => '(사)', + '㈕' => '(아)', + '㈖' => '(자)', + '㈗' => '(차)', + '㈘' => '(카)', + '㈙' => '(타)', + '㈚' => '(파)', + '㈛' => '(하)', + '㈜' => '(주)', + '㈝' => '(오전)', + '㈞' => '(오후)', + '㈠' => '(一)', + '㈡' => '(二)', + '㈢' => '(三)', + '㈣' => '(四)', + '㈤' => '(五)', + '㈥' => '(六)', + '㈦' => '(七)', + '㈧' => '(八)', + '㈨' => '(九)', + '㈩' => '(十)', + '㈪' => '(月)', + '㈫' => '(火)', + '㈬' => '(水)', + '㈭' => '(木)', + '㈮' => '(金)', + '㈯' => '(土)', + '㈰' => '(日)', + '㈱' => '(株)', + '㈲' => '(有)', + '㈳' => '(社)', + '㈴' => '(名)', + '㈵' => '(特)', + '㈶' => '(財)', + '㈷' => '(祝)', + '㈸' => '(労)', + '㈹' => '(代)', + '㈺' => '(呼)', + '㈻' => '(学)', + '㈼' => '(監)', + '㈽' => '(企)', + '㈾' => '(資)', + '㈿' => '(協)', + '㉀' => '(祭)', + '㉁' => '(休)', + '㉂' => '(自)', + '㉃' => '(至)', + '㉄' => '問', + '㉅' => '幼', + '㉆' => '文', + '㉇' => '箏', + '㉐' => 'PTE', + '㉑' => '21', + '㉒' => '22', + '㉓' => '23', + '㉔' => '24', + '㉕' => '25', + '㉖' => '26', + '㉗' => '27', + '㉘' => '28', + '㉙' => '29', + '㉚' => '30', + '㉛' => '31', + '㉜' => '32', + '㉝' => '33', + '㉞' => '34', + '㉟' => '35', + '㉠' => 'ᄀ', + '㉡' => 'ᄂ', + '㉢' => 'ᄃ', + '㉣' => 'ᄅ', + '㉤' => 'ᄆ', + '㉥' => 'ᄇ', + '㉦' => 'ᄉ', + '㉧' => 'ᄋ', + '㉨' => 'ᄌ', + '㉩' => 'ᄎ', + '㉪' => 'ᄏ', + '㉫' => 'ᄐ', + '㉬' => 'ᄑ', + '㉭' => 'ᄒ', + '㉮' => '가', + '㉯' => '나', + '㉰' => '다', + '㉱' => '라', + '㉲' => '마', + '㉳' => '바', + '㉴' => '사', + '㉵' => '아', + '㉶' => '자', + '㉷' => '차', + '㉸' => '카', + '㉹' => '타', + '㉺' => '파', + '㉻' => '하', + '㉼' => '참고', + '㉽' => '주의', + '㉾' => '우', + '㊀' => '一', + '㊁' => '二', + '㊂' => '三', + '㊃' => '四', + '㊄' => '五', + '㊅' => '六', + '㊆' => '七', + '㊇' => '八', + '㊈' => '九', + '㊉' => '十', + '㊊' => '月', + '㊋' => '火', + '㊌' => '水', + '㊍' => '木', + '㊎' => '金', + '㊏' => '土', + '㊐' => '日', + '㊑' => '株', + '㊒' => '有', + '㊓' => '社', + '㊔' => '名', + '㊕' => '特', + '㊖' => '財', + '㊗' => '祝', + '㊘' => '労', + '㊙' => '秘', + '㊚' => '男', + '㊛' => '女', + '㊜' => '適', + '㊝' => '優', + '㊞' => '印', + '㊟' => '注', + '㊠' => '項', + '㊡' => '休', + '㊢' => '写', + '㊣' => '正', + '㊤' => '上', + '㊥' => '中', + '㊦' => '下', + '㊧' => '左', + '㊨' => '右', + '㊩' => '医', + '㊪' => '宗', + '㊫' => '学', + '㊬' => '監', + '㊭' => '企', + '㊮' => '資', + '㊯' => '協', + '㊰' => '夜', + '㊱' => '36', + '㊲' => '37', + '㊳' => '38', + '㊴' => '39', + '㊵' => '40', + '㊶' => '41', + '㊷' => '42', + '㊸' => '43', + '㊹' => '44', + '㊺' => '45', + '㊻' => '46', + '㊼' => '47', + '㊽' => '48', + '㊾' => '49', + '㊿' => '50', + '㋀' => '1月', + '㋁' => '2月', + '㋂' => '3月', + '㋃' => '4月', + '㋄' => '5月', + '㋅' => '6月', + '㋆' => '7月', + '㋇' => '8月', + '㋈' => '9月', + '㋉' => '10月', + '㋊' => '11月', + '㋋' => '12月', + '㋌' => 'Hg', + '㋍' => 'erg', + '㋎' => 'eV', + '㋏' => 'LTD', + '㋐' => 'ア', + '㋑' => 'イ', + '㋒' => 'ウ', + '㋓' => 'エ', + '㋔' => 'オ', + '㋕' => 'カ', + '㋖' => 'キ', + '㋗' => 'ク', + '㋘' => 'ケ', + '㋙' => 'コ', + '㋚' => 'サ', + '㋛' => 'シ', + '㋜' => 'ス', + '㋝' => 'セ', + '㋞' => 'ソ', + '㋟' => 'タ', + '㋠' => 'チ', + '㋡' => 'ツ', + '㋢' => 'テ', + '㋣' => 'ト', + '㋤' => 'ナ', + '㋥' => 'ニ', + '㋦' => 'ヌ', + '㋧' => 'ネ', + '㋨' => 'ノ', + '㋩' => 'ハ', + '㋪' => 'ヒ', + '㋫' => 'フ', + '㋬' => 'ヘ', + '㋭' => 'ホ', + '㋮' => 'マ', + '㋯' => 'ミ', + '㋰' => 'ム', + '㋱' => 'メ', + '㋲' => 'モ', + '㋳' => 'ヤ', + '㋴' => 'ユ', + '㋵' => 'ヨ', + '㋶' => 'ラ', + '㋷' => 'リ', + '㋸' => 'ル', + '㋹' => 'レ', + '㋺' => 'ロ', + '㋻' => 'ワ', + '㋼' => 'ヰ', + '㋽' => 'ヱ', + '㋾' => 'ヲ', + '㋿' => '令和', + '㌀' => 'アパート', + '㌁' => 'アルファ', + '㌂' => 'アンペア', + '㌃' => 'アール', + '㌄' => 'イニング', + '㌅' => 'インチ', + '㌆' => 'ウォン', + '㌇' => 'エスクード', + '㌈' => 'エーカー', + '㌉' => 'オンス', + '㌊' => 'オーム', + '㌋' => 'カイリ', + '㌌' => 'カラット', + '㌍' => 'カロリー', + '㌎' => 'ガロン', + '㌏' => 'ガンマ', + '㌐' => 'ギガ', + '㌑' => 'ギニー', + '㌒' => 'キュリー', + '㌓' => 'ギルダー', + '㌔' => 'キロ', + '㌕' => 'キログラム', + '㌖' => 'キロメートル', + '㌗' => 'キロワット', + '㌘' => 'グラム', + '㌙' => 'グラムトン', + '㌚' => 'クルゼイロ', + '㌛' => 'クローネ', + '㌜' => 'ケース', + '㌝' => 'コルナ', + '㌞' => 'コーポ', + '㌟' => 'サイクル', + '㌠' => 'サンチーム', + '㌡' => 'シリング', + '㌢' => 'センチ', + '㌣' => 'セント', + '㌤' => 'ダース', + '㌥' => 'デシ', + '㌦' => 'ドル', + '㌧' => 'トン', + '㌨' => 'ナノ', + '㌩' => 'ノット', + '㌪' => 'ハイツ', + '㌫' => 'パーセント', + '㌬' => 'パーツ', + '㌭' => 'バーレル', + '㌮' => 'ピアストル', + '㌯' => 'ピクル', + '㌰' => 'ピコ', + '㌱' => 'ビル', + '㌲' => 'ファラッド', + '㌳' => 'フィート', + '㌴' => 'ブッシェル', + '㌵' => 'フラン', + '㌶' => 'ヘクタール', + '㌷' => 'ペソ', + '㌸' => 'ペニヒ', + '㌹' => 'ヘルツ', + '㌺' => 'ペンス', + '㌻' => 'ページ', + '㌼' => 'ベータ', + '㌽' => 'ポイント', + '㌾' => 'ボルト', + '㌿' => 'ホン', + '㍀' => 'ポンド', + '㍁' => 'ホール', + '㍂' => 'ホーン', + '㍃' => 'マイクロ', + '㍄' => 'マイル', + '㍅' => 'マッハ', + '㍆' => 'マルク', + '㍇' => 'マンション', + '㍈' => 'ミクロン', + '㍉' => 'ミリ', + '㍊' => 'ミリバール', + '㍋' => 'メガ', + '㍌' => 'メガトン', + '㍍' => 'メートル', + '㍎' => 'ヤード', + '㍏' => 'ヤール', + '㍐' => 'ユアン', + '㍑' => 'リットル', + '㍒' => 'リラ', + '㍓' => 'ルピー', + '㍔' => 'ルーブル', + '㍕' => 'レム', + '㍖' => 'レントゲン', + '㍗' => 'ワット', + '㍘' => '0点', + '㍙' => '1点', + '㍚' => '2点', + '㍛' => '3点', + '㍜' => '4点', + '㍝' => '5点', + '㍞' => '6点', + '㍟' => '7点', + '㍠' => '8点', + '㍡' => '9点', + '㍢' => '10点', + '㍣' => '11点', + '㍤' => '12点', + '㍥' => '13点', + '㍦' => '14点', + '㍧' => '15点', + '㍨' => '16点', + '㍩' => '17点', + '㍪' => '18点', + '㍫' => '19点', + '㍬' => '20点', + '㍭' => '21点', + '㍮' => '22点', + '㍯' => '23点', + '㍰' => '24点', + '㍱' => 'hPa', + '㍲' => 'da', + '㍳' => 'AU', + '㍴' => 'bar', + '㍵' => 'oV', + '㍶' => 'pc', + '㍷' => 'dm', + '㍸' => 'dm2', + '㍹' => 'dm3', + '㍺' => 'IU', + '㍻' => '平成', + '㍼' => '昭和', + '㍽' => '大正', + '㍾' => '明治', + '㍿' => '株式会社', + '㎀' => 'pA', + '㎁' => 'nA', + '㎂' => 'μA', + '㎃' => 'mA', + '㎄' => 'kA', + '㎅' => 'KB', + '㎆' => 'MB', + '㎇' => 'GB', + '㎈' => 'cal', + '㎉' => 'kcal', + '㎊' => 'pF', + '㎋' => 'nF', + '㎌' => 'μF', + '㎍' => 'μg', + '㎎' => 'mg', + '㎏' => 'kg', + '㎐' => 'Hz', + '㎑' => 'kHz', + '㎒' => 'MHz', + '㎓' => 'GHz', + '㎔' => 'THz', + '㎕' => 'μl', + '㎖' => 'ml', + '㎗' => 'dl', + '㎘' => 'kl', + '㎙' => 'fm', + '㎚' => 'nm', + '㎛' => 'μm', + '㎜' => 'mm', + '㎝' => 'cm', + '㎞' => 'km', + '㎟' => 'mm2', + '㎠' => 'cm2', + '㎡' => 'm2', + '㎢' => 'km2', + '㎣' => 'mm3', + '㎤' => 'cm3', + '㎥' => 'm3', + '㎦' => 'km3', + '㎧' => 'm∕s', + '㎨' => 'm∕s2', + '㎩' => 'Pa', + '㎪' => 'kPa', + '㎫' => 'MPa', + '㎬' => 'GPa', + '㎭' => 'rad', + '㎮' => 'rad∕s', + '㎯' => 'rad∕s2', + '㎰' => 'ps', + '㎱' => 'ns', + '㎲' => 'μs', + '㎳' => 'ms', + '㎴' => 'pV', + '㎵' => 'nV', + '㎶' => 'μV', + '㎷' => 'mV', + '㎸' => 'kV', + '㎹' => 'MV', + '㎺' => 'pW', + '㎻' => 'nW', + '㎼' => 'μW', + '㎽' => 'mW', + '㎾' => 'kW', + '㎿' => 'MW', + '㏀' => 'kΩ', + '㏁' => 'MΩ', + '㏂' => 'a.m.', + '㏃' => 'Bq', + '㏄' => 'cc', + '㏅' => 'cd', + '㏆' => 'C∕kg', + '㏇' => 'Co.', + '㏈' => 'dB', + '㏉' => 'Gy', + '㏊' => 'ha', + '㏋' => 'HP', + '㏌' => 'in', + '㏍' => 'KK', + '㏎' => 'KM', + '㏏' => 'kt', + '㏐' => 'lm', + '㏑' => 'ln', + '㏒' => 'log', + '㏓' => 'lx', + '㏔' => 'mb', + '㏕' => 'mil', + '㏖' => 'mol', + '㏗' => 'PH', + '㏘' => 'p.m.', + '㏙' => 'PPM', + '㏚' => 'PR', + '㏛' => 'sr', + '㏜' => 'Sv', + '㏝' => 'Wb', + '㏞' => 'V∕m', + '㏟' => 'A∕m', + '㏠' => '1日', + '㏡' => '2日', + '㏢' => '3日', + '㏣' => '4日', + '㏤' => '5日', + '㏥' => '6日', + '㏦' => '7日', + '㏧' => '8日', + '㏨' => '9日', + '㏩' => '10日', + '㏪' => '11日', + '㏫' => '12日', + '㏬' => '13日', + '㏭' => '14日', + '㏮' => '15日', + '㏯' => '16日', + '㏰' => '17日', + '㏱' => '18日', + '㏲' => '19日', + '㏳' => '20日', + '㏴' => '21日', + '㏵' => '22日', + '㏶' => '23日', + '㏷' => '24日', + '㏸' => '25日', + '㏹' => '26日', + '㏺' => '27日', + '㏻' => '28日', + '㏼' => '29日', + '㏽' => '30日', + '㏾' => '31日', + '㏿' => 'gal', + 'ꚜ' => 'ъ', + 'ꚝ' => 'ь', + 'ꝰ' => 'ꝯ', + 'ꟸ' => 'Ħ', + 'ꟹ' => 'œ', + 'ꭜ' => 'ꜧ', + 'ꭝ' => 'ꬷ', + 'ꭞ' => 'ɫ', + 'ꭟ' => 'ꭒ', + 'ꭩ' => 'ʍ', + 'ff' => 'ff', + 'fi' => 'fi', + 'fl' => 'fl', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + 'ſt' => 'st', + 'st' => 'st', + 'ﬓ' => 'մն', + 'ﬔ' => 'մե', + 'ﬕ' => 'մի', + 'ﬖ' => 'վն', + 'ﬗ' => 'մխ', + 'ﬠ' => 'ע', + 'ﬡ' => 'א', + 'ﬢ' => 'ד', + 'ﬣ' => 'ה', + 'ﬤ' => 'כ', + 'ﬥ' => 'ל', + 'ﬦ' => 'ם', + 'ﬧ' => 'ר', + 'ﬨ' => 'ת', + '﬩' => '+', + 'ﭏ' => 'אל', + 'ﭐ' => 'ٱ', + 'ﭑ' => 'ٱ', + 'ﭒ' => 'ٻ', + 'ﭓ' => 'ٻ', + 'ﭔ' => 'ٻ', + 'ﭕ' => 'ٻ', + 'ﭖ' => 'پ', + 'ﭗ' => 'پ', + 'ﭘ' => 'پ', + 'ﭙ' => 'پ', + 'ﭚ' => 'ڀ', + 'ﭛ' => 'ڀ', + 'ﭜ' => 'ڀ', + 'ﭝ' => 'ڀ', + 'ﭞ' => 'ٺ', + 'ﭟ' => 'ٺ', + 'ﭠ' => 'ٺ', + 'ﭡ' => 'ٺ', + 'ﭢ' => 'ٿ', + 'ﭣ' => 'ٿ', + 'ﭤ' => 'ٿ', + 'ﭥ' => 'ٿ', + 'ﭦ' => 'ٹ', + 'ﭧ' => 'ٹ', + 'ﭨ' => 'ٹ', + 'ﭩ' => 'ٹ', + 'ﭪ' => 'ڤ', + 'ﭫ' => 'ڤ', + 'ﭬ' => 'ڤ', + 'ﭭ' => 'ڤ', + 'ﭮ' => 'ڦ', + 'ﭯ' => 'ڦ', + 'ﭰ' => 'ڦ', + 'ﭱ' => 'ڦ', + 'ﭲ' => 'ڄ', + 'ﭳ' => 'ڄ', + 'ﭴ' => 'ڄ', + 'ﭵ' => 'ڄ', + 'ﭶ' => 'ڃ', + 'ﭷ' => 'ڃ', + 'ﭸ' => 'ڃ', + 'ﭹ' => 'ڃ', + 'ﭺ' => 'چ', + 'ﭻ' => 'چ', + 'ﭼ' => 'چ', + 'ﭽ' => 'چ', + 'ﭾ' => 'ڇ', + 'ﭿ' => 'ڇ', + 'ﮀ' => 'ڇ', + 'ﮁ' => 'ڇ', + 'ﮂ' => 'ڍ', + 'ﮃ' => 'ڍ', + 'ﮄ' => 'ڌ', + 'ﮅ' => 'ڌ', + 'ﮆ' => 'ڎ', + 'ﮇ' => 'ڎ', + 'ﮈ' => 'ڈ', + 'ﮉ' => 'ڈ', + 'ﮊ' => 'ژ', + 'ﮋ' => 'ژ', + 'ﮌ' => 'ڑ', + 'ﮍ' => 'ڑ', + 'ﮎ' => 'ک', + 'ﮏ' => 'ک', + 'ﮐ' => 'ک', + 'ﮑ' => 'ک', + 'ﮒ' => 'گ', + 'ﮓ' => 'گ', + 'ﮔ' => 'گ', + 'ﮕ' => 'گ', + 'ﮖ' => 'ڳ', + 'ﮗ' => 'ڳ', + 'ﮘ' => 'ڳ', + 'ﮙ' => 'ڳ', + 'ﮚ' => 'ڱ', + 'ﮛ' => 'ڱ', + 'ﮜ' => 'ڱ', + 'ﮝ' => 'ڱ', + 'ﮞ' => 'ں', + 'ﮟ' => 'ں', + 'ﮠ' => 'ڻ', + 'ﮡ' => 'ڻ', + 'ﮢ' => 'ڻ', + 'ﮣ' => 'ڻ', + 'ﮤ' => 'ۀ', + 'ﮥ' => 'ۀ', + 'ﮦ' => 'ہ', + 'ﮧ' => 'ہ', + 'ﮨ' => 'ہ', + 'ﮩ' => 'ہ', + 'ﮪ' => 'ھ', + 'ﮫ' => 'ھ', + 'ﮬ' => 'ھ', + 'ﮭ' => 'ھ', + 'ﮮ' => 'ے', + 'ﮯ' => 'ے', + 'ﮰ' => 'ۓ', + 'ﮱ' => 'ۓ', + 'ﯓ' => 'ڭ', + 'ﯔ' => 'ڭ', + 'ﯕ' => 'ڭ', + 'ﯖ' => 'ڭ', + 'ﯗ' => 'ۇ', + 'ﯘ' => 'ۇ', + 'ﯙ' => 'ۆ', + 'ﯚ' => 'ۆ', + 'ﯛ' => 'ۈ', + 'ﯜ' => 'ۈ', + 'ﯝ' => 'ۇٴ', + 'ﯞ' => 'ۋ', + 'ﯟ' => 'ۋ', + 'ﯠ' => 'ۅ', + 'ﯡ' => 'ۅ', + 'ﯢ' => 'ۉ', + 'ﯣ' => 'ۉ', + 'ﯤ' => 'ې', + 'ﯥ' => 'ې', + 'ﯦ' => 'ې', + 'ﯧ' => 'ې', + 'ﯨ' => 'ى', + 'ﯩ' => 'ى', + 'ﯪ' => 'ئا', + 'ﯫ' => 'ئا', + 'ﯬ' => 'ئە', + 'ﯭ' => 'ئە', + 'ﯮ' => 'ئو', + 'ﯯ' => 'ئو', + 'ﯰ' => 'ئۇ', + 'ﯱ' => 'ئۇ', + 'ﯲ' => 'ئۆ', + 'ﯳ' => 'ئۆ', + 'ﯴ' => 'ئۈ', + 'ﯵ' => 'ئۈ', + 'ﯶ' => 'ئې', + 'ﯷ' => 'ئې', + 'ﯸ' => 'ئې', + 'ﯹ' => 'ئى', + 'ﯺ' => 'ئى', + 'ﯻ' => 'ئى', + 'ﯼ' => 'ی', + 'ﯽ' => 'ی', + 'ﯾ' => 'ی', + 'ﯿ' => 'ی', + 'ﰀ' => 'ئج', + 'ﰁ' => 'ئح', + 'ﰂ' => 'ئم', + 'ﰃ' => 'ئى', + 'ﰄ' => 'ئي', + 'ﰅ' => 'بج', + 'ﰆ' => 'بح', + 'ﰇ' => 'بخ', + 'ﰈ' => 'بم', + 'ﰉ' => 'بى', + 'ﰊ' => 'بي', + 'ﰋ' => 'تج', + 'ﰌ' => 'تح', + 'ﰍ' => 'تخ', + 'ﰎ' => 'تم', + 'ﰏ' => 'تى', + 'ﰐ' => 'تي', + 'ﰑ' => 'ثج', + 'ﰒ' => 'ثم', + 'ﰓ' => 'ثى', + 'ﰔ' => 'ثي', + 'ﰕ' => 'جح', + 'ﰖ' => 'جم', + 'ﰗ' => 'حج', + 'ﰘ' => 'حم', + 'ﰙ' => 'خج', + 'ﰚ' => 'خح', + 'ﰛ' => 'خم', + 'ﰜ' => 'سج', + 'ﰝ' => 'سح', + 'ﰞ' => 'سخ', + 'ﰟ' => 'سم', + 'ﰠ' => 'صح', + 'ﰡ' => 'صم', + 'ﰢ' => 'ضج', + 'ﰣ' => 'ضح', + 'ﰤ' => 'ضخ', + 'ﰥ' => 'ضم', + 'ﰦ' => 'طح', + 'ﰧ' => 'طم', + 'ﰨ' => 'ظم', + 'ﰩ' => 'عج', + 'ﰪ' => 'عم', + 'ﰫ' => 'غج', + 'ﰬ' => 'غم', + 'ﰭ' => 'فج', + 'ﰮ' => 'فح', + 'ﰯ' => 'فخ', + 'ﰰ' => 'فم', + 'ﰱ' => 'فى', + 'ﰲ' => 'في', + 'ﰳ' => 'قح', + 'ﰴ' => 'قم', + 'ﰵ' => 'قى', + 'ﰶ' => 'قي', + 'ﰷ' => 'كا', + 'ﰸ' => 'كج', + 'ﰹ' => 'كح', + 'ﰺ' => 'كخ', + 'ﰻ' => 'كل', + 'ﰼ' => 'كم', + 'ﰽ' => 'كى', + 'ﰾ' => 'كي', + 'ﰿ' => 'لج', + 'ﱀ' => 'لح', + 'ﱁ' => 'لخ', + 'ﱂ' => 'لم', + 'ﱃ' => 'لى', + 'ﱄ' => 'لي', + 'ﱅ' => 'مج', + 'ﱆ' => 'مح', + 'ﱇ' => 'مخ', + 'ﱈ' => 'مم', + 'ﱉ' => 'مى', + 'ﱊ' => 'مي', + 'ﱋ' => 'نج', + 'ﱌ' => 'نح', + 'ﱍ' => 'نخ', + 'ﱎ' => 'نم', + 'ﱏ' => 'نى', + 'ﱐ' => 'ني', + 'ﱑ' => 'هج', + 'ﱒ' => 'هم', + 'ﱓ' => 'هى', + 'ﱔ' => 'هي', + 'ﱕ' => 'يج', + 'ﱖ' => 'يح', + 'ﱗ' => 'يخ', + 'ﱘ' => 'يم', + 'ﱙ' => 'يى', + 'ﱚ' => 'يي', + 'ﱛ' => 'ذٰ', + 'ﱜ' => 'رٰ', + 'ﱝ' => 'ىٰ', + 'ﱞ' => ' ٌّ', + 'ﱟ' => ' ٍّ', + 'ﱠ' => ' َّ', + 'ﱡ' => ' ُّ', + 'ﱢ' => ' ِّ', + 'ﱣ' => ' ّٰ', + 'ﱤ' => 'ئر', + 'ﱥ' => 'ئز', + 'ﱦ' => 'ئم', + 'ﱧ' => 'ئن', + 'ﱨ' => 'ئى', + 'ﱩ' => 'ئي', + 'ﱪ' => 'بر', + 'ﱫ' => 'بز', + 'ﱬ' => 'بم', + 'ﱭ' => 'بن', + 'ﱮ' => 'بى', + 'ﱯ' => 'بي', + 'ﱰ' => 'تر', + 'ﱱ' => 'تز', + 'ﱲ' => 'تم', + 'ﱳ' => 'تن', + 'ﱴ' => 'تى', + 'ﱵ' => 'تي', + 'ﱶ' => 'ثر', + 'ﱷ' => 'ثز', + 'ﱸ' => 'ثم', + 'ﱹ' => 'ثن', + 'ﱺ' => 'ثى', + 'ﱻ' => 'ثي', + 'ﱼ' => 'فى', + 'ﱽ' => 'في', + 'ﱾ' => 'قى', + 'ﱿ' => 'قي', + 'ﲀ' => 'كا', + 'ﲁ' => 'كل', + 'ﲂ' => 'كم', + 'ﲃ' => 'كى', + 'ﲄ' => 'كي', + 'ﲅ' => 'لم', + 'ﲆ' => 'لى', + 'ﲇ' => 'لي', + 'ﲈ' => 'ما', + 'ﲉ' => 'مم', + 'ﲊ' => 'نر', + 'ﲋ' => 'نز', + 'ﲌ' => 'نم', + 'ﲍ' => 'نن', + 'ﲎ' => 'نى', + 'ﲏ' => 'ني', + 'ﲐ' => 'ىٰ', + 'ﲑ' => 'ير', + 'ﲒ' => 'يز', + 'ﲓ' => 'يم', + 'ﲔ' => 'ين', + 'ﲕ' => 'يى', + 'ﲖ' => 'يي', + 'ﲗ' => 'ئج', + 'ﲘ' => 'ئح', + 'ﲙ' => 'ئخ', + 'ﲚ' => 'ئم', + 'ﲛ' => 'ئه', + 'ﲜ' => 'بج', + 'ﲝ' => 'بح', + 'ﲞ' => 'بخ', + 'ﲟ' => 'بم', + 'ﲠ' => 'به', + 'ﲡ' => 'تج', + 'ﲢ' => 'تح', + 'ﲣ' => 'تخ', + 'ﲤ' => 'تم', + 'ﲥ' => 'ته', + 'ﲦ' => 'ثم', + 'ﲧ' => 'جح', + 'ﲨ' => 'جم', + 'ﲩ' => 'حج', + 'ﲪ' => 'حم', + 'ﲫ' => 'خج', + 'ﲬ' => 'خم', + 'ﲭ' => 'سج', + 'ﲮ' => 'سح', + 'ﲯ' => 'سخ', + 'ﲰ' => 'سم', + 'ﲱ' => 'صح', + 'ﲲ' => 'صخ', + 'ﲳ' => 'صم', + 'ﲴ' => 'ضج', + 'ﲵ' => 'ضح', + 'ﲶ' => 'ضخ', + 'ﲷ' => 'ضم', + 'ﲸ' => 'طح', + 'ﲹ' => 'ظم', + 'ﲺ' => 'عج', + 'ﲻ' => 'عم', + 'ﲼ' => 'غج', + 'ﲽ' => 'غم', + 'ﲾ' => 'فج', + 'ﲿ' => 'فح', + 'ﳀ' => 'فخ', + 'ﳁ' => 'فم', + 'ﳂ' => 'قح', + 'ﳃ' => 'قم', + 'ﳄ' => 'كج', + 'ﳅ' => 'كح', + 'ﳆ' => 'كخ', + 'ﳇ' => 'كل', + 'ﳈ' => 'كم', + 'ﳉ' => 'لج', + 'ﳊ' => 'لح', + 'ﳋ' => 'لخ', + 'ﳌ' => 'لم', + 'ﳍ' => 'له', + 'ﳎ' => 'مج', + 'ﳏ' => 'مح', + 'ﳐ' => 'مخ', + 'ﳑ' => 'مم', + 'ﳒ' => 'نج', + 'ﳓ' => 'نح', + 'ﳔ' => 'نخ', + 'ﳕ' => 'نم', + 'ﳖ' => 'نه', + 'ﳗ' => 'هج', + 'ﳘ' => 'هم', + 'ﳙ' => 'هٰ', + 'ﳚ' => 'يج', + 'ﳛ' => 'يح', + 'ﳜ' => 'يخ', + 'ﳝ' => 'يم', + 'ﳞ' => 'يه', + 'ﳟ' => 'ئم', + 'ﳠ' => 'ئه', + 'ﳡ' => 'بم', + 'ﳢ' => 'به', + 'ﳣ' => 'تم', + 'ﳤ' => 'ته', + 'ﳥ' => 'ثم', + 'ﳦ' => 'ثه', + 'ﳧ' => 'سم', + 'ﳨ' => 'سه', + 'ﳩ' => 'شم', + 'ﳪ' => 'شه', + 'ﳫ' => 'كل', + 'ﳬ' => 'كم', + 'ﳭ' => 'لم', + 'ﳮ' => 'نم', + 'ﳯ' => 'نه', + 'ﳰ' => 'يم', + 'ﳱ' => 'يه', + 'ﳲ' => 'ـَّ', + 'ﳳ' => 'ـُّ', + 'ﳴ' => 'ـِّ', + 'ﳵ' => 'طى', + 'ﳶ' => 'طي', + 'ﳷ' => 'عى', + 'ﳸ' => 'عي', + 'ﳹ' => 'غى', + 'ﳺ' => 'غي', + 'ﳻ' => 'سى', + 'ﳼ' => 'سي', + 'ﳽ' => 'شى', + 'ﳾ' => 'شي', + 'ﳿ' => 'حى', + 'ﴀ' => 'حي', + 'ﴁ' => 'جى', + 'ﴂ' => 'جي', + 'ﴃ' => 'خى', + 'ﴄ' => 'خي', + 'ﴅ' => 'صى', + 'ﴆ' => 'صي', + 'ﴇ' => 'ضى', + 'ﴈ' => 'ضي', + 'ﴉ' => 'شج', + 'ﴊ' => 'شح', + 'ﴋ' => 'شخ', + 'ﴌ' => 'شم', + 'ﴍ' => 'شر', + 'ﴎ' => 'سر', + 'ﴏ' => 'صر', + 'ﴐ' => 'ضر', + 'ﴑ' => 'طى', + 'ﴒ' => 'طي', + 'ﴓ' => 'عى', + 'ﴔ' => 'عي', + 'ﴕ' => 'غى', + 'ﴖ' => 'غي', + 'ﴗ' => 'سى', + 'ﴘ' => 'سي', + 'ﴙ' => 'شى', + 'ﴚ' => 'شي', + 'ﴛ' => 'حى', + 'ﴜ' => 'حي', + 'ﴝ' => 'جى', + 'ﴞ' => 'جي', + 'ﴟ' => 'خى', + 'ﴠ' => 'خي', + 'ﴡ' => 'صى', + 'ﴢ' => 'صي', + 'ﴣ' => 'ضى', + 'ﴤ' => 'ضي', + 'ﴥ' => 'شج', + 'ﴦ' => 'شح', + 'ﴧ' => 'شخ', + 'ﴨ' => 'شم', + 'ﴩ' => 'شر', + 'ﴪ' => 'سر', + 'ﴫ' => 'صر', + 'ﴬ' => 'ضر', + 'ﴭ' => 'شج', + 'ﴮ' => 'شح', + 'ﴯ' => 'شخ', + 'ﴰ' => 'شم', + 'ﴱ' => 'سه', + 'ﴲ' => 'شه', + 'ﴳ' => 'طم', + 'ﴴ' => 'سج', + 'ﴵ' => 'سح', + 'ﴶ' => 'سخ', + 'ﴷ' => 'شج', + 'ﴸ' => 'شح', + 'ﴹ' => 'شخ', + 'ﴺ' => 'طم', + 'ﴻ' => 'ظم', + 'ﴼ' => 'اً', + 'ﴽ' => 'اً', + 'ﵐ' => 'تجم', + 'ﵑ' => 'تحج', + 'ﵒ' => 'تحج', + 'ﵓ' => 'تحم', + 'ﵔ' => 'تخم', + 'ﵕ' => 'تمج', + 'ﵖ' => 'تمح', + 'ﵗ' => 'تمخ', + 'ﵘ' => 'جمح', + 'ﵙ' => 'جمح', + 'ﵚ' => 'حمي', + 'ﵛ' => 'حمى', + 'ﵜ' => 'سحج', + 'ﵝ' => 'سجح', + 'ﵞ' => 'سجى', + 'ﵟ' => 'سمح', + 'ﵠ' => 'سمح', + 'ﵡ' => 'سمج', + 'ﵢ' => 'سمم', + 'ﵣ' => 'سمم', + 'ﵤ' => 'صحح', + 'ﵥ' => 'صحح', + 'ﵦ' => 'صمم', + 'ﵧ' => 'شحم', + 'ﵨ' => 'شحم', + 'ﵩ' => 'شجي', + 'ﵪ' => 'شمخ', + 'ﵫ' => 'شمخ', + 'ﵬ' => 'شمم', + 'ﵭ' => 'شمم', + 'ﵮ' => 'ضحى', + 'ﵯ' => 'ضخم', + 'ﵰ' => 'ضخم', + 'ﵱ' => 'طمح', + 'ﵲ' => 'طمح', + 'ﵳ' => 'طمم', + 'ﵴ' => 'طمي', + 'ﵵ' => 'عجم', + 'ﵶ' => 'عمم', + 'ﵷ' => 'عمم', + 'ﵸ' => 'عمى', + 'ﵹ' => 'غمم', + 'ﵺ' => 'غمي', + 'ﵻ' => 'غمى', + 'ﵼ' => 'فخم', + 'ﵽ' => 'فخم', + 'ﵾ' => 'قمح', + 'ﵿ' => 'قمم', + 'ﶀ' => 'لحم', + 'ﶁ' => 'لحي', + 'ﶂ' => 'لحى', + 'ﶃ' => 'لجج', + 'ﶄ' => 'لجج', + 'ﶅ' => 'لخم', + 'ﶆ' => 'لخم', + 'ﶇ' => 'لمح', + 'ﶈ' => 'لمح', + 'ﶉ' => 'محج', + 'ﶊ' => 'محم', + 'ﶋ' => 'محي', + 'ﶌ' => 'مجح', + 'ﶍ' => 'مجم', + 'ﶎ' => 'مخج', + 'ﶏ' => 'مخم', + 'ﶒ' => 'مجخ', + 'ﶓ' => 'همج', + 'ﶔ' => 'همم', + 'ﶕ' => 'نحم', + 'ﶖ' => 'نحى', + 'ﶗ' => 'نجم', + 'ﶘ' => 'نجم', + 'ﶙ' => 'نجى', + 'ﶚ' => 'نمي', + 'ﶛ' => 'نمى', + 'ﶜ' => 'يمم', + 'ﶝ' => 'يمم', + 'ﶞ' => 'بخي', + 'ﶟ' => 'تجي', + 'ﶠ' => 'تجى', + 'ﶡ' => 'تخي', + 'ﶢ' => 'تخى', + 'ﶣ' => 'تمي', + 'ﶤ' => 'تمى', + 'ﶥ' => 'جمي', + 'ﶦ' => 'جحى', + 'ﶧ' => 'جمى', + 'ﶨ' => 'سخى', + 'ﶩ' => 'صحي', + 'ﶪ' => 'شحي', + 'ﶫ' => 'ضحي', + 'ﶬ' => 'لجي', + 'ﶭ' => 'لمي', + 'ﶮ' => 'يحي', + 'ﶯ' => 'يجي', + 'ﶰ' => 'يمي', + 'ﶱ' => 'ممي', + 'ﶲ' => 'قمي', + 'ﶳ' => 'نحي', + 'ﶴ' => 'قمح', + 'ﶵ' => 'لحم', + 'ﶶ' => 'عمي', + 'ﶷ' => 'كمي', + 'ﶸ' => 'نجح', + 'ﶹ' => 'مخي', + 'ﶺ' => 'لجم', + 'ﶻ' => 'كمم', + 'ﶼ' => 'لجم', + 'ﶽ' => 'نجح', + 'ﶾ' => 'جحي', + 'ﶿ' => 'حجي', + 'ﷀ' => 'مجي', + 'ﷁ' => 'فمي', + 'ﷂ' => 'بحي', + 'ﷃ' => 'كمم', + 'ﷄ' => 'عجم', + 'ﷅ' => 'صمم', + 'ﷆ' => 'سخي', + 'ﷇ' => 'نجي', + 'ﷰ' => 'صلے', + 'ﷱ' => 'قلے', + 'ﷲ' => 'الله', + 'ﷳ' => 'اكبر', + 'ﷴ' => 'محمد', + 'ﷵ' => 'صلعم', + 'ﷶ' => 'رسول', + 'ﷷ' => 'عليه', + 'ﷸ' => 'وسلم', + 'ﷹ' => 'صلى', + 'ﷺ' => 'صلى الله عليه وسلم', + 'ﷻ' => 'جل جلاله', + '﷼' => 'ریال', + '︐' => ',', + '︑' => '、', + '︒' => '。', + '︓' => ':', + '︔' => ';', + '︕' => '!', + '︖' => '?', + '︗' => '〖', + '︘' => '〗', + '︙' => '...', + '︰' => '..', + '︱' => '—', + '︲' => '–', + '︳' => '_', + '︴' => '_', + '︵' => '(', + '︶' => ')', + '︷' => '{', + '︸' => '}', + '︹' => '〔', + '︺' => '〕', + '︻' => '【', + '︼' => '】', + '︽' => '《', + '︾' => '》', + '︿' => '〈', + '﹀' => '〉', + '﹁' => '「', + '﹂' => '」', + '﹃' => '『', + '﹄' => '』', + '﹇' => '[', + '﹈' => ']', + '﹉' => ' ̅', + '﹊' => ' ̅', + '﹋' => ' ̅', + '﹌' => ' ̅', + '﹍' => '_', + '﹎' => '_', + '﹏' => '_', + '﹐' => ',', + '﹑' => '、', + '﹒' => '.', + '﹔' => ';', + '﹕' => ':', + '﹖' => '?', + '﹗' => '!', + '﹘' => '—', + '﹙' => '(', + '﹚' => ')', + '﹛' => '{', + '﹜' => '}', + '﹝' => '〔', + '﹞' => '〕', + '﹟' => '#', + '﹠' => '&', + '﹡' => '*', + '﹢' => '+', + '﹣' => '-', + '﹤' => '<', + '﹥' => '>', + '﹦' => '=', + '﹨' => '\\', + '﹩' => '$', + '﹪' => '%', + '﹫' => '@', + 'ﹰ' => ' ً', + 'ﹱ' => 'ـً', + 'ﹲ' => ' ٌ', + 'ﹴ' => ' ٍ', + 'ﹶ' => ' َ', + 'ﹷ' => 'ـَ', + 'ﹸ' => ' ُ', + 'ﹹ' => 'ـُ', + 'ﹺ' => ' ِ', + 'ﹻ' => 'ـِ', + 'ﹼ' => ' ّ', + 'ﹽ' => 'ـّ', + 'ﹾ' => ' ْ', + 'ﹿ' => 'ـْ', + 'ﺀ' => 'ء', + 'ﺁ' => 'آ', + 'ﺂ' => 'آ', + 'ﺃ' => 'أ', + 'ﺄ' => 'أ', + 'ﺅ' => 'ؤ', + 'ﺆ' => 'ؤ', + 'ﺇ' => 'إ', + 'ﺈ' => 'إ', + 'ﺉ' => 'ئ', + 'ﺊ' => 'ئ', + 'ﺋ' => 'ئ', + 'ﺌ' => 'ئ', + 'ﺍ' => 'ا', + 'ﺎ' => 'ا', + 'ﺏ' => 'ب', + 'ﺐ' => 'ب', + 'ﺑ' => 'ب', + 'ﺒ' => 'ب', + 'ﺓ' => 'ة', + 'ﺔ' => 'ة', + 'ﺕ' => 'ت', + 'ﺖ' => 'ت', + 'ﺗ' => 'ت', + 'ﺘ' => 'ت', + 'ﺙ' => 'ث', + 'ﺚ' => 'ث', + 'ﺛ' => 'ث', + 'ﺜ' => 'ث', + 'ﺝ' => 'ج', + 'ﺞ' => 'ج', + 'ﺟ' => 'ج', + 'ﺠ' => 'ج', + 'ﺡ' => 'ح', + 'ﺢ' => 'ح', + 'ﺣ' => 'ح', + 'ﺤ' => 'ح', + 'ﺥ' => 'خ', + 'ﺦ' => 'خ', + 'ﺧ' => 'خ', + 'ﺨ' => 'خ', + 'ﺩ' => 'د', + 'ﺪ' => 'د', + 'ﺫ' => 'ذ', + 'ﺬ' => 'ذ', + 'ﺭ' => 'ر', + 'ﺮ' => 'ر', + 'ﺯ' => 'ز', + 'ﺰ' => 'ز', + 'ﺱ' => 'س', + 'ﺲ' => 'س', + 'ﺳ' => 'س', + 'ﺴ' => 'س', + 'ﺵ' => 'ش', + 'ﺶ' => 'ش', + 'ﺷ' => 'ش', + 'ﺸ' => 'ش', + 'ﺹ' => 'ص', + 'ﺺ' => 'ص', + 'ﺻ' => 'ص', + 'ﺼ' => 'ص', + 'ﺽ' => 'ض', + 'ﺾ' => 'ض', + 'ﺿ' => 'ض', + 'ﻀ' => 'ض', + 'ﻁ' => 'ط', + 'ﻂ' => 'ط', + 'ﻃ' => 'ط', + 'ﻄ' => 'ط', + 'ﻅ' => 'ظ', + 'ﻆ' => 'ظ', + 'ﻇ' => 'ظ', + 'ﻈ' => 'ظ', + 'ﻉ' => 'ع', + 'ﻊ' => 'ع', + 'ﻋ' => 'ع', + 'ﻌ' => 'ع', + 'ﻍ' => 'غ', + 'ﻎ' => 'غ', + 'ﻏ' => 'غ', + 'ﻐ' => 'غ', + 'ﻑ' => 'ف', + 'ﻒ' => 'ف', + 'ﻓ' => 'ف', + 'ﻔ' => 'ف', + 'ﻕ' => 'ق', + 'ﻖ' => 'ق', + 'ﻗ' => 'ق', + 'ﻘ' => 'ق', + 'ﻙ' => 'ك', + 'ﻚ' => 'ك', + 'ﻛ' => 'ك', + 'ﻜ' => 'ك', + 'ﻝ' => 'ل', + 'ﻞ' => 'ل', + 'ﻟ' => 'ل', + 'ﻠ' => 'ل', + 'ﻡ' => 'م', + 'ﻢ' => 'م', + 'ﻣ' => 'م', + 'ﻤ' => 'م', + 'ﻥ' => 'ن', + 'ﻦ' => 'ن', + 'ﻧ' => 'ن', + 'ﻨ' => 'ن', + 'ﻩ' => 'ه', + 'ﻪ' => 'ه', + 'ﻫ' => 'ه', + 'ﻬ' => 'ه', + 'ﻭ' => 'و', + 'ﻮ' => 'و', + 'ﻯ' => 'ى', + 'ﻰ' => 'ى', + 'ﻱ' => 'ي', + 'ﻲ' => 'ي', + 'ﻳ' => 'ي', + 'ﻴ' => 'ي', + 'ﻵ' => 'لآ', + 'ﻶ' => 'لآ', + 'ﻷ' => 'لأ', + 'ﻸ' => 'لأ', + 'ﻹ' => 'لإ', + 'ﻺ' => 'لإ', + 'ﻻ' => 'لا', + 'ﻼ' => 'لا', + '!' => '!', + '"' => '"', + '#' => '#', + '$' => '$', + '%' => '%', + '&' => '&', + ''' => '\'', + '(' => '(', + ')' => ')', + '*' => '*', + '+' => '+', + ',' => ',', + '-' => '-', + '.' => '.', + '/' => '/', + '0' => '0', + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + '7' => '7', + '8' => '8', + '9' => '9', + ':' => ':', + ';' => ';', + '<' => '<', + '=' => '=', + '>' => '>', + '?' => '?', + '@' => '@', + 'A' => 'A', + 'B' => 'B', + 'C' => 'C', + 'D' => 'D', + 'E' => 'E', + 'F' => 'F', + 'G' => 'G', + 'H' => 'H', + 'I' => 'I', + 'J' => 'J', + 'K' => 'K', + 'L' => 'L', + 'M' => 'M', + 'N' => 'N', + 'O' => 'O', + 'P' => 'P', + 'Q' => 'Q', + 'R' => 'R', + 'S' => 'S', + 'T' => 'T', + 'U' => 'U', + 'V' => 'V', + 'W' => 'W', + 'X' => 'X', + 'Y' => 'Y', + 'Z' => 'Z', + '[' => '[', + '\' => '\\', + ']' => ']', + '^' => '^', + '_' => '_', + '`' => '`', + 'a' => 'a', + 'b' => 'b', + 'c' => 'c', + 'd' => 'd', + 'e' => 'e', + 'f' => 'f', + 'g' => 'g', + 'h' => 'h', + 'i' => 'i', + 'j' => 'j', + 'k' => 'k', + 'l' => 'l', + 'm' => 'm', + 'n' => 'n', + 'o' => 'o', + 'p' => 'p', + 'q' => 'q', + 'r' => 'r', + 's' => 's', + 't' => 't', + 'u' => 'u', + 'v' => 'v', + 'w' => 'w', + 'x' => 'x', + 'y' => 'y', + 'z' => 'z', + '{' => '{', + '|' => '|', + '}' => '}', + '~' => '~', + '⦅' => '⦅', + '⦆' => '⦆', + '。' => '。', + '「' => '「', + '」' => '」', + '、' => '、', + '・' => '・', + 'ヲ' => 'ヲ', + 'ァ' => 'ァ', + 'ィ' => 'ィ', + 'ゥ' => 'ゥ', + 'ェ' => 'ェ', + 'ォ' => 'ォ', + 'ャ' => 'ャ', + 'ュ' => 'ュ', + 'ョ' => 'ョ', + 'ッ' => 'ッ', + 'ー' => 'ー', + 'ア' => 'ア', + 'イ' => 'イ', + 'ウ' => 'ウ', + 'エ' => 'エ', + 'オ' => 'オ', + 'カ' => 'カ', + 'キ' => 'キ', + 'ク' => 'ク', + 'ケ' => 'ケ', + 'コ' => 'コ', + 'サ' => 'サ', + 'シ' => 'シ', + 'ス' => 'ス', + 'セ' => 'セ', + 'ソ' => 'ソ', + 'タ' => 'タ', + 'チ' => 'チ', + 'ツ' => 'ツ', + 'テ' => 'テ', + 'ト' => 'ト', + 'ナ' => 'ナ', + 'ニ' => 'ニ', + 'ヌ' => 'ヌ', + 'ネ' => 'ネ', + 'ノ' => 'ノ', + 'ハ' => 'ハ', + 'ヒ' => 'ヒ', + 'フ' => 'フ', + 'ヘ' => 'ヘ', + 'ホ' => 'ホ', + 'マ' => 'マ', + 'ミ' => 'ミ', + 'ム' => 'ム', + 'メ' => 'メ', + 'モ' => 'モ', + 'ヤ' => 'ヤ', + 'ユ' => 'ユ', + 'ヨ' => 'ヨ', + 'ラ' => 'ラ', + 'リ' => 'リ', + 'ル' => 'ル', + 'レ' => 'レ', + 'ロ' => 'ロ', + 'ワ' => 'ワ', + 'ン' => 'ン', + '゙' => '゙', + '゚' => '゚', + 'ᅠ' => 'ᅠ', + 'ᄀ' => 'ᄀ', + 'ᄁ' => 'ᄁ', + 'ᆪ' => 'ᆪ', + 'ᄂ' => 'ᄂ', + 'ᆬ' => 'ᆬ', + 'ᆭ' => 'ᆭ', + 'ᄃ' => 'ᄃ', + 'ᄄ' => 'ᄄ', + 'ᄅ' => 'ᄅ', + 'ᆰ' => 'ᆰ', + 'ᆱ' => 'ᆱ', + 'ᆲ' => 'ᆲ', + 'ᆳ' => 'ᆳ', + 'ᆴ' => 'ᆴ', + 'ᆵ' => 'ᆵ', + 'ᄚ' => 'ᄚ', + 'ᄆ' => 'ᄆ', + 'ᄇ' => 'ᄇ', + 'ᄈ' => 'ᄈ', + 'ᄡ' => 'ᄡ', + 'ᄉ' => 'ᄉ', + 'ᄊ' => 'ᄊ', + 'ᄋ' => 'ᄋ', + 'ᄌ' => 'ᄌ', + 'ᄍ' => 'ᄍ', + 'ᄎ' => 'ᄎ', + 'ᄏ' => 'ᄏ', + 'ᄐ' => 'ᄐ', + 'ᄑ' => 'ᄑ', + 'ᄒ' => 'ᄒ', + 'ᅡ' => 'ᅡ', + 'ᅢ' => 'ᅢ', + 'ᅣ' => 'ᅣ', + 'ᅤ' => 'ᅤ', + 'ᅥ' => 'ᅥ', + 'ᅦ' => 'ᅦ', + 'ᅧ' => 'ᅧ', + 'ᅨ' => 'ᅨ', + 'ᅩ' => 'ᅩ', + 'ᅪ' => 'ᅪ', + 'ᅫ' => 'ᅫ', + 'ᅬ' => 'ᅬ', + 'ᅭ' => 'ᅭ', + 'ᅮ' => 'ᅮ', + 'ᅯ' => 'ᅯ', + 'ᅰ' => 'ᅰ', + 'ᅱ' => 'ᅱ', + 'ᅲ' => 'ᅲ', + 'ᅳ' => 'ᅳ', + 'ᅴ' => 'ᅴ', + 'ᅵ' => 'ᅵ', + '¢' => '¢', + '£' => '£', + '¬' => '¬', + ' ̄' => ' ̄', + '¦' => '¦', + '¥' => '¥', + '₩' => '₩', + '│' => '│', + '←' => '←', + '↑' => '↑', + '→' => '→', + '↓' => '↓', + '■' => '■', + '○' => '○', + '𝐀' => 'A', + '𝐁' => 'B', + '𝐂' => 'C', + '𝐃' => 'D', + '𝐄' => 'E', + '𝐅' => 'F', + '𝐆' => 'G', + '𝐇' => 'H', + '𝐈' => 'I', + '𝐉' => 'J', + '𝐊' => 'K', + '𝐋' => 'L', + '𝐌' => 'M', + '𝐍' => 'N', + '𝐎' => 'O', + '𝐏' => 'P', + '𝐐' => 'Q', + '𝐑' => 'R', + '𝐒' => 'S', + '𝐓' => 'T', + '𝐔' => 'U', + '𝐕' => 'V', + '𝐖' => 'W', + '𝐗' => 'X', + '𝐘' => 'Y', + '𝐙' => 'Z', + '𝐚' => 'a', + '𝐛' => 'b', + '𝐜' => 'c', + '𝐝' => 'd', + '𝐞' => 'e', + '𝐟' => 'f', + '𝐠' => 'g', + '𝐡' => 'h', + '𝐢' => 'i', + '𝐣' => 'j', + '𝐤' => 'k', + '𝐥' => 'l', + '𝐦' => 'm', + '𝐧' => 'n', + '𝐨' => 'o', + '𝐩' => 'p', + '𝐪' => 'q', + '𝐫' => 'r', + '𝐬' => 's', + '𝐭' => 't', + '𝐮' => 'u', + '𝐯' => 'v', + '𝐰' => 'w', + '𝐱' => 'x', + '𝐲' => 'y', + '𝐳' => 'z', + '𝐴' => 'A', + '𝐵' => 'B', + '𝐶' => 'C', + '𝐷' => 'D', + '𝐸' => 'E', + '𝐹' => 'F', + '𝐺' => 'G', + '𝐻' => 'H', + '𝐼' => 'I', + '𝐽' => 'J', + '𝐾' => 'K', + '𝐿' => 'L', + '𝑀' => 'M', + '𝑁' => 'N', + '𝑂' => 'O', + '𝑃' => 'P', + '𝑄' => 'Q', + '𝑅' => 'R', + '𝑆' => 'S', + '𝑇' => 'T', + '𝑈' => 'U', + '𝑉' => 'V', + '𝑊' => 'W', + '𝑋' => 'X', + '𝑌' => 'Y', + '𝑍' => 'Z', + '𝑎' => 'a', + '𝑏' => 'b', + '𝑐' => 'c', + '𝑑' => 'd', + '𝑒' => 'e', + '𝑓' => 'f', + '𝑔' => 'g', + '𝑖' => 'i', + '𝑗' => 'j', + '𝑘' => 'k', + '𝑙' => 'l', + '𝑚' => 'm', + '𝑛' => 'n', + '𝑜' => 'o', + '𝑝' => 'p', + '𝑞' => 'q', + '𝑟' => 'r', + '𝑠' => 's', + '𝑡' => 't', + '𝑢' => 'u', + '𝑣' => 'v', + '𝑤' => 'w', + '𝑥' => 'x', + '𝑦' => 'y', + '𝑧' => 'z', + '𝑨' => 'A', + '𝑩' => 'B', + '𝑪' => 'C', + '𝑫' => 'D', + '𝑬' => 'E', + '𝑭' => 'F', + '𝑮' => 'G', + '𝑯' => 'H', + '𝑰' => 'I', + '𝑱' => 'J', + '𝑲' => 'K', + '𝑳' => 'L', + '𝑴' => 'M', + '𝑵' => 'N', + '𝑶' => 'O', + '𝑷' => 'P', + '𝑸' => 'Q', + '𝑹' => 'R', + '𝑺' => 'S', + '𝑻' => 'T', + '𝑼' => 'U', + '𝑽' => 'V', + '𝑾' => 'W', + '𝑿' => 'X', + '𝒀' => 'Y', + '𝒁' => 'Z', + '𝒂' => 'a', + '𝒃' => 'b', + '𝒄' => 'c', + '𝒅' => 'd', + '𝒆' => 'e', + '𝒇' => 'f', + '𝒈' => 'g', + '𝒉' => 'h', + '𝒊' => 'i', + '𝒋' => 'j', + '𝒌' => 'k', + '𝒍' => 'l', + '𝒎' => 'm', + '𝒏' => 'n', + '𝒐' => 'o', + '𝒑' => 'p', + '𝒒' => 'q', + '𝒓' => 'r', + '𝒔' => 's', + '𝒕' => 't', + '𝒖' => 'u', + '𝒗' => 'v', + '𝒘' => 'w', + '𝒙' => 'x', + '𝒚' => 'y', + '𝒛' => 'z', + '𝒜' => 'A', + '𝒞' => 'C', + '𝒟' => 'D', + '𝒢' => 'G', + '𝒥' => 'J', + '𝒦' => 'K', + '𝒩' => 'N', + '𝒪' => 'O', + '𝒫' => 'P', + '𝒬' => 'Q', + '𝒮' => 'S', + '𝒯' => 'T', + '𝒰' => 'U', + '𝒱' => 'V', + '𝒲' => 'W', + '𝒳' => 'X', + '𝒴' => 'Y', + '𝒵' => 'Z', + '𝒶' => 'a', + '𝒷' => 'b', + '𝒸' => 'c', + '𝒹' => 'd', + '𝒻' => 'f', + '𝒽' => 'h', + '𝒾' => 'i', + '𝒿' => 'j', + '𝓀' => 'k', + '𝓁' => 'l', + '𝓂' => 'm', + '𝓃' => 'n', + '𝓅' => 'p', + '𝓆' => 'q', + '𝓇' => 'r', + '𝓈' => 's', + '𝓉' => 't', + '𝓊' => 'u', + '𝓋' => 'v', + '𝓌' => 'w', + '𝓍' => 'x', + '𝓎' => 'y', + '𝓏' => 'z', + '𝓐' => 'A', + '𝓑' => 'B', + '𝓒' => 'C', + '𝓓' => 'D', + '𝓔' => 'E', + '𝓕' => 'F', + '𝓖' => 'G', + '𝓗' => 'H', + '𝓘' => 'I', + '𝓙' => 'J', + '𝓚' => 'K', + '𝓛' => 'L', + '𝓜' => 'M', + '𝓝' => 'N', + '𝓞' => 'O', + '𝓟' => 'P', + '𝓠' => 'Q', + '𝓡' => 'R', + '𝓢' => 'S', + '𝓣' => 'T', + '𝓤' => 'U', + '𝓥' => 'V', + '𝓦' => 'W', + '𝓧' => 'X', + '𝓨' => 'Y', + '𝓩' => 'Z', + '𝓪' => 'a', + '𝓫' => 'b', + '𝓬' => 'c', + '𝓭' => 'd', + '𝓮' => 'e', + '𝓯' => 'f', + '𝓰' => 'g', + '𝓱' => 'h', + '𝓲' => 'i', + '𝓳' => 'j', + '𝓴' => 'k', + '𝓵' => 'l', + '𝓶' => 'm', + '𝓷' => 'n', + '𝓸' => 'o', + '𝓹' => 'p', + '𝓺' => 'q', + '𝓻' => 'r', + '𝓼' => 's', + '𝓽' => 't', + '𝓾' => 'u', + '𝓿' => 'v', + '𝔀' => 'w', + '𝔁' => 'x', + '𝔂' => 'y', + '𝔃' => 'z', + '𝔄' => 'A', + '𝔅' => 'B', + '𝔇' => 'D', + '𝔈' => 'E', + '𝔉' => 'F', + '𝔊' => 'G', + '𝔍' => 'J', + '𝔎' => 'K', + '𝔏' => 'L', + '𝔐' => 'M', + '𝔑' => 'N', + '𝔒' => 'O', + '𝔓' => 'P', + '𝔔' => 'Q', + '𝔖' => 'S', + '𝔗' => 'T', + '𝔘' => 'U', + '𝔙' => 'V', + '𝔚' => 'W', + '𝔛' => 'X', + '𝔜' => 'Y', + '𝔞' => 'a', + '𝔟' => 'b', + '𝔠' => 'c', + '𝔡' => 'd', + '𝔢' => 'e', + '𝔣' => 'f', + '𝔤' => 'g', + '𝔥' => 'h', + '𝔦' => 'i', + '𝔧' => 'j', + '𝔨' => 'k', + '𝔩' => 'l', + '𝔪' => 'm', + '𝔫' => 'n', + '𝔬' => 'o', + '𝔭' => 'p', + '𝔮' => 'q', + '𝔯' => 'r', + '𝔰' => 's', + '𝔱' => 't', + '𝔲' => 'u', + '𝔳' => 'v', + '𝔴' => 'w', + '𝔵' => 'x', + '𝔶' => 'y', + '𝔷' => 'z', + '𝔸' => 'A', + '𝔹' => 'B', + '𝔻' => 'D', + '𝔼' => 'E', + '𝔽' => 'F', + '𝔾' => 'G', + '𝕀' => 'I', + '𝕁' => 'J', + '𝕂' => 'K', + '𝕃' => 'L', + '𝕄' => 'M', + '𝕆' => 'O', + '𝕊' => 'S', + '𝕋' => 'T', + '𝕌' => 'U', + '𝕍' => 'V', + '𝕎' => 'W', + '𝕏' => 'X', + '𝕐' => 'Y', + '𝕒' => 'a', + '𝕓' => 'b', + '𝕔' => 'c', + '𝕕' => 'd', + '𝕖' => 'e', + '𝕗' => 'f', + '𝕘' => 'g', + '𝕙' => 'h', + '𝕚' => 'i', + '𝕛' => 'j', + '𝕜' => 'k', + '𝕝' => 'l', + '𝕞' => 'm', + '𝕟' => 'n', + '𝕠' => 'o', + '𝕡' => 'p', + '𝕢' => 'q', + '𝕣' => 'r', + '𝕤' => 's', + '𝕥' => 't', + '𝕦' => 'u', + '𝕧' => 'v', + '𝕨' => 'w', + '𝕩' => 'x', + '𝕪' => 'y', + '𝕫' => 'z', + '𝕬' => 'A', + '𝕭' => 'B', + '𝕮' => 'C', + '𝕯' => 'D', + '𝕰' => 'E', + '𝕱' => 'F', + '𝕲' => 'G', + '𝕳' => 'H', + '𝕴' => 'I', + '𝕵' => 'J', + '𝕶' => 'K', + '𝕷' => 'L', + '𝕸' => 'M', + '𝕹' => 'N', + '𝕺' => 'O', + '𝕻' => 'P', + '𝕼' => 'Q', + '𝕽' => 'R', + '𝕾' => 'S', + '𝕿' => 'T', + '𝖀' => 'U', + '𝖁' => 'V', + '𝖂' => 'W', + '𝖃' => 'X', + '𝖄' => 'Y', + '𝖅' => 'Z', + '𝖆' => 'a', + '𝖇' => 'b', + '𝖈' => 'c', + '𝖉' => 'd', + '𝖊' => 'e', + '𝖋' => 'f', + '𝖌' => 'g', + '𝖍' => 'h', + '𝖎' => 'i', + '𝖏' => 'j', + '𝖐' => 'k', + '𝖑' => 'l', + '𝖒' => 'm', + '𝖓' => 'n', + '𝖔' => 'o', + '𝖕' => 'p', + '𝖖' => 'q', + '𝖗' => 'r', + '𝖘' => 's', + '𝖙' => 't', + '𝖚' => 'u', + '𝖛' => 'v', + '𝖜' => 'w', + '𝖝' => 'x', + '𝖞' => 'y', + '𝖟' => 'z', + '𝖠' => 'A', + '𝖡' => 'B', + '𝖢' => 'C', + '𝖣' => 'D', + '𝖤' => 'E', + '𝖥' => 'F', + '𝖦' => 'G', + '𝖧' => 'H', + '𝖨' => 'I', + '𝖩' => 'J', + '𝖪' => 'K', + '𝖫' => 'L', + '𝖬' => 'M', + '𝖭' => 'N', + '𝖮' => 'O', + '𝖯' => 'P', + '𝖰' => 'Q', + '𝖱' => 'R', + '𝖲' => 'S', + '𝖳' => 'T', + '𝖴' => 'U', + '𝖵' => 'V', + '𝖶' => 'W', + '𝖷' => 'X', + '𝖸' => 'Y', + '𝖹' => 'Z', + '𝖺' => 'a', + '𝖻' => 'b', + '𝖼' => 'c', + '𝖽' => 'd', + '𝖾' => 'e', + '𝖿' => 'f', + '𝗀' => 'g', + '𝗁' => 'h', + '𝗂' => 'i', + '𝗃' => 'j', + '𝗄' => 'k', + '𝗅' => 'l', + '𝗆' => 'm', + '𝗇' => 'n', + '𝗈' => 'o', + '𝗉' => 'p', + '𝗊' => 'q', + '𝗋' => 'r', + '𝗌' => 's', + '𝗍' => 't', + '𝗎' => 'u', + '𝗏' => 'v', + '𝗐' => 'w', + '𝗑' => 'x', + '𝗒' => 'y', + '𝗓' => 'z', + '𝗔' => 'A', + '𝗕' => 'B', + '𝗖' => 'C', + '𝗗' => 'D', + '𝗘' => 'E', + '𝗙' => 'F', + '𝗚' => 'G', + '𝗛' => 'H', + '𝗜' => 'I', + '𝗝' => 'J', + '𝗞' => 'K', + '𝗟' => 'L', + '𝗠' => 'M', + '𝗡' => 'N', + '𝗢' => 'O', + '𝗣' => 'P', + '𝗤' => 'Q', + '𝗥' => 'R', + '𝗦' => 'S', + '𝗧' => 'T', + '𝗨' => 'U', + '𝗩' => 'V', + '𝗪' => 'W', + '𝗫' => 'X', + '𝗬' => 'Y', + '𝗭' => 'Z', + '𝗮' => 'a', + '𝗯' => 'b', + '𝗰' => 'c', + '𝗱' => 'd', + '𝗲' => 'e', + '𝗳' => 'f', + '𝗴' => 'g', + '𝗵' => 'h', + '𝗶' => 'i', + '𝗷' => 'j', + '𝗸' => 'k', + '𝗹' => 'l', + '𝗺' => 'm', + '𝗻' => 'n', + '𝗼' => 'o', + '𝗽' => 'p', + '𝗾' => 'q', + '𝗿' => 'r', + '𝘀' => 's', + '𝘁' => 't', + '𝘂' => 'u', + '𝘃' => 'v', + '𝘄' => 'w', + '𝘅' => 'x', + '𝘆' => 'y', + '𝘇' => 'z', + '𝘈' => 'A', + '𝘉' => 'B', + '𝘊' => 'C', + '𝘋' => 'D', + '𝘌' => 'E', + '𝘍' => 'F', + '𝘎' => 'G', + '𝘏' => 'H', + '𝘐' => 'I', + '𝘑' => 'J', + '𝘒' => 'K', + '𝘓' => 'L', + '𝘔' => 'M', + '𝘕' => 'N', + '𝘖' => 'O', + '𝘗' => 'P', + '𝘘' => 'Q', + '𝘙' => 'R', + '𝘚' => 'S', + '𝘛' => 'T', + '𝘜' => 'U', + '𝘝' => 'V', + '𝘞' => 'W', + '𝘟' => 'X', + '𝘠' => 'Y', + '𝘡' => 'Z', + '𝘢' => 'a', + '𝘣' => 'b', + '𝘤' => 'c', + '𝘥' => 'd', + '𝘦' => 'e', + '𝘧' => 'f', + '𝘨' => 'g', + '𝘩' => 'h', + '𝘪' => 'i', + '𝘫' => 'j', + '𝘬' => 'k', + '𝘭' => 'l', + '𝘮' => 'm', + '𝘯' => 'n', + '𝘰' => 'o', + '𝘱' => 'p', + '𝘲' => 'q', + '𝘳' => 'r', + '𝘴' => 's', + '𝘵' => 't', + '𝘶' => 'u', + '𝘷' => 'v', + '𝘸' => 'w', + '𝘹' => 'x', + '𝘺' => 'y', + '𝘻' => 'z', + '𝘼' => 'A', + '𝘽' => 'B', + '𝘾' => 'C', + '𝘿' => 'D', + '𝙀' => 'E', + '𝙁' => 'F', + '𝙂' => 'G', + '𝙃' => 'H', + '𝙄' => 'I', + '𝙅' => 'J', + '𝙆' => 'K', + '𝙇' => 'L', + '𝙈' => 'M', + '𝙉' => 'N', + '𝙊' => 'O', + '𝙋' => 'P', + '𝙌' => 'Q', + '𝙍' => 'R', + '𝙎' => 'S', + '𝙏' => 'T', + '𝙐' => 'U', + '𝙑' => 'V', + '𝙒' => 'W', + '𝙓' => 'X', + '𝙔' => 'Y', + '𝙕' => 'Z', + '𝙖' => 'a', + '𝙗' => 'b', + '𝙘' => 'c', + '𝙙' => 'd', + '𝙚' => 'e', + '𝙛' => 'f', + '𝙜' => 'g', + '𝙝' => 'h', + '𝙞' => 'i', + '𝙟' => 'j', + '𝙠' => 'k', + '𝙡' => 'l', + '𝙢' => 'm', + '𝙣' => 'n', + '𝙤' => 'o', + '𝙥' => 'p', + '𝙦' => 'q', + '𝙧' => 'r', + '𝙨' => 's', + '𝙩' => 't', + '𝙪' => 'u', + '𝙫' => 'v', + '𝙬' => 'w', + '𝙭' => 'x', + '𝙮' => 'y', + '𝙯' => 'z', + '𝙰' => 'A', + '𝙱' => 'B', + '𝙲' => 'C', + '𝙳' => 'D', + '𝙴' => 'E', + '𝙵' => 'F', + '𝙶' => 'G', + '𝙷' => 'H', + '𝙸' => 'I', + '𝙹' => 'J', + '𝙺' => 'K', + '𝙻' => 'L', + '𝙼' => 'M', + '𝙽' => 'N', + '𝙾' => 'O', + '𝙿' => 'P', + '𝚀' => 'Q', + '𝚁' => 'R', + '𝚂' => 'S', + '𝚃' => 'T', + '𝚄' => 'U', + '𝚅' => 'V', + '𝚆' => 'W', + '𝚇' => 'X', + '𝚈' => 'Y', + '𝚉' => 'Z', + '𝚊' => 'a', + '𝚋' => 'b', + '𝚌' => 'c', + '𝚍' => 'd', + '𝚎' => 'e', + '𝚏' => 'f', + '𝚐' => 'g', + '𝚑' => 'h', + '𝚒' => 'i', + '𝚓' => 'j', + '𝚔' => 'k', + '𝚕' => 'l', + '𝚖' => 'm', + '𝚗' => 'n', + '𝚘' => 'o', + '𝚙' => 'p', + '𝚚' => 'q', + '𝚛' => 'r', + '𝚜' => 's', + '𝚝' => 't', + '𝚞' => 'u', + '𝚟' => 'v', + '𝚠' => 'w', + '𝚡' => 'x', + '𝚢' => 'y', + '𝚣' => 'z', + '𝚤' => 'ı', + '𝚥' => 'ȷ', + '𝚨' => 'Α', + '𝚩' => 'Β', + '𝚪' => 'Γ', + '𝚫' => 'Δ', + '𝚬' => 'Ε', + '𝚭' => 'Ζ', + '𝚮' => 'Η', + '𝚯' => 'Θ', + '𝚰' => 'Ι', + '𝚱' => 'Κ', + '𝚲' => 'Λ', + '𝚳' => 'Μ', + '𝚴' => 'Ν', + '𝚵' => 'Ξ', + '𝚶' => 'Ο', + '𝚷' => 'Π', + '𝚸' => 'Ρ', + '𝚹' => 'Θ', + '𝚺' => 'Σ', + '𝚻' => 'Τ', + '𝚼' => 'Υ', + '𝚽' => 'Φ', + '𝚾' => 'Χ', + '𝚿' => 'Ψ', + '𝛀' => 'Ω', + '𝛁' => '∇', + '𝛂' => 'α', + '𝛃' => 'β', + '𝛄' => 'γ', + '𝛅' => 'δ', + '𝛆' => 'ε', + '𝛇' => 'ζ', + '𝛈' => 'η', + '𝛉' => 'θ', + '𝛊' => 'ι', + '𝛋' => 'κ', + '𝛌' => 'λ', + '𝛍' => 'μ', + '𝛎' => 'ν', + '𝛏' => 'ξ', + '𝛐' => 'ο', + '𝛑' => 'π', + '𝛒' => 'ρ', + '𝛓' => 'ς', + '𝛔' => 'σ', + '𝛕' => 'τ', + '𝛖' => 'υ', + '𝛗' => 'φ', + '𝛘' => 'χ', + '𝛙' => 'ψ', + '𝛚' => 'ω', + '𝛛' => '∂', + '𝛜' => 'ε', + '𝛝' => 'θ', + '𝛞' => 'κ', + '𝛟' => 'φ', + '𝛠' => 'ρ', + '𝛡' => 'π', + '𝛢' => 'Α', + '𝛣' => 'Β', + '𝛤' => 'Γ', + '𝛥' => 'Δ', + '𝛦' => 'Ε', + '𝛧' => 'Ζ', + '𝛨' => 'Η', + '𝛩' => 'Θ', + '𝛪' => 'Ι', + '𝛫' => 'Κ', + '𝛬' => 'Λ', + '𝛭' => 'Μ', + '𝛮' => 'Ν', + '𝛯' => 'Ξ', + '𝛰' => 'Ο', + '𝛱' => 'Π', + '𝛲' => 'Ρ', + '𝛳' => 'Θ', + '𝛴' => 'Σ', + '𝛵' => 'Τ', + '𝛶' => 'Υ', + '𝛷' => 'Φ', + '𝛸' => 'Χ', + '𝛹' => 'Ψ', + '𝛺' => 'Ω', + '𝛻' => '∇', + '𝛼' => 'α', + '𝛽' => 'β', + '𝛾' => 'γ', + '𝛿' => 'δ', + '𝜀' => 'ε', + '𝜁' => 'ζ', + '𝜂' => 'η', + '𝜃' => 'θ', + '𝜄' => 'ι', + '𝜅' => 'κ', + '𝜆' => 'λ', + '𝜇' => 'μ', + '𝜈' => 'ν', + '𝜉' => 'ξ', + '𝜊' => 'ο', + '𝜋' => 'π', + '𝜌' => 'ρ', + '𝜍' => 'ς', + '𝜎' => 'σ', + '𝜏' => 'τ', + '𝜐' => 'υ', + '𝜑' => 'φ', + '𝜒' => 'χ', + '𝜓' => 'ψ', + '𝜔' => 'ω', + '𝜕' => '∂', + '𝜖' => 'ε', + '𝜗' => 'θ', + '𝜘' => 'κ', + '𝜙' => 'φ', + '𝜚' => 'ρ', + '𝜛' => 'π', + '𝜜' => 'Α', + '𝜝' => 'Β', + '𝜞' => 'Γ', + '𝜟' => 'Δ', + '𝜠' => 'Ε', + '𝜡' => 'Ζ', + '𝜢' => 'Η', + '𝜣' => 'Θ', + '𝜤' => 'Ι', + '𝜥' => 'Κ', + '𝜦' => 'Λ', + '𝜧' => 'Μ', + '𝜨' => 'Ν', + '𝜩' => 'Ξ', + '𝜪' => 'Ο', + '𝜫' => 'Π', + '𝜬' => 'Ρ', + '𝜭' => 'Θ', + '𝜮' => 'Σ', + '𝜯' => 'Τ', + '𝜰' => 'Υ', + '𝜱' => 'Φ', + '𝜲' => 'Χ', + '𝜳' => 'Ψ', + '𝜴' => 'Ω', + '𝜵' => '∇', + '𝜶' => 'α', + '𝜷' => 'β', + '𝜸' => 'γ', + '𝜹' => 'δ', + '𝜺' => 'ε', + '𝜻' => 'ζ', + '𝜼' => 'η', + '𝜽' => 'θ', + '𝜾' => 'ι', + '𝜿' => 'κ', + '𝝀' => 'λ', + '𝝁' => 'μ', + '𝝂' => 'ν', + '𝝃' => 'ξ', + '𝝄' => 'ο', + '𝝅' => 'π', + '𝝆' => 'ρ', + '𝝇' => 'ς', + '𝝈' => 'σ', + '𝝉' => 'τ', + '𝝊' => 'υ', + '𝝋' => 'φ', + '𝝌' => 'χ', + '𝝍' => 'ψ', + '𝝎' => 'ω', + '𝝏' => '∂', + '𝝐' => 'ε', + '𝝑' => 'θ', + '𝝒' => 'κ', + '𝝓' => 'φ', + '𝝔' => 'ρ', + '𝝕' => 'π', + '𝝖' => 'Α', + '𝝗' => 'Β', + '𝝘' => 'Γ', + '𝝙' => 'Δ', + '𝝚' => 'Ε', + '𝝛' => 'Ζ', + '𝝜' => 'Η', + '𝝝' => 'Θ', + '𝝞' => 'Ι', + '𝝟' => 'Κ', + '𝝠' => 'Λ', + '𝝡' => 'Μ', + '𝝢' => 'Ν', + '𝝣' => 'Ξ', + '𝝤' => 'Ο', + '𝝥' => 'Π', + '𝝦' => 'Ρ', + '𝝧' => 'Θ', + '𝝨' => 'Σ', + '𝝩' => 'Τ', + '𝝪' => 'Υ', + '𝝫' => 'Φ', + '𝝬' => 'Χ', + '𝝭' => 'Ψ', + '𝝮' => 'Ω', + '𝝯' => '∇', + '𝝰' => 'α', + '𝝱' => 'β', + '𝝲' => 'γ', + '𝝳' => 'δ', + '𝝴' => 'ε', + '𝝵' => 'ζ', + '𝝶' => 'η', + '𝝷' => 'θ', + '𝝸' => 'ι', + '𝝹' => 'κ', + '𝝺' => 'λ', + '𝝻' => 'μ', + '𝝼' => 'ν', + '𝝽' => 'ξ', + '𝝾' => 'ο', + '𝝿' => 'π', + '𝞀' => 'ρ', + '𝞁' => 'ς', + '𝞂' => 'σ', + '𝞃' => 'τ', + '𝞄' => 'υ', + '𝞅' => 'φ', + '𝞆' => 'χ', + '𝞇' => 'ψ', + '𝞈' => 'ω', + '𝞉' => '∂', + '𝞊' => 'ε', + '𝞋' => 'θ', + '𝞌' => 'κ', + '𝞍' => 'φ', + '𝞎' => 'ρ', + '𝞏' => 'π', + '𝞐' => 'Α', + '𝞑' => 'Β', + '𝞒' => 'Γ', + '𝞓' => 'Δ', + '𝞔' => 'Ε', + '𝞕' => 'Ζ', + '𝞖' => 'Η', + '𝞗' => 'Θ', + '𝞘' => 'Ι', + '𝞙' => 'Κ', + '𝞚' => 'Λ', + '𝞛' => 'Μ', + '𝞜' => 'Ν', + '𝞝' => 'Ξ', + '𝞞' => 'Ο', + '𝞟' => 'Π', + '𝞠' => 'Ρ', + '𝞡' => 'Θ', + '𝞢' => 'Σ', + '𝞣' => 'Τ', + '𝞤' => 'Υ', + '𝞥' => 'Φ', + '𝞦' => 'Χ', + '𝞧' => 'Ψ', + '𝞨' => 'Ω', + '𝞩' => '∇', + '𝞪' => 'α', + '𝞫' => 'β', + '𝞬' => 'γ', + '𝞭' => 'δ', + '𝞮' => 'ε', + '𝞯' => 'ζ', + '𝞰' => 'η', + '𝞱' => 'θ', + '𝞲' => 'ι', + '𝞳' => 'κ', + '𝞴' => 'λ', + '𝞵' => 'μ', + '𝞶' => 'ν', + '𝞷' => 'ξ', + '𝞸' => 'ο', + '𝞹' => 'π', + '𝞺' => 'ρ', + '𝞻' => 'ς', + '𝞼' => 'σ', + '𝞽' => 'τ', + '𝞾' => 'υ', + '𝞿' => 'φ', + '𝟀' => 'χ', + '𝟁' => 'ψ', + '𝟂' => 'ω', + '𝟃' => '∂', + '𝟄' => 'ε', + '𝟅' => 'θ', + '𝟆' => 'κ', + '𝟇' => 'φ', + '𝟈' => 'ρ', + '𝟉' => 'π', + '𝟊' => 'Ϝ', + '𝟋' => 'ϝ', + '𝟎' => '0', + '𝟏' => '1', + '𝟐' => '2', + '𝟑' => '3', + '𝟒' => '4', + '𝟓' => '5', + '𝟔' => '6', + '𝟕' => '7', + '𝟖' => '8', + '𝟗' => '9', + '𝟘' => '0', + '𝟙' => '1', + '𝟚' => '2', + '𝟛' => '3', + '𝟜' => '4', + '𝟝' => '5', + '𝟞' => '6', + '𝟟' => '7', + '𝟠' => '8', + '𝟡' => '9', + '𝟢' => '0', + '𝟣' => '1', + '𝟤' => '2', + '𝟥' => '3', + '𝟦' => '4', + '𝟧' => '5', + '𝟨' => '6', + '𝟩' => '7', + '𝟪' => '8', + '𝟫' => '9', + '𝟬' => '0', + '𝟭' => '1', + '𝟮' => '2', + '𝟯' => '3', + '𝟰' => '4', + '𝟱' => '5', + '𝟲' => '6', + '𝟳' => '7', + '𝟴' => '8', + '𝟵' => '9', + '𝟶' => '0', + '𝟷' => '1', + '𝟸' => '2', + '𝟹' => '3', + '𝟺' => '4', + '𝟻' => '5', + '𝟼' => '6', + '𝟽' => '7', + '𝟾' => '8', + '𝟿' => '9', + '𞸀' => 'ا', + '𞸁' => 'ب', + '𞸂' => 'ج', + '𞸃' => 'د', + '𞸅' => 'و', + '𞸆' => 'ز', + '𞸇' => 'ح', + '𞸈' => 'ط', + '𞸉' => 'ي', + '𞸊' => 'ك', + '𞸋' => 'ل', + '𞸌' => 'م', + '𞸍' => 'ن', + '𞸎' => 'س', + '𞸏' => 'ع', + '𞸐' => 'ف', + '𞸑' => 'ص', + '𞸒' => 'ق', + '𞸓' => 'ر', + '𞸔' => 'ش', + '𞸕' => 'ت', + '𞸖' => 'ث', + '𞸗' => 'خ', + '𞸘' => 'ذ', + '𞸙' => 'ض', + '𞸚' => 'ظ', + '𞸛' => 'غ', + '𞸜' => 'ٮ', + '𞸝' => 'ں', + '𞸞' => 'ڡ', + '𞸟' => 'ٯ', + '𞸡' => 'ب', + '𞸢' => 'ج', + '𞸤' => 'ه', + '𞸧' => 'ح', + '𞸩' => 'ي', + '𞸪' => 'ك', + '𞸫' => 'ل', + '𞸬' => 'م', + '𞸭' => 'ن', + '𞸮' => 'س', + '𞸯' => 'ع', + '𞸰' => 'ف', + '𞸱' => 'ص', + '𞸲' => 'ق', + '𞸴' => 'ش', + '𞸵' => 'ت', + '𞸶' => 'ث', + '𞸷' => 'خ', + '𞸹' => 'ض', + '𞸻' => 'غ', + '𞹂' => 'ج', + '𞹇' => 'ح', + '𞹉' => 'ي', + '𞹋' => 'ل', + '𞹍' => 'ن', + '𞹎' => 'س', + '𞹏' => 'ع', + '𞹑' => 'ص', + '𞹒' => 'ق', + '𞹔' => 'ش', + '𞹗' => 'خ', + '𞹙' => 'ض', + '𞹛' => 'غ', + '𞹝' => 'ں', + '𞹟' => 'ٯ', + '𞹡' => 'ب', + '𞹢' => 'ج', + '𞹤' => 'ه', + '𞹧' => 'ح', + '𞹨' => 'ط', + '𞹩' => 'ي', + '𞹪' => 'ك', + '𞹬' => 'م', + '𞹭' => 'ن', + '𞹮' => 'س', + '𞹯' => 'ع', + '𞹰' => 'ف', + '𞹱' => 'ص', + '𞹲' => 'ق', + '𞹴' => 'ش', + '𞹵' => 'ت', + '𞹶' => 'ث', + '𞹷' => 'خ', + '𞹹' => 'ض', + '𞹺' => 'ظ', + '𞹻' => 'غ', + '𞹼' => 'ٮ', + '𞹾' => 'ڡ', + '𞺀' => 'ا', + '𞺁' => 'ب', + '𞺂' => 'ج', + '𞺃' => 'د', + '𞺄' => 'ه', + '𞺅' => 'و', + '𞺆' => 'ز', + '𞺇' => 'ح', + '𞺈' => 'ط', + '𞺉' => 'ي', + '𞺋' => 'ل', + '𞺌' => 'م', + '𞺍' => 'ن', + '𞺎' => 'س', + '𞺏' => 'ع', + '𞺐' => 'ف', + '𞺑' => 'ص', + '𞺒' => 'ق', + '𞺓' => 'ر', + '𞺔' => 'ش', + '𞺕' => 'ت', + '𞺖' => 'ث', + '𞺗' => 'خ', + '𞺘' => 'ذ', + '𞺙' => 'ض', + '𞺚' => 'ظ', + '𞺛' => 'غ', + '𞺡' => 'ب', + '𞺢' => 'ج', + '𞺣' => 'د', + '𞺥' => 'و', + '𞺦' => 'ز', + '𞺧' => 'ح', + '𞺨' => 'ط', + '𞺩' => 'ي', + '𞺫' => 'ل', + '𞺬' => 'م', + '𞺭' => 'ن', + '𞺮' => 'س', + '𞺯' => 'ع', + '𞺰' => 'ف', + '𞺱' => 'ص', + '𞺲' => 'ق', + '𞺳' => 'ر', + '𞺴' => 'ش', + '𞺵' => 'ت', + '𞺶' => 'ث', + '𞺷' => 'خ', + '𞺸' => 'ذ', + '𞺹' => 'ض', + '𞺺' => 'ظ', + '𞺻' => 'غ', + '🄀' => '0.', + '🄁' => '0,', + '🄂' => '1,', + '🄃' => '2,', + '🄄' => '3,', + '🄅' => '4,', + '🄆' => '5,', + '🄇' => '6,', + '🄈' => '7,', + '🄉' => '8,', + '🄊' => '9,', + '🄐' => '(A)', + '🄑' => '(B)', + '🄒' => '(C)', + '🄓' => '(D)', + '🄔' => '(E)', + '🄕' => '(F)', + '🄖' => '(G)', + '🄗' => '(H)', + '🄘' => '(I)', + '🄙' => '(J)', + '🄚' => '(K)', + '🄛' => '(L)', + '🄜' => '(M)', + '🄝' => '(N)', + '🄞' => '(O)', + '🄟' => '(P)', + '🄠' => '(Q)', + '🄡' => '(R)', + '🄢' => '(S)', + '🄣' => '(T)', + '🄤' => '(U)', + '🄥' => '(V)', + '🄦' => '(W)', + '🄧' => '(X)', + '🄨' => '(Y)', + '🄩' => '(Z)', + '🄪' => '〔S〕', + '🄫' => 'C', + '🄬' => 'R', + '🄭' => 'CD', + '🄮' => 'WZ', + '🄰' => 'A', + '🄱' => 'B', + '🄲' => 'C', + '🄳' => 'D', + '🄴' => 'E', + '🄵' => 'F', + '🄶' => 'G', + '🄷' => 'H', + '🄸' => 'I', + '🄹' => 'J', + '🄺' => 'K', + '🄻' => 'L', + '🄼' => 'M', + '🄽' => 'N', + '🄾' => 'O', + '🄿' => 'P', + '🅀' => 'Q', + '🅁' => 'R', + '🅂' => 'S', + '🅃' => 'T', + '🅄' => 'U', + '🅅' => 'V', + '🅆' => 'W', + '🅇' => 'X', + '🅈' => 'Y', + '🅉' => 'Z', + '🅊' => 'HV', + '🅋' => 'MV', + '🅌' => 'SD', + '🅍' => 'SS', + '🅎' => 'PPV', + '🅏' => 'WC', + '🅪' => 'MC', + '🅫' => 'MD', + '🅬' => 'MR', + '🆐' => 'DJ', + '🈀' => 'ほか', + '🈁' => 'ココ', + '🈂' => 'サ', + '🈐' => '手', + '🈑' => '字', + '🈒' => '双', + '🈓' => 'デ', + '🈔' => '二', + '🈕' => '多', + '🈖' => '解', + '🈗' => '天', + '🈘' => '交', + '🈙' => '映', + '🈚' => '無', + '🈛' => '料', + '🈜' => '前', + '🈝' => '後', + '🈞' => '再', + '🈟' => '新', + '🈠' => '初', + '🈡' => '終', + '🈢' => '生', + '🈣' => '販', + '🈤' => '声', + '🈥' => '吹', + '🈦' => '演', + '🈧' => '投', + '🈨' => '捕', + '🈩' => '一', + '🈪' => '三', + '🈫' => '遊', + '🈬' => '左', + '🈭' => '中', + '🈮' => '右', + '🈯' => '指', + '🈰' => '走', + '🈱' => '打', + '🈲' => '禁', + '🈳' => '空', + '🈴' => '合', + '🈵' => '満', + '🈶' => '有', + '🈷' => '月', + '🈸' => '申', + '🈹' => '割', + '🈺' => '営', + '🈻' => '配', + '🉀' => '〔本〕', + '🉁' => '〔三〕', + '🉂' => '〔二〕', + '🉃' => '〔安〕', + '🉄' => '〔点〕', + '🉅' => '〔打〕', + '🉆' => '〔盗〕', + '🉇' => '〔勝〕', + '🉈' => '〔敗〕', + '🉐' => '得', + '🉑' => '可', + '🯰' => '0', + '🯱' => '1', + '🯲' => '2', + '🯳' => '3', + '🯴' => '4', + '🯵' => '5', + '🯶' => '6', + '🯷' => '7', + '🯸' => '8', + '🯹' => '9', +); diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/bootstrap.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/bootstrap.php new file mode 100644 index 0000000000..3608e5c05d --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/bootstrap.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Normalizer as p; + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('normalizer_is_normalized')) { + function normalizer_is_normalized($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::isNormalized($string, $form); } +} +if (!function_exists('normalizer_normalize')) { + function normalizer_normalize($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::normalize($string, $form); } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/bootstrap80.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/bootstrap80.php new file mode 100644 index 0000000000..e36d1a9477 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/bootstrap80.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Normalizer as p; + +if (!function_exists('normalizer_is_normalized')) { + function normalizer_is_normalized(?string $string, ?int $form = p\Normalizer::FORM_C): bool { return p\Normalizer::isNormalized((string) $string, (int) $form); } +} +if (!function_exists('normalizer_normalize')) { + function normalizer_normalize(?string $string, ?int $form = p\Normalizer::FORM_C): string|false { return p\Normalizer::normalize((string) $string, (int) $form); } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/composer.json b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/composer.json new file mode 100644 index 0000000000..0c325b0bb8 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-intl-normalizer/composer.json @@ -0,0 +1,49 @@ +{ + "name": "symfony/polyfill-intl-normalizer", + "type": "library", + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "keywords": [ + "polyfill", + "shim", + "compatibility", + "portable", + "intl", + "normalizer" + ], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2" + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "suggest": { + "ext-intl": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/LICENSE b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/LICENSE new file mode 100644 index 0000000000..0ed3a24655 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Php80.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Php80.php new file mode 100644 index 0000000000..362dd1a959 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Php80.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Ion Bazan + * @author Nico Oelgart + * @author Nicolas Grekas + * + * @internal + */ +final class Php80 +{ + public static function fdiv(float $dividend, float $divisor): float + { + return @($dividend / $divisor); + } + + public static function get_debug_type($value): string + { + switch (true) { + case null === $value: return 'null'; + case \is_bool($value): return 'bool'; + case \is_string($value): return 'string'; + case \is_array($value): return 'array'; + case \is_int($value): return 'int'; + case \is_float($value): return 'float'; + case \is_object($value): break; + case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; + default: + if (null === $type = @get_resource_type($value)) { + return 'unknown'; + } + + if ('Unknown' === $type) { + $type = 'closed'; + } + + return "resource ($type)"; + } + + $class = \get_class($value); + + if (false === strpos($class, '@')) { + return $class; + } + + return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; + } + + public static function get_resource_id($res): int + { + if (!\is_resource($res) && null === @get_resource_type($res)) { + throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); + } + + return (int) $res; + } + + public static function preg_last_error_msg(): string + { + switch (preg_last_error()) { + case \PREG_INTERNAL_ERROR: + return 'Internal error'; + case \PREG_BAD_UTF8_ERROR: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + case \PREG_BAD_UTF8_OFFSET_ERROR: + return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; + case \PREG_BACKTRACK_LIMIT_ERROR: + return 'Backtrack limit exhausted'; + case \PREG_RECURSION_LIMIT_ERROR: + return 'Recursion limit exhausted'; + case \PREG_JIT_STACKLIMIT_ERROR: + return 'JIT stack limit exhausted'; + case \PREG_NO_ERROR: + return 'No error'; + default: + return 'Unknown error'; + } + } + + public static function str_contains(string $haystack, string $needle): bool + { + return '' === $needle || false !== strpos($haystack, $needle); + } + + public static function str_starts_with(string $haystack, string $needle): bool + { + return 0 === strncmp($haystack, $needle, \strlen($needle)); + } + + public static function str_ends_with(string $haystack, string $needle): bool + { + if ('' === $needle || $needle === $haystack) { + return true; + } + + if ('' === $haystack) { + return false; + } + + $needleLength = \strlen($needle); + + return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/PhpToken.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/PhpToken.php new file mode 100644 index 0000000000..fe6e691056 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/PhpToken.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Fedonyuk Anton + * + * @internal + */ +class PhpToken implements \Stringable +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $text; + + /** + * @var int + */ + public $line; + + /** + * @var int + */ + public $pos; + + public function __construct(int $id, string $text, int $line = -1, int $position = -1) + { + $this->id = $id; + $this->text = $text; + $this->line = $line; + $this->pos = $position; + } + + public function getTokenName(): ?string + { + if ('UNKNOWN' === $name = token_name($this->id)) { + $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; + } + + return $name; + } + + /** + * @param int|string|array $kind + */ + public function is($kind): bool + { + foreach ((array) $kind as $value) { + if (\in_array($value, [$this->id, $this->text], true)) { + return true; + } + } + + return false; + } + + public function isIgnorable(): bool + { + return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true); + } + + public function __toString(): string + { + return (string) $this->text; + } + + /** + * @return static[] + */ + public static function tokenize(string $code, int $flags = 0): array + { + $line = 1; + $position = 0; + $tokens = token_get_all($code, $flags); + foreach ($tokens as $index => $token) { + if (\is_string($token)) { + $id = \ord($token); + $text = $token; + } else { + [$id, $text, $line] = $token; + } + $tokens[$index] = new static($id, $text, $line, $position); + $position += \strlen($text); + } + + return $tokens; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/README.md b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/README.md new file mode 100644 index 0000000000..09450984ee --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/README.md @@ -0,0 +1,23 @@ +# Symfony Polyfill / Php80 + +This component provides features added to PHP 8.0 core: + +- [`Stringable`](https://php.net/stringable) interface +- [`fdiv`](https://php.net/fdiv) +- [`ValueError`](https://php.net/valueerror) class +- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class +- `FILTER_VALIDATE_BOOL` constant +- [`get_debug_type`](https://php.net/get_debug_type) +- [`PhpToken`](https://php.net/phptoken) class +- [`preg_last_error_msg`](https://php.net/preg_last_error_msg) +- [`str_contains`](https://php.net/str_contains) +- [`str_starts_with`](https://php.net/str_starts_with) +- [`str_ends_with`](https://php.net/str_ends_with) +- [`get_resource_id`](https://php.net/get_resource_id) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +# License + +This library is released under the [MIT license](LICENSE). diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/Attribute.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/Attribute.php new file mode 100644 index 0000000000..2b955423fc --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/Attribute.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#[Attribute(Attribute::TARGET_CLASS)] +final class Attribute +{ + public const TARGET_CLASS = 1; + public const TARGET_FUNCTION = 2; + public const TARGET_METHOD = 4; + public const TARGET_PROPERTY = 8; + public const TARGET_CLASS_CONSTANT = 16; + public const TARGET_PARAMETER = 32; + public const TARGET_ALL = 63; + public const IS_REPEATABLE = 64; + + /** @var int */ + public $flags; + + public function __construct(int $flags = self::TARGET_ALL) + { + $this->flags = $flags; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/PhpToken.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/PhpToken.php new file mode 100644 index 0000000000..bd1212f6e4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/PhpToken.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) { + class PhpToken extends Symfony\Polyfill\Php80\PhpToken + { + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/Stringable.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/Stringable.php new file mode 100644 index 0000000000..7c62d7508b --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/Stringable.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + interface Stringable + { + /** + * @return string + */ + public function __toString(); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php new file mode 100644 index 0000000000..01c6c6c8ab --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class UnhandledMatchError extends Error + { + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/ValueError.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/ValueError.php new file mode 100644 index 0000000000..783dbc28c7 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/Resources/stubs/ValueError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class ValueError extends Error + { + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/bootstrap.php b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/bootstrap.php new file mode 100644 index 0000000000..e5f7dbc1a4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/bootstrap.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php80 as p; + +if (\PHP_VERSION_ID >= 80000) { + return; +} + +if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { + define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); +} + +if (!function_exists('fdiv')) { + function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } +} +if (!function_exists('preg_last_error_msg')) { + function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } +} +if (!function_exists('str_contains')) { + function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_starts_with')) { + function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_ends_with')) { + function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('get_debug_type')) { + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } +} +if (!function_exists('get_resource_id')) { + function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/composer.json b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/composer.json new file mode 100644 index 0000000000..b14a1c9c2f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/polyfill-php80/composer.json @@ -0,0 +1,48 @@ +{ + "name": "symfony/polyfill-php80", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "keywords": [ + "polyfill", + "shim", + "compatibility", + "portable" + ], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2" + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "minimum-stability": "dev", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/CHANGELOG.md b/packages/playground/data-liberation/vendor-patched/symfony/yaml/CHANGELOG.md new file mode 100644 index 0000000000..33d1c5b67f --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/CHANGELOG.md @@ -0,0 +1,242 @@ +# CHANGELOG + +## 7.1 + +- Add support for getting all the enum cases with `!php/enum Foo` + +## 7.0 + +- Remove the `!php/const:` tag, use `!php/const` instead (without the colon) + +## 6.3 + +- Add support to dump int keys as strings by using the `Yaml::DUMP_NUMERIC_KEY_AS_STRING` flag + +## 6.2 + +- Add support for `!php/enum` and `!php/enum *->value` +- Deprecate the `!php/const:` tag in key which will be replaced by the `!php/const` tag (without the colon) since 3.4 + +## 6.1 + +- In cases where it will likely improve readability, strings containing single quotes will be double-quoted + +## 5.4 + +- Add new `lint:yaml dirname --exclude=/dirname/foo.yaml --exclude=/dirname/bar.yaml` + option to exclude one or more specific files from multiple file list +- Allow negatable for the parse tags option with `--no-parse-tags` + +## 5.3 + +- Added `github` format support & autodetection to render errors as annotations + when running the YAML linter command in a Github Action environment. + +## 5.1.0 + +- Added support for parsing numbers prefixed with `0o` as octal numbers. +- Deprecated support for parsing numbers starting with `0` as octal numbers. They will be parsed as strings as of Symfony 6.0. Prefix numbers with `0o` + so that they are parsed as octal numbers. + + Before: + + ```yaml + Yaml::parse('072'); + ``` + + After: + + ```yaml + Yaml::parse('0o72'); + ``` + +- Added `yaml-lint` binary. +- Deprecated using the `!php/object` and `!php/const` tags without a value. + +## 5.0.0 + +- Removed support for mappings inside multi-line strings. +- removed support for implicit STDIN usage in the `lint:yaml` command, use `lint:yaml -` (append a dash) instead to make it explicit. + +## 4.4.0 + +- Added support for parsing the inline notation spanning multiple lines. +- Added support to dump `null` as `~` by using the `Yaml::DUMP_NULL_AS_TILDE` flag. +- deprecated accepting STDIN implicitly when using the `lint:yaml` command, use `lint:yaml -` (append a dash) instead to make it explicit. + +## 4.3.0 + +- Using a mapping inside a multi-line string is deprecated and will throw a `ParseException` in 5.0. + +## 4.2.0 + +- added support for multiple files or directories in `LintCommand` + +## 4.0.0 + +- The behavior of the non-specific tag `!` is changed and now forces + non-evaluating your values. +- complex mappings will throw a `ParseException` +- support for the comma as a group separator for floats has been dropped, use + the underscore instead +- support for the `!!php/object` tag has been dropped, use the `!php/object` + tag instead +- duplicate mapping keys throw a `ParseException` +- non-string mapping keys throw a `ParseException`, use the `Yaml::PARSE_KEYS_AS_STRINGS` + flag to cast them to strings +- `%` at the beginning of an unquoted string throw a `ParseException` +- mappings with a colon (`:`) that is not followed by a whitespace throw a + `ParseException` +- the `Dumper::setIndentation()` method has been removed +- being able to pass boolean options to the `Yaml::parse()`, `Yaml::dump()`, + `Parser::parse()`, and `Dumper::dump()` methods to configure the behavior of + the parser and dumper is no longer supported, pass bitmask flags instead +- the constructor arguments of the `Parser` class have been removed +- the `Inline` class is internal and no longer part of the BC promise +- removed support for the `!str` tag, use the `!!str` tag instead +- added support for tagged scalars. + + ```yml + Yaml::parse('!foo bar', Yaml::PARSE_CUSTOM_TAGS); + // returns TaggedValue('foo', 'bar'); + ``` + +## 3.4.0 + +- added support for parsing YAML files using the `Yaml::parseFile()` or `Parser::parseFile()` method + +- the `Dumper`, `Parser`, and `Yaml` classes are marked as final + +- Deprecated the `!php/object:` tag which will be replaced by the + `!php/object` tag (without the colon) in 4.0. + +- Deprecated the `!php/const:` tag which will be replaced by the + `!php/const` tag (without the colon) in 4.0. + +- Support for the `!str` tag is deprecated, use the `!!str` tag instead. + +- Deprecated using the non-specific tag `!` as its behavior will change in 4.0. + It will force non-evaluating your values in 4.0. Use plain integers or `!!float` instead. + +## 3.3.0 + +- Starting an unquoted string with a question mark followed by a space is + deprecated and will throw a `ParseException` in Symfony 4.0. + +- Deprecated support for implicitly parsing non-string mapping keys as strings. + Mapping keys that are no strings will lead to a `ParseException` in Symfony + 4.0. Use quotes to opt-in for keys to be parsed as strings. + + Before: + + ```php + $yaml = << new A(), 'bar' => 1], 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE | Yaml::DUMP_OBJECT); + ``` + +## 3.0.0 + +- Yaml::parse() now throws an exception when a blackslash is not escaped + in double-quoted strings + +## 2.8.0 + +- Deprecated usage of a colon in an unquoted mapping value +- Deprecated usage of @, \`, | and > at the beginning of an unquoted string +- When surrounding strings with double-quotes, you must now escape `\` characters. Not + escaping those characters (when surrounded by double-quotes) is deprecated. + + Before: + + ```yml + class: "Foo\Var" + ``` + + After: + + ```yml + class: "Foo\\Var" + ``` + +## 2.1.0 + +- Yaml::parse() does not evaluate loaded files as PHP files by default + anymore (call Yaml::enablePhpParsing() to get back the old behavior) diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Command/LintCommand.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Command/LintCommand.php new file mode 100644 index 0000000000..75c09f51ed --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Command/LintCommand.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Command; + +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\CI\GithubActionReporter; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser; +use Symfony\Component\Yaml\Yaml; + +/** + * Validates YAML files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + */ +#[AsCommand(name: 'lint:yaml', description: 'Lint a YAML file and outputs encountered errors')] +class LintCommand extends Command +{ + private Parser $parser; + private ?string $format = null; + private bool $displayCorrectFiles; + private ?\Closure $directoryIteratorProvider; + private ?\Closure $isReadableProvider; + + public function __construct(?string $name = null, ?callable $directoryIteratorProvider = null, ?callable $isReadableProvider = null) + { + parent::__construct($name); + + $this->directoryIteratorProvider = null === $directoryIteratorProvider ? null : $directoryIteratorProvider(...); + $this->isReadableProvider = null === $isReadableProvider ? null : $isReadableProvider(...); + } + + protected function configure(): void + { + $this + ->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN') + ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions()))) + ->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude') + ->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null) + ->setHelp(<<%command.name% command lints a YAML file and outputs to STDOUT +the first encountered syntax error. + +You can validates YAML contents passed from STDIN: + + cat filename | php %command.full_name% - + +You can also validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +You can also exclude one or more specific files: + + php %command.full_name% dirname --exclude="dirname/foo.yaml" --exclude="dirname/bar.yaml" + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $filenames = (array) $input->getArgument('filename'); + $excludes = $input->getOption('exclude'); + $this->format = $input->getOption('format'); + $flags = $input->getOption('parse-tags'); + + if (null === $this->format) { + // Autodetect format according to CI environment + $this->format = class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt'; + } + + $flags = $flags ? Yaml::PARSE_CUSTOM_TAGS : 0; + + $this->displayCorrectFiles = $output->isVerbose(); + + if (['-'] === $filenames) { + return $this->display($io, [$this->validate(file_get_contents('php://stdin'), $flags)]); + } + + if (!$filenames) { + throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); + } + + $filesInfo = []; + foreach ($filenames as $filename) { + if (!$this->isReadable($filename)) { + throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); + } + + foreach ($this->getFiles($filename) as $file) { + if (!\in_array($file->getPathname(), $excludes, true)) { + $filesInfo[] = $this->validate(file_get_contents($file), $flags, $file); + } + } + } + + return $this->display($io, $filesInfo); + } + + private function validate(string $content, int $flags, ?string $file = null): array + { + $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) { + if (\E_USER_DEPRECATED === $level) { + throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1); + } + + return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; + }); + + try { + $this->getParser()->parse($content, Yaml::PARSE_CONSTANT | $flags); + } catch (ParseException $e) { + return ['file' => $file, 'line' => $e->getParsedLine(), 'valid' => false, 'message' => $e->getMessage()]; + } finally { + restore_error_handler(); + } + + return ['file' => $file, 'valid' => true]; + } + + private function display(SymfonyStyle $io, array $files): int + { + return match ($this->format) { + 'txt' => $this->displayTxt($io, $files), + 'json' => $this->displayJson($io, $files), + 'github' => $this->displayTxt($io, $files, true), + default => throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))), + }; + } + + private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false): int + { + $countFiles = \count($filesInfo); + $erroredFiles = 0; + $suggestTagOption = false; + + if ($errorAsGithubAnnotations) { + $githubReporter = new GithubActionReporter($io); + } + + foreach ($filesInfo as $info) { + if ($info['valid'] && $this->displayCorrectFiles) { + $io->comment('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + ++$erroredFiles; + $io->text(' ERROR '.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + $io->text(sprintf(' >> %s', $info['message'])); + + if (str_contains($info['message'], 'PARSE_CUSTOM_TAGS')) { + $suggestTagOption = true; + } + + if ($errorAsGithubAnnotations) { + $githubReporter->error($info['message'], $info['file'] ?? 'php://stdin', $info['line']); + } + } + } + + if (0 === $erroredFiles) { + $io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles)); + } else { + $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.%s', $countFiles - $erroredFiles, $erroredFiles, $suggestTagOption ? ' Use the --parse-tags option if you want parse custom tags.' : '')); + } + + return min($erroredFiles, 1); + } + + private function displayJson(SymfonyStyle $io, array $filesInfo): int + { + $errors = 0; + + array_walk($filesInfo, function (&$v) use (&$errors) { + $v['file'] = (string) $v['file']; + if (!$v['valid']) { + ++$errors; + } + + if (isset($v['message']) && str_contains($v['message'], 'PARSE_CUSTOM_TAGS')) { + $v['message'] .= ' Use the --parse-tags option if you want parse custom tags.'; + } + }); + + $io->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); + + return min($errors, 1); + } + + private function getFiles(string $fileOrDirectory): iterable + { + if (is_file($fileOrDirectory)) { + yield new \SplFileInfo($fileOrDirectory); + + return; + } + + foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) { + if (!\in_array($file->getExtension(), ['yml', 'yaml'])) { + continue; + } + + yield $file; + } + } + + private function getParser(): Parser + { + return $this->parser ??= new Parser(); + } + + private function getDirectoryIterator(string $directory): iterable + { + $default = fn ($directory) => new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + if (null !== $this->directoryIteratorProvider) { + return ($this->directoryIteratorProvider)($directory, $default); + } + + return $default($directory); + } + + private function isReadable(string $fileOrDirectory): bool + { + $default = is_readable(...); + + if (null !== $this->isReadableProvider) { + return ($this->isReadableProvider)($fileOrDirectory, $default); + } + + return $default($fileOrDirectory); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues($this->getAvailableFormatOptions()); + } + } + + private function getAvailableFormatOptions(): array + { + return ['txt', 'json', 'github']; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Dumper.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Dumper.php new file mode 100644 index 0000000000..4292c368c1 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Dumper.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Tag\TaggedValue; + +/** + * Dumper dumps PHP variables to YAML strings. + * + * @author Fabien Potencier + * + * @final + */ +class Dumper +{ + /** + * The amount of spaces to use for indentation of nested nodes. + */ + private int $indentation; + + public function __construct(int $indentation = 4) + { + if ($indentation < 1) { + throw new \InvalidArgumentException('The indentation must be greater than zero.'); + } + + $this->indentation = $indentation; + } + + /** + * Dumps a PHP value to YAML. + * + * @param mixed $input The PHP value + * @param int $inline The level where you switch to inline YAML + * @param int $indent The level of indentation (used internally) + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + */ + public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string + { + $output = ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; + $dumpObjectAsInlineMap = true; + + if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) { + $dumpObjectAsInlineMap = !(array) $input; + } + + if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || !$input) { + $output .= $prefix.Inline::dump($input, $flags); + } elseif ($input instanceof TaggedValue) { + $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix); + } else { + $dumpAsMap = Inline::isHash($input); + + foreach ($input as $key => $value) { + if ('' !== $output && "\n" !== $output[-1]) { + $output .= "\n"; + } + + if (\is_int($key) && Yaml::DUMP_NUMERIC_KEY_AS_STRING & $flags) { + $key = (string) $key; + } + + if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && str_contains($value, "\n") && !str_contains($value, "\r")) { + $blockIndentationIndicator = $this->getBlockIndentationIndicator($value); + + if (isset($value[-2]) && "\n" === $value[-2] && "\n" === $value[-1]) { + $blockChompingIndicator = '+'; + } elseif ("\n" === $value[-1]) { + $blockChompingIndicator = ''; + } else { + $blockChompingIndicator = '-'; + } + + $output .= sprintf('%s%s%s |%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator, $blockChompingIndicator); + + foreach (explode("\n", $value) as $row) { + if ('' === $row) { + $output .= "\n"; + } else { + $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); + } + } + + continue; + } + + if ($value instanceof TaggedValue) { + $output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag()); + + if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && str_contains($value->getValue(), "\n") && !str_contains($value->getValue(), "\r\n")) { + $blockIndentationIndicator = $this->getBlockIndentationIndicator($value->getValue()); + $output .= sprintf(' |%s', $blockIndentationIndicator); + + foreach (explode("\n", $value->getValue()) as $row) { + $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); + } + + continue; + } + + if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { + $output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; + } else { + $output .= "\n"; + $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags); + } + + continue; + } + + $dumpObjectAsInlineMap = true; + + if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) { + $dumpObjectAsInlineMap = !(array) $value; + } + + $willBeInlined = $inline - 1 <= 0 || !\is_array($value) && $dumpObjectAsInlineMap || !$value; + + $output .= sprintf('%s%s%s%s', + $prefix, + $dumpAsMap ? Inline::dump($key, $flags).':' : '-', + $willBeInlined ? ' ' : "\n", + $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags) + ).($willBeInlined ? "\n" : ''); + } + } + + return $output; + } + + private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string + { + $output = sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag()); + + if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && str_contains($value->getValue(), "\n") && !str_contains($value->getValue(), "\r\n")) { + $blockIndentationIndicator = $this->getBlockIndentationIndicator($value->getValue()); + $output .= sprintf(' |%s', $blockIndentationIndicator); + + foreach (explode("\n", $value->getValue()) as $row) { + $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); + } + + return $output; + } + + if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { + return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; + } + + return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags); + } + + private function getBlockIndentationIndicator(string $value): string + { + $lines = explode("\n", $value); + + // If the first line (that is neither empty nor contains only spaces) + // starts with a space character, the spec requires a block indentation indicator + // http://www.yaml.org/spec/1.2/spec.html#id2793979 + foreach ($lines as $line) { + if ('' !== trim($line, ' ')) { + return str_starts_with($line, ' ') ? (string) $this->indentation : ''; + } + } + + return ''; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Escaper.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Escaper.php new file mode 100644 index 0000000000..044f1a3b1d --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Escaper.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Escaper encapsulates escaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + * + * @internal + */ +class Escaper +{ + // Characters that would cause a dumped string to require double quoting. + public const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\x7f|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; + + // Mapping arrays for escaping a double quoted string. The backslash is + // first to ensure proper escaping because str_replace operates iteratively + // on the input arrays. This ordering of the characters avoids the use of strtr, + // which performs more slowly. + private const ESCAPEES = ['\\', '\\\\', '\\"', '"', + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", + "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", + "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", + "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", + "\x7f", + "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9", + ]; + private const ESCAPED = ['\\\\', '\\"', '\\\\', '\\"', + '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', + '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', + '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', + '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', + '\\x7f', + '\\N', '\\_', '\\L', '\\P', + ]; + + /** + * Determines if a PHP value would require double quoting in YAML. + * + * @param string $value A PHP value + */ + public static function requiresDoubleQuoting(string $value): bool + { + return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); + } + + /** + * Escapes and surrounds a PHP value with double quotes. + * + * @param string $value A PHP value + */ + public static function escapeWithDoubleQuotes(string $value): string + { + return sprintf('"%s"', str_replace(self::ESCAPEES, self::ESCAPED, $value)); + } + + /** + * Determines if a PHP value would require single quoting in YAML. + * + * @param string $value A PHP value + */ + public static function requiresSingleQuoting(string $value): bool + { + // Determines if a PHP value is entirely composed of a value that would + // require single quoting in YAML. + if (\in_array(strtolower($value), ['null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'])) { + return true; + } + + // Determines if the PHP value contains any single characters that would + // cause it to require single quoting in YAML. + return 0 < preg_match('/[\s\'"\:\{\}\[\],&\*\#\?] | \A[\-?|<>=!%@`\p{Zs}]/xu', $value); + } + + /** + * Escapes and surrounds a PHP value with single quotes. + * + * @param string $value A PHP value + */ + public static function escapeWithSingleQuotes(string $value): string + { + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/DumpException.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/DumpException.php new file mode 100644 index 0000000000..cce972f246 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/DumpException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during dumping. + * + * @author Fabien Potencier + */ +class DumpException extends RuntimeException +{ +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/ExceptionInterface.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..909131684c --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Fabien Potencier + */ +interface ExceptionInterface extends \Throwable +{ +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/ParseException.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/ParseException.php new file mode 100644 index 0000000000..73c067b3a4 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/ParseException.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Fabien Potencier + */ +class ParseException extends RuntimeException +{ + /** + * @param string $rawMessage The error message + * @param int $parsedLine The line where the error occurred + * @param string|null $snippet The snippet of code near the problem + * @param string|null $parsedFile The file name where the error occurred + */ + public function __construct( + private string $rawMessage, + private int $parsedLine = -1, + private ?string $snippet = null, + private ?string $parsedFile = null, + ?\Throwable $previous = null, + ) { + $this->updateRepr(); + + parent::__construct($this->message, 0, $previous); + } + + /** + * Gets the snippet of code near the error. + */ + public function getSnippet(): string + { + return $this->snippet; + } + + /** + * Sets the snippet of code near the error. + */ + public function setSnippet(string $snippet): void + { + $this->snippet = $snippet; + + $this->updateRepr(); + } + + /** + * Gets the filename where the error occurred. + * + * This method returns null if a string is parsed. + */ + public function getParsedFile(): string + { + return $this->parsedFile; + } + + /** + * Sets the filename where the error occurred. + */ + public function setParsedFile(string $parsedFile): void + { + $this->parsedFile = $parsedFile; + + $this->updateRepr(); + } + + /** + * Gets the line where the error occurred. + */ + public function getParsedLine(): int + { + return $this->parsedLine; + } + + /** + * Sets the line where the error occurred. + */ + public function setParsedLine(int $parsedLine): void + { + $this->parsedLine = $parsedLine; + + $this->updateRepr(); + } + + private function updateRepr(): void + { + $this->message = $this->rawMessage; + + $dot = false; + if (str_ends_with($this->message, '.')) { + $this->message = substr($this->message, 0, -1); + $dot = true; + } + + if (null !== $this->parsedFile) { + $this->message .= sprintf(' in %s', json_encode($this->parsedFile, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE)); + } + + if ($this->parsedLine >= 0) { + $this->message .= sprintf(' at line %d', $this->parsedLine); + } + + if ($this->snippet) { + $this->message .= sprintf(' (near "%s")', $this->snippet); + } + + if ($dot) { + $this->message .= '.'; + } + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/RuntimeException.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/RuntimeException.php new file mode 100644 index 0000000000..3f36b73bec --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Romain Neutron + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Inline.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Inline.php new file mode 100644 index 0000000000..9d6c3c5c09 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Inline.php @@ -0,0 +1,848 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\DumpException; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Tag\TaggedValue; + +/** + * Inline implements a YAML parser/dumper for the YAML inline syntax. + * + * @author Fabien Potencier + * + * @internal + */ +class Inline +{ + public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; + + public static int $parsedLineNumber = -1; + public static ?string $parsedFilename = null; + + private static bool $exceptionOnInvalidType = false; + private static bool $objectSupport = false; + private static bool $objectForMap = false; + private static bool $constantSupport = false; + + public static function initialize(int $flags, ?int $parsedLineNumber = null, ?string $parsedFilename = null): void + { + self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); + self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags); + self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags); + self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags); + self::$parsedFilename = $parsedFilename; + + if (null !== $parsedLineNumber) { + self::$parsedLineNumber = $parsedLineNumber; + } + } + + /** + * Converts a YAML string to a PHP value. + * + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior + * @param array $references Mapping of variable names to values + * + * @throws ParseException + */ + public static function parse(string $value, int $flags = 0, array &$references = []): mixed + { + self::initialize($flags); + + $value = trim($value); + + if ('' === $value) { + return ''; + } + + $i = 0; + $tag = self::parseTag($value, $i, $flags); + switch ($value[$i]) { + case '[': + $result = self::parseSequence($value, $flags, $i, $references); + ++$i; + break; + case '{': + $result = self::parseMapping($value, $flags, $i, $references); + ++$i; + break; + default: + $result = self::parseScalar($value, $flags, null, $i, true, $references); + } + + // some comments are allowed at the end + if (preg_replace('/\s*#.*$/A', '', substr($value, $i))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + + if (null !== $tag && '' !== $tag) { + return new TaggedValue($tag, $result); + } + + return $result; + } + + /** + * Dumps a given PHP variable to a YAML string. + * + * @param mixed $value The PHP variable to convert + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + * + * @throws DumpException When trying to dump PHP resource + */ + public static function dump(mixed $value, int $flags = 0): string + { + switch (true) { + case \is_resource($value): + if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { + throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); + } + + return self::dumpNull($flags); + case $value instanceof \DateTimeInterface: + return $value->format(match (true) { + !$length = \strlen(rtrim($value->format('u'), '0')) => 'c', + $length < 4 => 'Y-m-d\TH:i:s.vP', + default => 'Y-m-d\TH:i:s.uP', + }); + case $value instanceof \UnitEnum: + return sprintf('!php/enum %s::%s', $value::class, $value->name); + case \is_object($value): + if ($value instanceof TaggedValue) { + return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags); + } + + if (Yaml::DUMP_OBJECT & $flags) { + return '!php/object '.self::dump(serialize($value)); + } + + if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { + return self::dumpHashArray($value, $flags); + } + + if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { + throw new DumpException('Object support when dumping a YAML file has been disabled.'); + } + + return self::dumpNull($flags); + case \is_array($value): + return self::dumpArray($value, $flags); + case null === $value: + return self::dumpNull($flags); + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case \is_int($value): + return $value; + case is_numeric($value) && false === strpbrk($value, "\f\n\r\t\v"): + $locale = setlocale(\LC_NUMERIC, 0); + if (false !== $locale) { + setlocale(\LC_NUMERIC, 'C'); + } + if (\is_float($value)) { + $repr = (string) $value; + if (is_infinite($value)) { + $repr = str_ireplace('INF', '.Inf', $repr); + } elseif (floor($value) == $value && $repr == $value) { + // Preserve float data type since storing a whole number will result in integer value. + if (!str_contains($repr, 'E')) { + $repr .= '.0'; + } + } + } else { + $repr = \is_string($value) ? "'$value'" : (string) $value; + } + if (false !== $locale) { + setlocale(\LC_NUMERIC, $locale); + } + + return $repr; + case '' == $value: + return "''"; + case self::isBinaryString($value): + return '!!binary '.base64_encode($value); + case Escaper::requiresDoubleQuoting($value): + return Escaper::escapeWithDoubleQuotes($value); + case Escaper::requiresSingleQuoting($value): + $singleQuoted = Escaper::escapeWithSingleQuotes($value); + if (!str_contains($value, "'")) { + return $singleQuoted; + } + // Attempt double-quoting the string instead to see if it's more efficient. + $doubleQuoted = Escaper::escapeWithDoubleQuotes($value); + + return \strlen($doubleQuoted) < \strlen($singleQuoted) ? $doubleQuoted : $singleQuoted; + case Parser::preg_match('{^[0-9]+[_0-9]*$}', $value): + case Parser::preg_match(self::getHexRegex(), $value): + case Parser::preg_match(self::getTimestampRegex(), $value): + return Escaper::escapeWithSingleQuotes($value); + default: + return $value; + } + } + + /** + * Check if given array is hash or just normal indexed array. + */ + public static function isHash(array|\ArrayObject|\stdClass $value): bool + { + if ($value instanceof \stdClass || $value instanceof \ArrayObject) { + return true; + } + + $expectedKey = 0; + + foreach ($value as $key => $val) { + if ($key !== $expectedKey++) { + return true; + } + } + + return false; + } + + /** + * Dumps a PHP array to a YAML string. + * + * @param array $value The PHP array to dump + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + */ + private static function dumpArray(array $value, int $flags): string + { + // array + if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) { + $output = []; + foreach ($value as $val) { + $output[] = self::dump($val, $flags); + } + + return sprintf('[%s]', implode(', ', $output)); + } + + return self::dumpHashArray($value, $flags); + } + + /** + * Dumps hash array to a YAML string. + * + * @param array|\ArrayObject|\stdClass $value The hash array to dump + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + */ + private static function dumpHashArray(array|\ArrayObject|\stdClass $value, int $flags): string + { + $output = []; + foreach ($value as $key => $val) { + if (\is_int($key) && Yaml::DUMP_NUMERIC_KEY_AS_STRING & $flags) { + $key = (string) $key; + } + + $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); + } + + return sprintf('{ %s }', implode(', ', $output)); + } + + private static function dumpNull(int $flags): string + { + if (Yaml::DUMP_NULL_AS_TILDE & $flags) { + return '~'; + } + + return 'null'; + } + + /** + * Parses a YAML scalar. + * + * @throws ParseException When malformed inline YAML string is parsed + */ + public static function parseScalar(string $scalar, int $flags = 0, ?array $delimiters = null, int &$i = 0, bool $evaluate = true, array &$references = [], ?bool &$isQuoted = null): mixed + { + if (\in_array($scalar[$i], ['"', "'"], true)) { + // quoted scalar + $isQuoted = true; + $output = self::parseQuotedScalar($scalar, $i); + + if (null !== $delimiters) { + $tmp = ltrim(substr($scalar, $i), " \n"); + if ('' === $tmp) { + throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + if (!\in_array($tmp[0], $delimiters)) { + throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + } + } else { + // "normal" string + $isQuoted = false; + + if (!$delimiters) { + $output = substr($scalar, $i); + $i += \strlen($output); + + // remove comments + if (Parser::preg_match('/[ \t]+#/', $output, $match, \PREG_OFFSET_CAPTURE)) { + $output = substr($output, 0, $match[0][1]); + } + } elseif (Parser::preg_match('/^(.*?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { + $output = $match[1]; + $i += \strlen($output); + $output = trim($output); + } else { + throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $scalar), self::$parsedLineNumber + 1, null, self::$parsedFilename); + } + + // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) + if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0] || '%' === $output[0])) { + throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber + 1, $output, self::$parsedFilename); + } + + if ($evaluate) { + $output = self::evaluateScalar($output, $flags, $references, $isQuoted); + } + } + + return $output; + } + + /** + * Parses a YAML quoted scalar. + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseQuotedScalar(string $scalar, int &$i = 0): string + { + if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { + throw new ParseException(sprintf('Malformed inline YAML string: "%s".', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + $output = substr($match[0], 1, -1); + + $unescaper = new Unescaper(); + if ('"' == $scalar[$i]) { + $output = $unescaper->unescapeDoubleQuotedString($output); + } else { + $output = $unescaper->unescapeSingleQuotedString($output); + } + + $i += \strlen($match[0]); + + return $output; + } + + /** + * Parses a YAML sequence. + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseSequence(string $sequence, int $flags, int &$i = 0, array &$references = []): array + { + $output = []; + $len = \strlen($sequence); + ++$i; + + // [foo, bar, ...] + $lastToken = null; + while ($i < $len) { + if (']' === $sequence[$i]) { + return $output; + } + if (',' === $sequence[$i] || ' ' === $sequence[$i]) { + if (',' === $sequence[$i] && (null === $lastToken || 'separator' === $lastToken)) { + $output[] = null; + } elseif (',' === $sequence[$i]) { + $lastToken = 'separator'; + } + + ++$i; + + continue; + } + + $tag = self::parseTag($sequence, $i, $flags); + switch ($sequence[$i]) { + case '[': + // nested sequence + $value = self::parseSequence($sequence, $flags, $i, $references); + break; + case '{': + // nested mapping + $value = self::parseMapping($sequence, $flags, $i, $references); + break; + default: + $value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references, $isQuoted); + + // the value can be an array if a reference has been resolved to an array var + if (\is_string($value) && !$isQuoted && str_contains($value, ': ')) { + // embedded mapping? + try { + $pos = 0; + $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references); + } catch (\InvalidArgumentException) { + // no, it's not + } + } + + if (!$isQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) { + $references[$matches['ref']] = $matches['value']; + $value = $matches['value']; + } + + --$i; + } + + if (null !== $tag && '' !== $tag) { + $value = new TaggedValue($tag, $value); + } + + $output[] = $value; + + $lastToken = 'value'; + ++$i; + } + + throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $sequence), self::$parsedLineNumber + 1, null, self::$parsedFilename); + } + + /** + * Parses a YAML mapping. + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseMapping(string $mapping, int $flags, int &$i = 0, array &$references = []): array|\stdClass + { + $output = []; + $len = \strlen($mapping); + ++$i; + $allowOverwrite = false; + + // {foo: bar, bar:foo, ...} + while ($i < $len) { + switch ($mapping[$i]) { + case ' ': + case ',': + case "\n": + ++$i; + continue 2; + case '}': + if (self::$objectForMap) { + return (object) $output; + } + + return $output; + } + + // key + $offsetBeforeKeyParsing = $i; + $isKeyQuoted = \in_array($mapping[$i], ['"', "'"], true); + $key = self::parseScalar($mapping, $flags, [':', ' '], $i, false); + + if ($offsetBeforeKeyParsing === $i) { + throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping); + } + + if ('!php/const' === $key || '!php/enum' === $key) { + $key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false); + $key = self::evaluateScalar($key, $flags); + } + + if (false === $i = strpos($mapping, ':', $i)) { + break; + } + + if (!$isKeyQuoted) { + $evaluatedKey = self::evaluateScalar($key, $flags, $references); + + if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) { + throw new ParseException('Implicit casting of incompatible mapping keys to strings is not supported. Quote your evaluable mapping keys instead.', self::$parsedLineNumber + 1, $mapping); + } + } + + if (!$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], [' ', ',', '[', ']', '{', '}', "\n"], true))) { + throw new ParseException('Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}").', self::$parsedLineNumber + 1, $mapping); + } + + if ('<<' === $key) { + $allowOverwrite = true; + } + + while ($i < $len) { + if (':' === $mapping[$i] || ' ' === $mapping[$i] || "\n" === $mapping[$i]) { + ++$i; + + continue; + } + + $tag = self::parseTag($mapping, $i, $flags); + switch ($mapping[$i]) { + case '[': + // nested sequence + $value = self::parseSequence($mapping, $flags, $i, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + // But overwriting is allowed when a merge node is used in current block. + if ('<<' === $key) { + foreach ($value as $parsedValue) { + $output += $parsedValue; + } + } elseif ($allowOverwrite || !isset($output[$key])) { + if (null !== $tag) { + $output[$key] = new TaggedValue($tag, $value); + } else { + $output[$key] = $value; + } + } elseif (isset($output[$key])) { + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); + } + break; + case '{': + // nested mapping + $value = self::parseMapping($mapping, $flags, $i, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + // But overwriting is allowed when a merge node is used in current block. + if ('<<' === $key) { + $output += $value; + } elseif ($allowOverwrite || !isset($output[$key])) { + if (null !== $tag) { + $output[$key] = new TaggedValue($tag, $value); + } else { + $output[$key] = $value; + } + } elseif (isset($output[$key])) { + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); + } + break; + default: + $value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references, $isValueQuoted); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + // But overwriting is allowed when a merge node is used in current block. + if ('<<' === $key) { + $output += $value; + } elseif ($allowOverwrite || !isset($output[$key])) { + if (!$isValueQuoted && \is_string($value) && '' !== $value && '&' === $value[0] && !self::isBinaryString($value) && Parser::preg_match(Parser::REFERENCE_PATTERN, $value, $matches)) { + $references[$matches['ref']] = $matches['value']; + $value = $matches['value']; + } + + if (null !== $tag) { + $output[$key] = new TaggedValue($tag, $value); + } else { + $output[$key] = $value; + } + } elseif (isset($output[$key])) { + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), self::$parsedLineNumber + 1, $mapping); + } + --$i; + } + ++$i; + + continue 2; + } + } + + throw new ParseException(sprintf('Malformed inline YAML string: "%s".', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename); + } + + /** + * Evaluates scalars and replaces magic values. + * + * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved + */ + private static function evaluateScalar(string $scalar, int $flags, array &$references = [], ?bool &$isQuotedString = null): mixed + { + $isQuotedString = false; + $scalar = trim($scalar); + + if (str_starts_with($scalar, '*')) { + if (false !== $pos = strpos($scalar, '#')) { + $value = substr($scalar, 1, $pos - 2); + } else { + $value = substr($scalar, 1); + } + + // an unquoted * + if (false === $value || '' === $value) { + throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + + if (!\array_key_exists($value, $references)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + + return $references[$value]; + } + + $scalarLower = strtolower($scalar); + + switch (true) { + case 'null' === $scalarLower: + case '' === $scalar: + case '~' === $scalar: + return null; + case 'true' === $scalarLower: + return true; + case 'false' === $scalarLower: + return false; + case '!' === $scalar[0]: + switch (true) { + case str_starts_with($scalar, '!!str '): + $s = (string) substr($scalar, 6); + + if (\in_array($s[0] ?? '', ['"', "'"], true)) { + $isQuotedString = true; + $s = self::parseQuotedScalar($s); + } + + return $s; + case str_starts_with($scalar, '! '): + return substr($scalar, 2); + case str_starts_with($scalar, '!php/object'): + if (self::$objectSupport) { + if (!isset($scalar[12])) { + throw new ParseException('Missing value for tag "!php/object".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + return unserialize(self::parseScalar(substr($scalar, 12))); + } + + if (self::$exceptionOnInvalidType) { + throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + return null; + case str_starts_with($scalar, '!php/const'): + if (self::$constantSupport) { + if (!isset($scalar[11])) { + throw new ParseException('Missing value for tag "!php/const".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + $i = 0; + if (\defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) { + return \constant($const); + } + + throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + if (self::$exceptionOnInvalidType) { + throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + return null; + case str_starts_with($scalar, '!php/enum'): + if (self::$constantSupport) { + if (!isset($scalar[11])) { + throw new ParseException('Missing value for tag "!php/enum".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + $i = 0; + $enumName = self::parseScalar(substr($scalar, 10), 0, null, $i, false); + $useName = str_contains($enumName, '::'); + $enum = $useName ? strstr($enumName, '::', true) : $enumName; + + if (!enum_exists($enum)) { + throw new ParseException(sprintf('The enum "%s" is not defined.', $enum), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + if (!$useName) { + return $enum::cases(); + } + if ($useValue = str_ends_with($enumName, '->value')) { + $enumName = substr($enumName, 0, -7); + } + + if (!\defined($enumName)) { + throw new ParseException(sprintf('The string "%s" is not the name of a valid enum.', $enumName), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + $value = \constant($enumName); + + if (!$useValue) { + return $value; + } + if (!$value instanceof \BackedEnum) { + throw new ParseException(sprintf('The enum "%s" defines no value next to its name.', $enumName), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + return $value->value; + } + if (self::$exceptionOnInvalidType) { + throw new ParseException(sprintf('The string "%s" could not be parsed as an enum. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + return null; + case str_starts_with($scalar, '!!float '): + return (float) substr($scalar, 8); + case str_starts_with($scalar, '!!binary '): + return self::evaluateBinaryScalar(substr($scalar, 9)); + } + + throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename); + case preg_match('/^(?:\+|-)?0o(?P[0-7_]++)$/', $scalar, $matches): + $value = str_replace('_', '', $matches['value']); + + if ('-' === $scalar[0]) { + return -octdec($value); + } + + return octdec($value); + case \in_array($scalar[0], ['+', '-', '.'], true) || is_numeric($scalar[0]): + if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) { + $scalar = str_replace('_', '', $scalar); + } + + switch (true) { + case ctype_digit($scalar): + case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): + $cast = (int) $scalar; + + return ($scalar === (string) $cast) ? $cast : $scalar; + case is_numeric($scalar): + case Parser::preg_match(self::getHexRegex(), $scalar): + $scalar = str_replace('_', '', $scalar); + + return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; + case '.inf' === $scalarLower: + case '.nan' === $scalarLower: + return -log(0); + case '-.inf' === $scalarLower: + return log(0); + case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): + return (float) str_replace('_', '', $scalar); + case Parser::preg_match(self::getTimestampRegex(), $scalar): + try { + // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. + $time = new \DateTimeImmutable($scalar, new \DateTimeZone('UTC')); + } catch (\Exception $e) { + // Some dates accepted by the regex are not valid dates. + throw new ParseException(\sprintf('The date "%s" could not be parsed as it is an invalid date.', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename, $e); + } + + if (Yaml::PARSE_DATETIME & $flags) { + return $time; + } + + if ('' !== rtrim($time->format('u'), '0')) { + return (float) $time->format('U.u'); + } + + try { + if (false !== $scalar = $time->getTimestamp()) { + return $scalar; + } + } catch (\ValueError) { + // no-op + } + + return $time->format('U'); + } + } + + return (string) $scalar; + } + + private static function parseTag(string $value, int &$i, int $flags): ?string + { + if ('!' !== $value[$i]) { + return null; + } + + $tagLength = strcspn($value, " \t\n[]{},", $i + 1); + $tag = substr($value, $i + 1, $tagLength); + + $nextOffset = $i + $tagLength + 1; + $nextOffset += strspn($value, ' ', $nextOffset); + + if ('' === $tag && (!isset($value[$nextOffset]) || \in_array($value[$nextOffset], [']', '}', ','], true))) { + throw new ParseException('Using the unquoted scalar value "!" is not supported. You must quote it.', self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + + // Is followed by a scalar and is a built-in tag + if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && ('!' === $tag[0] || \in_array($tag, ['str', 'php/const', 'php/enum', 'php/object'], true))) { + // Manage in {@link self::evaluateScalar()} + return null; + } + + $i = $nextOffset; + + // Built-in tags + if ('' !== $tag && '!' === $tag[0]) { + throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + + if ('' !== $tag && !isset($value[$i])) { + throw new ParseException(sprintf('Missing value for tag "%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + + if ('' === $tag || Yaml::PARSE_CUSTOM_TAGS & $flags) { + return $tag; + } + + throw new ParseException(sprintf('Tags support is not enabled. Enable the "Yaml::PARSE_CUSTOM_TAGS" flag to use "!%s".', $tag), self::$parsedLineNumber + 1, $value, self::$parsedFilename); + } + + public static function evaluateBinaryScalar(string $scalar): string + { + $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); + + if (0 !== (\strlen($parsedBinaryData) % 4)) { + throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) { + throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + return base64_decode($parsedBinaryData, true); + } + + private static function isBinaryString(string $value): bool + { + return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value); + } + + /** + * Gets a regex that matches a YAML date. + * + * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 + */ + private static function getTimestampRegex(): string + { + return <<[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[0-9][0-9]))?))?)? + $~x +EOF; + } + + /** + * Gets a regex that matches a YAML number in hexadecimal notation. + */ + private static function getHexRegex(): string + { + return '~^0x[0-9a-f_]++$~i'; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/LICENSE b/packages/playground/data-liberation/vendor-patched/symfony/yaml/LICENSE new file mode 100644 index 0000000000..0138f8f071 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Parser.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Parser.php new file mode 100644 index 0000000000..7a4150999a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Parser.php @@ -0,0 +1,1244 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Tag\TaggedValue; + +/** + * Parser parses YAML strings to convert them to PHP arrays. + * + * @author Fabien Potencier + * + * @final + */ +class Parser +{ + public const TAG_PATTERN = '(?P![\w!.\/:-]+)'; + public const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; + public const REFERENCE_PATTERN = '#^&(?P[^ ]++) *+(?P.*)#u'; + + private ?string $filename = null; + private int $offset = 0; + private int $numberOfParsedLines = 0; + private ?int $totalNumberOfLines = null; + private array $lines = []; + private int $currentLineNb = -1; + private string $currentLine = ''; + private array $refs = []; + private array $skippedLineNumbers = []; + private array $locallySkippedLineNumbers = []; + private array $refsBeingParsed = []; + + /** + * Parses a YAML file into a PHP value. + * + * @param string $filename The path to the YAML file to be parsed + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior + * + * @throws ParseException If the file could not be read or the YAML is not valid + */ + public function parseFile(string $filename, int $flags = 0): mixed + { + if (!is_file($filename)) { + throw new ParseException(sprintf('File "%s" does not exist.', $filename)); + } + + if (!is_readable($filename)) { + throw new ParseException(sprintf('File "%s" cannot be read.', $filename)); + } + + $this->filename = $filename; + + try { + return $this->parse(file_get_contents($filename), $flags); + } finally { + $this->filename = null; + } + } + + /** + * Parses a YAML string to a PHP value. + * + * @param string $value A YAML string + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior + * + * @throws ParseException If the YAML is not valid + */ + public function parse(string $value, int $flags = 0): mixed + { + if (false === preg_match('//u', $value)) { + throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename); + } + + $this->refs = []; + + try { + $data = $this->doParse($value, $flags); + } finally { + $this->refsBeingParsed = []; + $this->offset = 0; + $this->lines = []; + $this->currentLine = ''; + $this->numberOfParsedLines = 0; + $this->refs = []; + $this->skippedLineNumbers = []; + $this->locallySkippedLineNumbers = []; + $this->totalNumberOfLines = null; + } + + return $data; + } + + private function doParse(string $value, int $flags): mixed + { + $this->currentLineNb = -1; + $this->currentLine = ''; + $value = $this->cleanup($value); + $this->lines = explode("\n", $value); + $this->numberOfParsedLines = \count($this->lines); + $this->locallySkippedLineNumbers = []; + $this->totalNumberOfLines ??= $this->numberOfParsedLines; + + if (!$this->moveToNextLine()) { + return null; + } + + $data = []; + $context = null; + $allowOverwrite = false; + + while ($this->isCurrentLineEmpty()) { + if (!$this->moveToNextLine()) { + return null; + } + } + + // Resolves the tag and returns if end of the document + if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) { + return new TaggedValue($tag, ''); + } + + do { + if ($this->isCurrentLineEmpty()) { + continue; + } + + // tab? + if ("\t" === $this->currentLine[0]) { + throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename); + + $isRef = $mergeNode = false; + if ('-' === $this->currentLine[0] && self::preg_match('#^\-((?P\s+)(?P.+))?$#u', rtrim($this->currentLine), $values)) { + if ($context && 'mapping' == $context) { + throw new ParseException('You cannot define a sequence item when in a mapping.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + $context = 'sequence'; + + if (isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) { + $isRef = $matches['ref']; + $this->refsBeingParsed[] = $isRef; + $values['value'] = $matches['value']; + } + + if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) { + throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + // array + if (isset($values['value']) && str_starts_with(ltrim($values['value'], ' '), '-')) { + // Inline first child + $currentLineNumber = $this->getRealCurrentLineNb(); + + $sequenceIndentation = \strlen($values['leadspaces']) + 1; + $sequenceYaml = substr($this->currentLine, $sequenceIndentation); + $sequenceYaml .= "\n".$this->getNextEmbedBlock($sequenceIndentation, true); + + $data[] = $this->parseBlock($currentLineNumber, rtrim($sequenceYaml), $flags); + } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || str_starts_with(ltrim($values['value'], ' '), '#')) { + $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true) ?? '', $flags); + } elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) { + $data[] = new TaggedValue( + $subTag, + $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags) + ); + } else { + if ( + isset($values['leadspaces']) + && ( + '!' === $values['value'][0] + || self::preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $this->trimTag($values['value']), $matches) + ) + ) { + $block = $values['value']; + if ($this->isNextLineIndented() || isset($matches['value']) && '>-' === $matches['value']) { + $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1); + } + + $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags); + } else { + $data[] = $this->parseValue($values['value'], $flags, $context); + } + } + if ($isRef) { + $this->refs[$isRef] = end($data); + array_pop($this->refsBeingParsed); + } + } elseif ( + self::preg_match('#^(?P(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{!].*?)) *\:(( |\t)++(?P.+))?$#u', rtrim($this->currentLine), $values) + && (!str_contains($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"])) + ) { + if ($context && 'sequence' == $context) { + throw new ParseException('You cannot define a mapping item when in a sequence.', $this->currentLineNb + 1, $this->currentLine, $this->filename); + } + $context = 'mapping'; + + try { + $key = Inline::parseScalar($values['key']); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + if (!\is_string($key) && !\is_int($key)) { + throw new ParseException((is_numeric($key) ? 'Numeric' : 'Non-string').' keys are not supported. Quote your evaluable mapping keys instead.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + // Convert float keys to strings, to avoid being converted to integers by PHP + if (\is_float($key)) { + $key = (string) $key; + } + + if ('<<' === $key && (!isset($values['value']) || '&' !== $values['value'][0] || !self::preg_match('#^&(?P[^ ]+)#u', $values['value'], $refMatches))) { + $mergeNode = true; + $allowOverwrite = true; + if (isset($values['value'][0]) && '*' === $values['value'][0]) { + $refName = substr(rtrim($values['value']), 1); + if (!\array_key_exists($refName, $this->refs)) { + if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) { + throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$refName])), $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); + } + + throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + $refValue = $this->refs[$refName]; + + if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) { + $refValue = (array) $refValue; + } + + if (!\is_array($refValue)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + $data += $refValue; // array union + } else { + if (isset($values['value']) && '' !== $values['value']) { + $value = $values['value']; + } else { + $value = $this->getNextEmbedBlock(); + } + $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags); + + if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) { + $parsed = (array) $parsed; + } + + if (!\is_array($parsed)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + if (isset($parsed[0])) { + // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes + // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier + // in the sequence override keys specified in later mapping nodes. + foreach ($parsed as $parsedItem) { + if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) { + $parsedItem = (array) $parsedItem; + } + + if (!\is_array($parsedItem)) { + throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename); + } + + $data += $parsedItem; // array union + } + } else { + // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the + // current mapping, unless the key already exists in it. + $data += $parsed; // array union + } + } + } elseif ('<<' !== $key && isset($values['value']) && '&' === $values['value'][0] && self::preg_match(self::REFERENCE_PATTERN, $values['value'], $matches)) { + $isRef = $matches['ref']; + $this->refsBeingParsed[] = $isRef; + $values['value'] = $matches['value']; + } + + $subTag = null; + if ($mergeNode) { + // Merge keys + } elseif (!isset($values['value']) || '' === $values['value'] || str_starts_with($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { + // hash + // if next line is less indented or equal, then it means that the current value is null + if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + if (null !== $subTag) { + $data[$key] = new TaggedValue($subTag, ''); + } else { + $data[$key] = null; + } + } else { + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } else { + // remember the parsed line number here in case we need it to provide some contexts in error messages below + $realCurrentLineNbKey = $this->getRealCurrentLineNb(); + $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); + if ('<<' === $key) { + $this->refs[$refMatches['ref']] = $value; + + if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) { + $value = (array) $value; + } + + $data += $value; + } elseif ($allowOverwrite || !isset($data[$key])) { + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if (null !== $subTag) { + $data[$key] = new TaggedValue($subTag, $value); + } else { + $data[$key] = $value; + } + } else { + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $realCurrentLineNbKey + 1, $this->currentLine); + } + } + } else { + $value = $this->parseValue(rtrim($values['value']), $flags, $context); + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + $data[$key] = $value; + } else { + throw new ParseException(sprintf('Duplicate key "%s" detected.', $key), $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } + if ($isRef) { + $this->refs[$isRef] = $data[$key]; + array_pop($this->refsBeingParsed); + } + } elseif ('"' === $this->currentLine[0] || "'" === $this->currentLine[0]) { + if (null !== $context) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + try { + return Inline::parse($this->lexInlineQuotedString(), $flags, $this->refs); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + } elseif ('{' === $this->currentLine[0]) { + if (null !== $context) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + try { + $parsedMapping = Inline::parse($this->lexInlineMapping(), $flags, $this->refs); + + while ($this->moveToNextLine()) { + if (!$this->isCurrentLineEmpty()) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + } + + return $parsedMapping; + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + } elseif ('[' === $this->currentLine[0]) { + if (null !== $context) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + try { + $parsedSequence = Inline::parse($this->lexInlineSequence(), $flags, $this->refs); + + while ($this->moveToNextLine()) { + if (!$this->isCurrentLineEmpty()) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + } + + return $parsedSequence; + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + } else { + // multiple documents are not supported + if ('---' === $this->currentLine) { + throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename); + } + + if (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1]) { + throw new ParseException('Complex mappings are not supported.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + // 1-liner optionally followed by newline(s) + if (\is_string($value) && $this->lines[0] === trim($value)) { + try { + $value = Inline::parse($this->lines[0], $flags, $this->refs); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + return $value; + } + + // try to parse the value as a multi-line string as a last resort + if (0 === $this->currentLineNb) { + $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = false; + $value = ''; + + foreach ($this->lines as $line) { + $trimmedLine = trim($line); + if ('#' === ($trimmedLine[0] ?? '')) { + continue; + } + // If the indentation is not consistent at offset 0, it is to be considered as a ParseError + if (0 === $this->offset && isset($line[0]) && ' ' === $line[0]) { + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + if (str_contains($line, ': ')) { + throw new ParseException('Mapping values are not allowed in multi-line blocks.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + + if ('' === $trimmedLine) { + $value .= "\n"; + } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { + $value .= ' '; + } + + if ('' !== $trimmedLine && str_ends_with($line, '\\')) { + $value .= ltrim(substr($line, 0, -1)); + } elseif ('' !== $trimmedLine) { + $value .= $trimmedLine; + } + + if ('' === $trimmedLine) { + $previousLineWasNewline = true; + $previousLineWasTerminatedWithBackslash = false; + } elseif (str_ends_with($line, '\\')) { + $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = true; + } else { + $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = false; + } + } + + try { + return Inline::parse(trim($value)); + } catch (ParseException) { + // fall-through to the ParseException thrown below + } + } + + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + } while ($this->moveToNextLine()); + + if (null !== $tag) { + $data = new TaggedValue($tag, $data); + } + + if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && 'mapping' === $context && !\is_object($data)) { + $object = new \stdClass(); + + foreach ($data as $key => $value) { + $object->$key = $value; + } + + $data = $object; + } + + return $data ?: null; + } + + private function parseBlock(int $offset, string $yaml, int $flags): mixed + { + $skippedLineNumbers = $this->skippedLineNumbers; + + foreach ($this->locallySkippedLineNumbers as $lineNumber) { + if ($lineNumber < $offset) { + continue; + } + + $skippedLineNumbers[] = $lineNumber; + } + + $parser = new self(); + $parser->offset = $offset; + $parser->totalNumberOfLines = $this->totalNumberOfLines; + $parser->skippedLineNumbers = $skippedLineNumbers; + $parser->refs = &$this->refs; + $parser->refsBeingParsed = $this->refsBeingParsed; + + return $parser->doParse($yaml, $flags); + } + + /** + * Returns the current line number (takes the offset into account). + * + * @internal + */ + public function getRealCurrentLineNb(): int + { + $realCurrentLineNumber = $this->currentLineNb + $this->offset; + + foreach ($this->skippedLineNumbers as $skippedLineNumber) { + if ($skippedLineNumber > $realCurrentLineNumber) { + break; + } + + ++$realCurrentLineNumber; + } + + return $realCurrentLineNumber; + } + + private function getCurrentLineIndentation(): int + { + if (' ' !== ($this->currentLine[0] ?? '')) { + return 0; + } + + return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' ')); + } + + /** + * Returns the next embed block of YAML. + * + * @param int|null $indentation The indent level at which the block is to be read, or null for default + * @param bool $inSequence True if the enclosing data structure is a sequence + * + * @throws ParseException When indentation problem are detected + */ + private function getNextEmbedBlock(?int $indentation = null, bool $inSequence = false): string + { + $oldLineIndentation = $this->getCurrentLineIndentation(); + + if (!$this->moveToNextLine()) { + return ''; + } + + if (null === $indentation) { + $newIndent = null; + $movements = 0; + + do { + $EOF = false; + + // empty and comment-like lines do not influence the indentation depth + if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) { + $EOF = !$this->moveToNextLine(); + + if (!$EOF) { + ++$movements; + } + } else { + $newIndent = $this->getCurrentLineIndentation(); + } + } while (!$EOF && null === $newIndent); + + for ($i = 0; $i < $movements; ++$i) { + $this->moveToPreviousLine(); + } + + $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); + + if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + } else { + $newIndent = $indentation; + } + + $data = []; + + if ($this->getCurrentLineIndentation() >= $newIndent) { + $data[] = substr($this->currentLine, $newIndent ?? 0); + } elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) { + $data[] = $this->currentLine; + } else { + $this->moveToPreviousLine(); + + return ''; + } + + if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) { + // the previous line contained a dash but no item content, this line is a sequence item with the same indentation + // and therefore no nested list or mapping + $this->moveToPreviousLine(); + + return ''; + } + + $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); + $isItComment = $this->isCurrentLineComment(); + + while ($this->moveToNextLine()) { + if ($isItComment && !$isItUnindentedCollection) { + $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); + $isItComment = $this->isCurrentLineComment(); + } + + $indent = $this->getCurrentLineIndentation(); + + if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { + $this->moveToPreviousLine(); + break; + } + + if ($this->isCurrentLineBlank()) { + $data[] = substr($this->currentLine, $newIndent ?? 0); + continue; + } + + if ($indent >= $newIndent) { + $data[] = substr($this->currentLine, $newIndent ?? 0); + } elseif ($this->isCurrentLineComment()) { + $data[] = $this->currentLine; + } elseif (0 == $indent) { + $this->moveToPreviousLine(); + + break; + } else { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); + } + } + + return implode("\n", $data); + } + + private function hasMoreLines(): bool + { + return (\count($this->lines) - 1) > $this->currentLineNb; + } + + /** + * Moves the parser to the next line. + */ + private function moveToNextLine(): bool + { + if ($this->currentLineNb >= $this->numberOfParsedLines - 1) { + return false; + } + + $this->currentLine = $this->lines[++$this->currentLineNb]; + + return true; + } + + /** + * Moves the parser to the previous line. + */ + private function moveToPreviousLine(): bool + { + if ($this->currentLineNb < 1) { + return false; + } + + $this->currentLine = $this->lines[--$this->currentLineNb]; + + return true; + } + + /** + * Parses a YAML value. + * + * @param string $value A YAML value + * @param int $flags A bit field of Yaml::PARSE_* constants to customize the YAML parser behavior + * @param string $context The parser context (either sequence or mapping) + * + * @throws ParseException When reference does not exist + */ + private function parseValue(string $value, int $flags, string $context): mixed + { + if (str_starts_with($value, '*')) { + if (false !== $pos = strpos($value, '#')) { + $value = substr($value, 1, $pos - 2); + } else { + $value = substr($value, 1); + } + + if (!\array_key_exists($value, $this->refs)) { + if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) { + throw new ParseException(sprintf('Circular reference [%s] detected for reference "%s".', implode(', ', array_merge(\array_slice($this->refsBeingParsed, $pos), [$value])), $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); + } + + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); + } + + return $this->refs[$value]; + } + + if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { + $modifiers = $matches['modifiers'] ?? ''; + + $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers)); + + if ('' !== $matches['tag'] && '!' !== $matches['tag']) { + if ('!!binary' === $matches['tag']) { + return Inline::evaluateBinaryScalar($data); + } + + return new TaggedValue(substr($matches['tag'], 1), $data); + } + + return $data; + } + + try { + if ('' !== $value && '{' === $value[0]) { + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); + + return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs); + } elseif ('' !== $value && '[' === $value[0]) { + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); + + return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs); + } + + switch ($value[0] ?? '') { + case '"': + case "'": + $cursor = \strlen(rtrim($this->currentLine)) - \strlen(rtrim($value)); + $parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs); + + if (isset($this->currentLine[$cursor]) && preg_replace('/\s*(#.*)?$/A', '', substr($this->currentLine, $cursor))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor))); + } + + return $parsedValue; + default: + $lines = []; + + while ($this->moveToNextLine()) { + // unquoted strings end before the first unindented line + if (0 === $this->getCurrentLineIndentation()) { + $this->moveToPreviousLine(); + + break; + } + + $lines[] = trim($this->currentLine); + } + + for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { + if ('' === $lines[$i]) { + $value .= "\n"; + $previousLineBlank = true; + } elseif ($previousLineBlank) { + $value .= $lines[$i]; + $previousLineBlank = false; + } else { + $value .= ' '.$lines[$i]; + $previousLineBlank = false; + } + } + + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); + + $parsedValue = Inline::parse($value, $flags, $this->refs); + + if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && str_contains($parsedValue, ': ')) { + throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename); + } + + return $parsedValue; + } + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + } + + /** + * Parses a block scalar. + * + * @param string $style The style indicator that was used to begin this block scalar (| or >) + * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -) + * @param int $indentation The indentation indicator that was used to begin this block scalar + */ + private function parseBlockScalar(string $style, string $chomping = '', int $indentation = 0): string + { + $notEOF = $this->moveToNextLine(); + if (!$notEOF) { + return ''; + } + + $isCurrentLineBlank = $this->isCurrentLineBlank(); + $blockLines = []; + + // leading blank lines are consumed before determining indentation + while ($notEOF && $isCurrentLineBlank) { + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $blockLines[] = ''; + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + + // determine indentation if not specified + if (0 === $indentation) { + $currentLineLength = \strlen($this->currentLine); + + for ($i = 0; $i < $currentLineLength && ' ' === $this->currentLine[$i]; ++$i) { + ++$indentation; + } + } + + if ($indentation > 0) { + $pattern = sprintf('/^ {%d}(.*)$/', $indentation); + + while ( + $notEOF && ( + $isCurrentLineBlank + || self::preg_match($pattern, $this->currentLine, $matches) + ) + ) { + if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) { + $blockLines[] = substr($this->currentLine, $indentation); + } elseif ($isCurrentLineBlank) { + $blockLines[] = ''; + } else { + $blockLines[] = $matches[1]; + } + + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + } elseif ($notEOF) { + $blockLines[] = ''; + } + + if ($notEOF) { + $blockLines[] = ''; + $this->moveToPreviousLine(); + } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { + $blockLines[] = ''; + } + + // folded style + if ('>' === $style) { + $text = ''; + $previousLineIndented = false; + $previousLineBlank = false; + + for ($i = 0, $blockLinesCount = \count($blockLines); $i < $blockLinesCount; ++$i) { + if ('' === $blockLines[$i]) { + $text .= "\n"; + $previousLineIndented = false; + $previousLineBlank = true; + } elseif (' ' === $blockLines[$i][0]) { + $text .= "\n".$blockLines[$i]; + $previousLineIndented = true; + $previousLineBlank = false; + } elseif ($previousLineIndented) { + $text .= "\n".$blockLines[$i]; + $previousLineIndented = false; + $previousLineBlank = false; + } elseif ($previousLineBlank || 0 === $i) { + $text .= $blockLines[$i]; + $previousLineIndented = false; + $previousLineBlank = false; + } else { + $text .= ' '.$blockLines[$i]; + $previousLineIndented = false; + $previousLineBlank = false; + } + } + } else { + $text = implode("\n", $blockLines); + } + + // deal with trailing newlines + if ('' === $chomping) { + $text = preg_replace('/\n+$/', "\n", $text); + } elseif ('-' === $chomping) { + $text = preg_replace('/\n+$/', '', $text); + } + + return $text; + } + + /** + * Returns true if the next line is indented. + */ + private function isNextLineIndented(): bool + { + $currentIndentation = $this->getCurrentLineIndentation(); + $movements = 0; + + do { + $EOF = !$this->moveToNextLine(); + + if (!$EOF) { + ++$movements; + } + } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment())); + + if ($EOF) { + for ($i = 0; $i < $movements; ++$i) { + $this->moveToPreviousLine(); + } + + return false; + } + + $ret = $this->getCurrentLineIndentation() > $currentIndentation; + + for ($i = 0; $i < $movements; ++$i) { + $this->moveToPreviousLine(); + } + + return $ret; + } + + private function isCurrentLineEmpty(): bool + { + return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); + } + + private function isCurrentLineBlank(): bool + { + return '' === $this->currentLine || '' === trim($this->currentLine, ' '); + } + + private function isCurrentLineComment(): bool + { + // checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = '' !== $this->currentLine && ' ' === $this->currentLine[0] ? ltrim($this->currentLine, ' ') : $this->currentLine; + + return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; + } + + private function isCurrentLineLastLineInDocument(): bool + { + return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); + } + + private function cleanup(string $value): string + { + $value = str_replace(["\r\n", "\r"], "\n", $value); + + // strip YAML header + $count = 0; + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); + $this->offset += $count; + + // remove leading comments + $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); + if (1 === $count) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + } + + // remove start of the document marker (---) + $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); + if (1 === $count) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + + // remove end of the document marker (...) + $value = preg_replace('#\.\.\.\s*$#', '', $value); + } + + return $value; + } + + private function isNextLineUnIndentedCollection(): bool + { + $currentIndentation = $this->getCurrentLineIndentation(); + $movements = 0; + + do { + $EOF = !$this->moveToNextLine(); + + if (!$EOF) { + ++$movements; + } + } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment())); + + if ($EOF) { + return false; + } + + $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); + + for ($i = 0; $i < $movements; ++$i) { + $this->moveToPreviousLine(); + } + + return $ret; + } + + private function isStringUnIndentedCollectionItem(): bool + { + return '-' === rtrim($this->currentLine) || str_starts_with($this->currentLine, '- '); + } + + /** + * A local wrapper for "preg_match" which will throw a ParseException if there + * is an internal error in the PCRE engine. + * + * This avoids us needing to check for "false" every time PCRE is used + * in the YAML engine + * + * @throws ParseException on a PCRE internal error + * + * @internal + */ + public static function preg_match(string $pattern, string $subject, ?array &$matches = null, int $flags = 0, int $offset = 0): int + { + if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { + throw new ParseException(preg_last_error_msg()); + } + + return $ret; + } + + /** + * Trim the tag on top of the value. + * + * Prevent values such as "!foo {quz: bar}" to be considered as + * a mapping block. + */ + private function trimTag(string $value): string + { + if ('!' === $value[0]) { + return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' '); + } + + return $value; + } + + private function getLineTag(string $value, int $flags, bool $nextLineCheck = true): ?string + { + if ('' === $value || '!' !== $value[0] || 1 !== self::preg_match('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) { + return null; + } + + if ($nextLineCheck && !$this->isNextLineIndented()) { + return null; + } + + $tag = substr($matches['tag'], 1); + + // Built-in tags + if ($tag && '!' === $tag[0]) { + throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename); + } + + if (Yaml::PARSE_CUSTOM_TAGS & $flags) { + return $tag; + } + + throw new ParseException(sprintf('Tags support is not enabled. You must use the flag "Yaml::PARSE_CUSTOM_TAGS" to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename); + } + + private function lexInlineQuotedString(int &$cursor = 0): string + { + $quotation = $this->currentLine[$cursor]; + $value = $quotation; + ++$cursor; + + $previousLineWasNewline = true; + $previousLineWasTerminatedWithBackslash = false; + $lineNumber = 0; + + do { + if (++$lineNumber > 1) { + $cursor += strspn($this->currentLine, ' ', $cursor); + } + + if ($this->isCurrentLineBlank()) { + $value .= "\n"; + } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { + $value .= ' '; + } + + for (; \strlen($this->currentLine) > $cursor; ++$cursor) { + switch ($this->currentLine[$cursor]) { + case '\\': + if ("'" === $quotation) { + $value .= '\\'; + } elseif (isset($this->currentLine[++$cursor])) { + $value .= '\\'.$this->currentLine[$cursor]; + } + + break; + case $quotation: + ++$cursor; + + if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) { + $value .= "''"; + break; + } + + return $value.$quotation; + default: + $value .= $this->currentLine[$cursor]; + } + } + + if ($this->isCurrentLineBlank()) { + $previousLineWasNewline = true; + $previousLineWasTerminatedWithBackslash = false; + } elseif ('\\' === $this->currentLine[-1]) { + $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = true; + } else { + $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = false; + } + + if ($this->hasMoreLines()) { + $cursor = 0; + } + } while ($this->moveToNextLine()); + + throw new ParseException('Malformed inline YAML string.'); + } + + private function lexUnquotedString(int &$cursor): string + { + $offset = $cursor; + $cursor += strcspn($this->currentLine, '[]{},: ', $cursor); + + if ($cursor === $offset) { + throw new ParseException('Malformed unquoted YAML string.'); + } + + return substr($this->currentLine, $offset, $cursor - $offset); + } + + private function lexInlineMapping(int &$cursor = 0): string + { + return $this->lexInlineStructure($cursor, '}'); + } + + private function lexInlineSequence(int &$cursor = 0): string + { + return $this->lexInlineStructure($cursor, ']'); + } + + private function lexInlineStructure(int &$cursor, string $closingTag): string + { + $value = $this->currentLine[$cursor]; + ++$cursor; + + do { + $this->consumeWhitespaces($cursor); + + while (isset($this->currentLine[$cursor])) { + switch ($this->currentLine[$cursor]) { + case '"': + case "'": + $value .= $this->lexInlineQuotedString($cursor); + break; + case ':': + case ',': + $value .= $this->currentLine[$cursor]; + ++$cursor; + break; + case '{': + $value .= $this->lexInlineMapping($cursor); + break; + case '[': + $value .= $this->lexInlineSequence($cursor); + break; + case $closingTag: + $value .= $this->currentLine[$cursor]; + ++$cursor; + + return $value; + case '#': + break 2; + default: + $value .= $this->lexUnquotedString($cursor); + } + + if ($this->consumeWhitespaces($cursor)) { + $value .= ' '; + } + } + + if ($this->hasMoreLines()) { + $cursor = 0; + } + } while ($this->moveToNextLine()); + + throw new ParseException('Malformed inline YAML string.'); + } + + private function consumeWhitespaces(int &$cursor): bool + { + $whitespacesConsumed = 0; + + do { + $whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor); + $whitespacesConsumed += $whitespaceOnlyTokenLength; + $cursor += $whitespaceOnlyTokenLength; + + if (isset($this->currentLine[$cursor])) { + return 0 < $whitespacesConsumed; + } + + if ($this->hasMoreLines()) { + $cursor = 0; + } + } while ($this->moveToNextLine()); + + return 0 < $whitespacesConsumed; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/README.md b/packages/playground/data-liberation/vendor-patched/symfony/yaml/README.md new file mode 100644 index 0000000000..87653ae81a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/README.md @@ -0,0 +1,11 @@ +# Yaml Component + +The Yaml component loads and dumps YAML files. + +## Resources + +- [Documentation](https://symfony.com/doc/current/components/yaml.html) +- [Contributing](https://symfony.com/doc/current/contributing/index.html) +- [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Resources/bin/yaml-lint b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Resources/bin/yaml-lint new file mode 100755 index 0000000000..143869e018 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Resources/bin/yaml-lint @@ -0,0 +1,49 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if ('cli' !== \PHP_SAPI) { + throw new Exception('This script must be run from the command line.'); +} + +/** + * Runs the Yaml lint command. + * + * @author Jan Schädlich + */ + +use Symfony\Component\Console\Application; +use Symfony\Component\Yaml\Command\LintCommand; + +function includeIfExists(string $file): bool +{ + return file_exists($file) && include $file; +} + +if ( + !includeIfExists(__DIR__ . '/../../../../autoload.php') && + !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && + !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php') +) { + fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL); + exit(1); +} + +if (!class_exists(Application::class)) { + fwrite(STDERR, 'You need the "symfony/console" component in order to run the Yaml linter.'.PHP_EOL); + exit(1); +} + +(new Application())->add($command = new LintCommand()) + ->getApplication() + ->setDefaultCommand($command->getName(), true) + ->run() +; diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Tag/TaggedValue.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Tag/TaggedValue.php new file mode 100644 index 0000000000..9d29193490 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Tag/TaggedValue.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Tag; + +/** + * @author Nicolas Grekas + * @author Guilhem N. + */ +final class TaggedValue +{ + public function __construct( + private string $tag, + private mixed $value, + ) { + } + + public function getTag(): string + { + return $this->tag; + } + + public function getValue(): mixed + { + return $this->value; + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Unescaper.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Unescaper.php new file mode 100644 index 0000000000..9e640ff248 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Unescaper.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Unescaper encapsulates unescaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + * + * @internal + */ +class Unescaper +{ + /** + * Regex fragment that matches an escaped character in a double quoted string. + */ + public const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; + + /** + * Unescapes a single quoted string. + * + * @param string $value A single quoted string + */ + public function unescapeSingleQuotedString(string $value): string + { + return str_replace('\'\'', '\'', $value); + } + + /** + * Unescapes a double quoted string. + * + * @param string $value A double quoted string + */ + public function unescapeDoubleQuotedString(string $value): string + { + $callback = fn ($match) => $this->unescapeCharacter($match[0]); + + // evaluate the string + return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); + } + + /** + * Unescapes a character that was found in a double-quoted string. + * + * @param string $value An escaped character + */ + private function unescapeCharacter(string $value): string + { + return match ($value[1]) { + '0' => "\x0", + 'a' => "\x7", + 'b' => "\x8", + 't' => "\t", + "\t" => "\t", + 'n' => "\n", + 'v' => "\xB", + 'f' => "\xC", + 'r' => "\r", + 'e' => "\x1B", + ' ' => ' ', + '"' => '"', + '/' => '/', + '\\' => '\\', + // U+0085 NEXT LINE + 'N' => "\xC2\x85", + // U+00A0 NO-BREAK SPACE + '_' => "\xC2\xA0", + // U+2028 LINE SEPARATOR + 'L' => "\xE2\x80\xA8", + // U+2029 PARAGRAPH SEPARATOR + 'P' => "\xE2\x80\xA9", + 'x' => self::utf8chr(hexdec(substr($value, 2, 2))), + 'u' => self::utf8chr(hexdec(substr($value, 2, 4))), + 'U' => self::utf8chr(hexdec(substr($value, 2, 8))), + default => throw new ParseException(sprintf('Found unknown escape character "%s".', $value)), + }; + } + + /** + * Get the UTF-8 character for the given code point. + */ + private static function utf8chr(int $c): string + { + if (0x80 > $c %= 0x200000) { + return \chr($c); + } + if (0x800 > $c) { + return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F); + } + if (0x10000 > $c) { + return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F); + } + + return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 & 0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/Yaml.php b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Yaml.php new file mode 100644 index 0000000000..e2d2af7310 --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/Yaml.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Yaml offers convenience methods to load and dump YAML. + * + * @author Fabien Potencier + * + * @final + */ +class Yaml +{ + public const DUMP_OBJECT = 1; + public const PARSE_EXCEPTION_ON_INVALID_TYPE = 2; + public const PARSE_OBJECT = 4; + public const PARSE_OBJECT_FOR_MAP = 8; + public const DUMP_EXCEPTION_ON_INVALID_TYPE = 16; + public const PARSE_DATETIME = 32; + public const DUMP_OBJECT_AS_MAP = 64; + public const DUMP_MULTI_LINE_LITERAL_BLOCK = 128; + public const PARSE_CONSTANT = 256; + public const PARSE_CUSTOM_TAGS = 512; + public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024; + public const DUMP_NULL_AS_TILDE = 2048; + public const DUMP_NUMERIC_KEY_AS_STRING = 4096; + + /** + * Parses a YAML file into a PHP value. + * + * Usage: + * + * $array = Yaml::parseFile('config.yml'); + * print_r($array); + * + * @param string $filename The path to the YAML file to be parsed + * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * + * @throws ParseException If the file could not be read or the YAML is not valid + */ + public static function parseFile(string $filename, int $flags = 0): mixed + { + $yaml = new Parser(); + + return $yaml->parseFile($filename, $flags); + } + + /** + * Parses YAML into a PHP value. + * + * Usage: + * + * $array = Yaml::parse(file_get_contents('config.yml')); + * print_r($array); + * + * + * @param string $input A string containing YAML + * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior + * + * @throws ParseException If the YAML is not valid + */ + public static function parse(string $input, int $flags = 0): mixed + { + $yaml = new Parser(); + + return $yaml->parse($input, $flags); + } + + /** + * Dumps a PHP value to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param mixed $input The PHP value + * @param int $inline The level where you switch to inline YAML + * @param int $indent The amount of spaces to use for indentation of nested nodes + * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string + */ + public static function dump(mixed $input, int $inline = 2, int $indent = 4, int $flags = 0): string + { + $yaml = new Dumper($indent); + + return $yaml->dump($input, $inline, 0, $flags); + } +} diff --git a/packages/playground/data-liberation/vendor-patched/symfony/yaml/composer.json b/packages/playground/data-liberation/vendor-patched/symfony/yaml/composer.json new file mode 100644 index 0000000000..f211c9df3a --- /dev/null +++ b/packages/playground/data-liberation/vendor-patched/symfony/yaml/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/yaml", + "type": "library", + "description": "Loads and dumps YAML files", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "minimum-stability": "dev" +} diff --git a/packages/playground/php-cors-proxy/cors-proxy.php b/packages/playground/php-cors-proxy/cors-proxy.php index 12308f2ece..438c0d410e 100644 --- a/packages/playground/php-cors-proxy/cors-proxy.php +++ b/packages/playground/php-cors-proxy/cors-proxy.php @@ -5,7 +5,6 @@ define('MAX_REQUEST_SIZE', 1 * 1024 * 1024); // 1MB define('MAX_RESPONSE_SIZE', 100 * 1024 * 1024); // 100MB - require_once __DIR__ . '/cors-proxy-functions.php'; $config_file = __DIR__ . '/cors-proxy-config.php'; @@ -20,9 +19,8 @@ header('Access-Control-Allow-Origin: ' . $origin); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); - header('Access-Control-Allow-Headers: Authorization, Content-Type'); + header('Access-Control-Allow-Headers: Accept, Authorization, Content-Type'); } - if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { header("Allow: GET, POST, OPTIONS"); exit; @@ -46,8 +44,10 @@ if (function_exists('playground_cors_proxy_maybe_rate_limit')) { playground_cors_proxy_maybe_rate_limit(); } else if ( - !defined('PLAYGROUND_CORS_PROXY_DISABLE_RATE_LIMIT') || - !PLAYGROUND_CORS_PROXY_DISABLE_RATE_LIMIT + !getenv('PLAYGROUND_CORS_PROXY_DISABLE_RATE_LIMIT') && ( + !defined('PLAYGROUND_CORS_PROXY_DISABLE_RATE_LIMIT') || + !PLAYGROUND_CORS_PROXY_DISABLE_RATE_LIMIT + ) ) { http_response_code(503); echo "Server needs to configure rate-limiting."; @@ -107,7 +107,7 @@ [ 'Cookie', 'Authorization', - 'Host' + 'Host', ] ); curl_setopt( @@ -166,9 +166,19 @@ function( ); header('Location: ' . $newLocation, true); } else if ( + // Safari fails with "Cannot connect to the server" if we let + // the HTTP/2 line be relayed. This proxy doesn't support HTTP/2, + // so let's not allow the HTTP line to explicitly pass through. + // PHP already provides the HTTP version in the response code anyway. + stripos($header, 'HTTP/') !== 0 && stripos($header, 'Set-Cookie:') !== 0 && stripos($header, 'Authorization:') !== 0 && - stripos($header, 'Cache-Control:') !== 0 + stripos($header, 'Cache-Control:') !== 0 && + // The browser won't accept multiple values for these headers. + stripos($header, 'Access-Control-Allow-Origin:') !== 0 && + stripos($header, 'Access-Control-Allow-Credentials:') !== 0 && + stripos($header, 'Access-Control-Allow-Methods:') !== 0 && + stripos($header, 'Access-Control-Allow-Headers:') !== 0 ) { header($header, false); } diff --git a/packages/playground/php-cors-proxy/project.json b/packages/playground/php-cors-proxy/project.json index f4b07ef08a..3d455c81a1 100644 --- a/packages/playground/php-cors-proxy/project.json +++ b/packages/playground/php-cors-proxy/project.json @@ -7,7 +7,9 @@ "start": { "executor": "nx:run-commands", "options": { - "commands": ["php -S 127.0.0.1:5263"], + "commands": [ + "PLAYGROUND_CORS_PROXY_DISABLE_RATE_LIMIT=true php -S 127.0.0.1:5263" + ], "cwd": "packages/playground/php-cors-proxy" } }, diff --git a/packages/playground/remote/src/lib/playground-client.ts b/packages/playground/remote/src/lib/playground-client.ts index 22b36e26c8..7460286b26 100644 --- a/packages/playground/remote/src/lib/playground-client.ts +++ b/packages/playground/remote/src/lib/playground-client.ts @@ -2,7 +2,7 @@ * Imports required for the Playground Client. */ import { ProgressReceiver } from '@php-wasm/progress'; -import { UniversalPHP } from '@php-wasm/universal'; +import { MessageListener, UniversalPHP } from '@php-wasm/universal'; import { RemoteAPI, SyncProgressCallback } from '@php-wasm/web'; import { ProgressBarOptions } from './progress-bar'; import type { @@ -59,7 +59,7 @@ export interface WebClientMixin extends ProgressReceiver { hasCachedStaticFilesRemovedFromMinifiedBuild: PlaygroundWorkerEndpoint['hasCachedStaticFilesRemovedFromMinifiedBuild']; /** @inheritDoc @php-wasm/universal!UniversalPHP.onMessage */ - onMessage: PlaygroundWorkerEndpoint['onMessage']; + onMessage(listener: MessageListener): Promise<() => Promise>; mountOpfs( options: MountDescriptor, diff --git a/packages/playground/remote/src/lib/worker-thread.ts b/packages/playground/remote/src/lib/worker-thread.ts index 8bdddd346e..8969854d9d 100644 --- a/packages/playground/remote/src/lib/worker-thread.ts +++ b/packages/playground/remote/src/lib/worker-thread.ts @@ -75,6 +75,7 @@ export type WorkerBootOptions = { withNetworking: boolean; mounts?: Array; shouldInstallWordPress?: boolean; + corsProxyUrl?: string; }; /** @inheritDoc PHPClient */ @@ -168,6 +169,7 @@ export class PlaygroundWorkerEndpoint extends PHPWorker { sapiName = 'cli', withNetworking = false, shouldInstallWordPress = true, + corsProxyUrl, }: WorkerBootOptions) { if (this.booted) { throw new Error('Playground already booted'); @@ -262,6 +264,7 @@ export class PlaygroundWorkerEndpoint extends PHPWorker { }); tcpOverFetch = { CAroot, + corsProxyUrl, }; } else { phpIniEntries['allow_url_fopen'] = '0'; diff --git a/packages/playground/website/playwright/e2e/blueprints.spec.ts b/packages/playground/website/playwright/e2e/blueprints.spec.ts index 18a3a0a902..45eadef70d 100644 --- a/packages/playground/website/playwright/e2e/blueprints.spec.ts +++ b/packages/playground/website/playwright/e2e/blueprints.spec.ts @@ -320,7 +320,7 @@ test('HTTPS requests via file_get_contents() to invalid URLs should fail', async ); }); -test('HTTPS requests via file_get_contents() to CORS-disabled URLs should fail', async ({ +test('HTTPS requests via file_get_contents() to CORS-disabled URLs should succeed thanks to the CORS proxy', async ({ website, wordpress, }) => { @@ -332,13 +332,13 @@ test('HTTPS requests via file_get_contents() to CORS-disabled URLs should fail', step: 'writeFile', path: '/wordpress/https-test.php', /** - * The URL is valid, but the server does not provide the CORS headers required for fetch() to work. + * The URL is valid, but the server does not provide the CORS headers required by fetch(). */ data: ` { diff --git a/packages/playground/website/playwright/playwright.ci.config.ts b/packages/playground/website/playwright/playwright.ci.config.ts index 00ffe8328f..a16f935e39 100644 --- a/packages/playground/website/playwright/playwright.ci.config.ts +++ b/packages/playground/website/playwright/playwright.ci.config.ts @@ -11,7 +11,8 @@ export default defineConfig({ baseURL: 'http://127.0.0.1/', }, webServer: { - command: 'npx nx run playground-website:preview:ci', + command: + 'nx run playground-php-cors-proxy:start& npx nx run playground-website:preview:ci', url: 'http://127.0.0.1/', reuseExistingServer: false, }, diff --git a/packages/playground/website/playwright/playwright.config.ts b/packages/playground/website/playwright/playwright.config.ts index 71bad3b733..9e9c1f2788 100644 --- a/packages/playground/website/playwright/playwright.config.ts +++ b/packages/playground/website/playwright/playwright.config.ts @@ -25,7 +25,7 @@ export const playwrightConfig: PlaywrightTestConfig = { }, timeout: 300000, - expect: { timeout: 30000 }, + expect: { timeout: 45000 }, /* Configure projects for major browsers */ projects: [ diff --git a/packages/playground/website/public/test-fixtures/cors-file.html b/packages/playground/website/public/test-fixtures/cors-file.html new file mode 100644 index 0000000000..9776c8e881 --- /dev/null +++ b/packages/playground/website/public/test-fixtures/cors-file.html @@ -0,0 +1,15 @@ + + + + + + CORS Test File + + +

CORS Test File

+

+ This file is used for testing CORS functionality in WordPress + Playground's E2E tests. +

+ + diff --git a/packages/playground/website/vite.config.ts b/packages/playground/website/vite.config.ts index 70c7861ac7..dd3cc7fda2 100644 --- a/packages/playground/website/vite.config.ts +++ b/packages/playground/website/vite.config.ts @@ -33,6 +33,13 @@ const proxy: CommonServerOptions['proxy'] = { const path = (filename: string) => new URL(filename, import.meta.url).pathname; export default defineConfig(({ command, mode }) => { + const corsProxyUrl = + 'CORS_PROXY_URL' in process.env + ? process.env.CORS_PROXY_URL + : mode === 'production' + ? '/cors-proxy.php?' + : 'http://127.0.0.1:5263/cors-proxy.php?'; + return { // Split traffic from this server on dev so that the iframe content and // outer content can be served from the same origin. In production it's @@ -81,11 +88,7 @@ export default defineConfig(({ command, mode }) => { virtualModule({ name: 'cors-proxy-url', content: ` - export const corsProxyUrl = '${ - mode === 'production' - ? '/cors-proxy.php' - : 'http://127.0.0.1:5263/cors-proxy.php' - }';`, + export const corsProxyUrl = ${JSON.stringify(corsProxyUrl || undefined)};`, }), // GitHub OAuth flow {