#!/usr/bin/env php S2 wp-cli.phar"php/WP_CLI/CommandWithDBObject.phpLV_rphp/WP_CLI/CommandWithMeta.phpLVAOphp/WP_CLI/CommandWithTerms.phpLV+2%php/WP_CLI/CommandWithTranslation.php,LV,!php/WP_CLI/CommandWithUpgrade.php0LV0öphp/WP_CLI/Completions.php]LV]F9Ƕphp/WP_CLI/Configurator.phpLVrphp/WP_CLI/CoreUpgrader.phpLV)'Ҷ(php/WP_CLI/DestructivePluginUpgrader.phpLV\"'php/WP_CLI/DestructiveThemeUpgrader.phpLVr (php/WP_CLI/Dispatcher/CommandFactory.php LV (M*php/WP_CLI/Dispatcher/CompositeCommand.phpLV(_"%php/WP_CLI/Dispatcher/RootCommand.phpLVZO$php/WP_CLI/Dispatcher/Subcommand.phpLV \php/WP_CLI/DocParser.phpJ LVJ y:php/WP_CLI/Fetchers/Base.phpLVy9php/WP_CLI/Fetchers/Comment.php#LV#php/WP_CLI/Fetchers/Plugin.phpLVophp/WP_CLI/Fetchers/Post.phpLVSphp/WP_CLI/Fetchers/Site.phpLV$ php/WP_CLI/Fetchers/Theme.phpLV¸php/WP_CLI/Fetchers/User.phpLV˗ php/WP_CLI/FileCache.phpLVT php/WP_CLI/Formatter.phpLVQ0php/WP_CLI/Iterators/CSV.php{LV{="php/WP_CLI/Iterators/Exception.phpRLVRY7php/WP_CLI/Iterators/Query.php LV EWphp/WP_CLI/Iterators/Table.php LV 音"php/WP_CLI/Iterators/Transform.phpLV0a+php/WP_CLI/Loggers/Base.php(LV(cmphp/WP_CLI/Loggers/Quiet.phpLVkphp/WP_CLI/Loggers/Regular.phpLVw(php/WP_CLI/NoOp.phpLV\R)php/WP_CLI/NonDestructiveCoreUpgrader.php3LV3?׶php/WP_CLI/Process.phpLV`php/commands/cache.phpLV2Iphp/commands/cap.php1 LV1 Qphp/commands/cli.php&LV&S-ophp/commands/comment.phpH+LVH+TJphp/commands/core.phpLV{php/commands/cron.php6LV66php/commands/db.phpLV Ophp/commands/eval-file.phpLVphp/commands/eval.php=LV=eLV>eg+php/commands/search-replace.php.LV.zQphp/commands/server.phpLVzphp/commands/shell.php#LV#KDphp/commands/sidebar.php-LV-aphp/commands/site.php=LV='Զphp/commands/super-admin.phpf LVf Zrphp/commands/taxonomy.php LV !ܶphp/commands/term.php2#LV2#php/commands/theme.phpGLVG6[php/commands/transient.php;LV;eT϶php/commands/user.phpQXLVQX׶php/commands/widget.php2LV2 O$php/config-spec.php LV "=nphp/dispatcher.phpQLVQ!%php/export/class-wp-export-oxymel.phpLVC$php/export/class-wp-export-query.php ,LV ,_,php/export/class-wp-export-wxr-formatter.php#LV#W6]php/export/functions.export.phpdLVd==php/export/iterators.phpzLVzLphp/export/writers.phpLV|1php/router.php LV Jphp/utils-wp.phpg!LVg!)% php/utils.phpA;LVA;"php/wp-cli.phpLVphp/wp-settings-cli.php:LV:pd%features/bootstrap/FeatureContext.php LV r&features/bootstrap/support.php3LV3features/extra/no-mail.php.LV.features/steps/given.php LV >ֶfeatures/steps/then.php5LV5v8ʶfeatures/steps/when.phpsLVs,vendor/wp-cli/php-cli-tools/http-console.phpLV>1vendor/wp-cli/php-cli-tools/lib/cli/Arguments.php,LV,$/vendor/wp-cli/php-cli-tools/lib/cli/Memoize.phpLV.vendor/wp-cli/php-cli-tools/lib/cli/Notify.phpLV,0vendor/wp-cli/php-cli-tools/lib/cli/Progress.php LV a/vendor/wp-cli/php-cli-tools/lib/cli/Streams.php!LV!WOj-vendor/wp-cli/php-cli-tools/lib/cli/Table.phpLV ,vendor/wp-cli/php-cli-tools/lib/cli/Tree.phpLV@:vendor/wp-cli/php-cli-tools/lib/cli/arguments/Argument.php LV =<vendor/wp-cli/php-cli-tools/lib/cli/arguments/HelpScreen.php LV +B%Bvendor/wp-cli/php-cli-tools/lib/cli/arguments/InvalidArguments.phpLVR7vendor/wp-cli/php-cli-tools/lib/cli/arguments/Lexer.php^ LV^ G+vendor/wp-cli/php-cli-tools/lib/cli/cli.phpPLVPkf|3vendor/wp-cli/php-cli-tools/lib/cli/notify/Dots.phpdLVdwz6vendor/wp-cli/php-cli-tools/lib/cli/notify/Spinner.phpLV8u4vendor/wp-cli/php-cli-tools/lib/cli/progress/Bar.phpLV3vendor/wp-cli/php-cli-tools/lib/cli/table/Ascii.phpLVߵ6vendor/wp-cli/php-cli-tools/lib/cli/table/Renderer.phpvLVv5vendor/wp-cli/php-cli-tools/lib/cli/table/Tabular.phpLVl2vendor/wp-cli/php-cli-tools/lib/cli/tree/Ascii.phpxLVx95vendor/wp-cli/php-cli-tools/lib/cli/tree/Markdown.phpLVT魶5vendor/wp-cli/php-cli-tools/lib/cli/tree/Renderer.phpOLVOc"Ӷ-vendor/wp-cli/php-cli-tools/lib/cli/Shell.phpV LVV d.vendor/wp-cli/php-cli-tools/lib/cli/Colors.phpLV>5$vendor/wp-cli/php-cli-tools/test.phpiLVi3M0vendor/mustache/mustache/bin/build_bootstrap.php9LV9#č=vendor/mustache/mustache/src/Mustache/Cache/AbstractCache.phpLVaO?vendor/mustache/mustache/src/Mustache/Cache/FilesystemCache.phpLV;9vendor/mustache/mustache/src/Mustache/Cache/NoopCache.phpALVAoALvendor/mustache/mustache/src/Mustache/Exception/InvalidArgumentException.phpLV+?Bvendor/mustache/mustache/src/Mustache/Exception/LogicException.phpLVQQDvendor/mustache/mustache/src/Mustache/Exception/RuntimeException.phpLVo^ͶCvendor/mustache/mustache/src/Mustache/Exception/SyntaxException.phpLVr ӶJvendor/mustache/mustache/src/Mustache/Exception/UnknownFilterException.phpLV_Jvendor/mustache/mustache/src/Mustache/Exception/UnknownHelperException.phpLVk &Lvendor/mustache/mustache/src/Mustache/Exception/UnknownTemplateException.phpLVR?<vendor/mustache/mustache/src/Mustache/Loader/ArrayLoader.phpLVǶ@vendor/mustache/mustache/src/Mustache/Loader/CascadingLoader.phpLVӈAvendor/mustache/mustache/src/Mustache/Loader/FilesystemLoader.php&LV&g =vendor/mustache/mustache/src/Mustache/Loader/InlineLoader.phpLVڙ>vendor/mustache/mustache/src/Mustache/Loader/MutableLoader.phpLVm=vendor/mustache/mustache/src/Mustache/Loader/StringLoader.phpLVwv?vendor/mustache/mustache/src/Mustache/Logger/AbstractLogger.phpv LVv ԀE=vendor/mustache/mustache/src/Mustache/Logger/StreamLogger.phpLV 4vendor/mustache/mustache/src/Mustache/Autoloader.phpLVX`Ն/vendor/mustache/mustache/src/Mustache/Cache.phpLV% X2vendor/mustache/mustache/src/Mustache/Compiler.phpVLVV3!z1vendor/mustache/mustache/src/Mustache/Context.phpLV0vendor/mustache/mustache/src/Mustache/Engine.php[LV[(3vendor/mustache/mustache/src/Mustache/Exception.phpRLVRm":vendor/mustache/mustache/src/Mustache/HelperCollection.phpLV6vendor/mustache/mustache/src/Mustache/LambdaHelper.phpLV۶0vendor/mustache/mustache/src/Mustache/Loader.php3LV3ٓ 0vendor/mustache/mustache/src/Mustache/Logger.php$ LV$ O0vendor/mustache/mustache/src/Mustache/Parser.php}*LV}*L3f2vendor/mustache/mustache/src/Mustache/Template.phpLVvm=3vendor/mustache/mustache/src/Mustache/Tokenizer.phpd)LVd)fn2vendor/rmccue/requests/bin/create_pear_package.php#LV# !쯶.vendor/rmccue/requests/examples/basic-auth.phpLVBǡ'vendor/rmccue/requests/examples/get.phpELVET _,vendor/rmccue/requests/examples/multiple.phpLVc ˶(vendor/rmccue/requests/examples/post.phpILVIӐ)vendor/rmccue/requests/examples/proxy.php(LV(ˁui+vendor/rmccue/requests/examples/session.php%LV%Md۶+vendor/rmccue/requests/library/Requests.phphLVhos0vendor/rmccue/requests/library/Requests/Auth.php*LV*KI6vendor/rmccue/requests/library/Requests/Auth/Basic.phpLV 2vendor/rmccue/requests/library/Requests/Cookie.php-LV-i6vendor/rmccue/requests/library/Requests/Cookie/Jar.phpz LVz O5vendor/rmccue/requests/library/Requests/Exception.phpLVkdJ:vendor/rmccue/requests/library/Requests/Exception/HTTP.php&LV&`>vendor/rmccue/requests/library/Requests/Exception/HTTP/400.phpLVv>vendor/rmccue/requests/library/Requests/Exception/HTTP/401.phpLV >vendor/rmccue/requests/library/Requests/Exception/HTTP/402.phpLVU>vendor/rmccue/requests/library/Requests/Exception/HTTP/403.php}LV}^%>vendor/rmccue/requests/library/Requests/Exception/HTTP/404.php}LV}k>vendor/rmccue/requests/library/Requests/Exception/HTTP/405.phpLV]>vendor/rmccue/requests/library/Requests/Exception/HTTP/406.phpLV*>vendor/rmccue/requests/library/Requests/Exception/HTTP/407.phpLVbiֶ>vendor/rmccue/requests/library/Requests/Exception/HTTP/408.phpLV&M>vendor/rmccue/requests/library/Requests/Exception/HTTP/409.phpzLVz>vendor/rmccue/requests/library/Requests/Exception/HTTP/410.phpnLVn|Z>vendor/rmccue/requests/library/Requests/Exception/HTTP/411.phpLVuI>vendor/rmccue/requests/library/Requests/Exception/HTTP/412.phpLVRUJ>vendor/rmccue/requests/library/Requests/Exception/HTTP/413.phpLV6^%>vendor/rmccue/requests/library/Requests/Exception/HTTP/414.phpLVy>vendor/rmccue/requests/library/Requests/Exception/HTTP/415.phpLV9>vendor/rmccue/requests/library/Requests/Exception/HTTP/416.phpLV1>vendor/rmccue/requests/library/Requests/Exception/HTTP/417.phpLV;>vendor/rmccue/requests/library/Requests/Exception/HTTP/418.phpLV*V>vendor/rmccue/requests/library/Requests/Exception/HTTP/428.phpLVp*>vendor/rmccue/requests/library/Requests/Exception/HTTP/429.php#LV#X>vendor/rmccue/requests/library/Requests/Exception/HTTP/431.phpLVx>vendor/rmccue/requests/library/Requests/Exception/HTTP/500.phpLV}l\>vendor/rmccue/requests/library/Requests/Exception/HTTP/501.phpLVc/>vendor/rmccue/requests/library/Requests/Exception/HTTP/502.phpLVb>vendor/rmccue/requests/library/Requests/Exception/HTTP/503.phpLV*>vendor/rmccue/requests/library/Requests/Exception/HTTP/504.phpLVm͍>vendor/rmccue/requests/library/Requests/Exception/HTTP/505.phpLV">vendor/rmccue/requests/library/Requests/Exception/HTTP/511.phpLVV+Bvendor/rmccue/requests/library/Requests/Exception/HTTP/Unknown.php+LV+DL2vendor/rmccue/requests/library/Requests/Hooker.phpLVé1vendor/rmccue/requests/library/Requests/Hooks.phpLV}$7vendor/rmccue/requests/library/Requests/IDNAEncoder.php-LV-Y๶0vendor/rmccue/requests/library/Requests/IPv6.phpLVgau/vendor/rmccue/requests/library/Requests/IRI.phpLVe1vendor/rmccue/requests/library/Requests/Proxy.php-LV-6vendor/rmccue/requests/library/Requests/Proxy/HTTP.php3 LV3 #>m4vendor/rmccue/requests/library/Requests/Response.phpLVf<vendor/rmccue/requests/library/Requests/Response/Headers.php;LV;JQ/vendor/rmccue/requests/library/Requests/SSL.phpLVGN3vendor/rmccue/requests/library/Requests/Session.phpLV|׶5vendor/rmccue/requests/library/Requests/Transport.phpLVH:vendor/rmccue/requests/library/Requests/Transport/cURL.php'LV'u^?vendor/rmccue/requests/library/Requests/Transport/fsockopen.phpC+LVC+ܛ3Mvendor/rmccue/requests/library/Requests/Utility/CaseInsensitiveDictionary.phpLVTDvendor/rmccue/requests/library/Requests/Utility/FilteredIterator.phpLVtn`'vendor/composer/autoload_namespaces.php1LV1(2ȶ!vendor/composer/autoload_psr4.php}LV}ۅt%vendor/composer/autoload_classmap.phpLVςB!vendor/composer/include_paths.phpeLVemR"vendor/composer/autoload_files.php9LV9!vendor/composer/autoload_real.phpLVTNvendor/composer/ClassLoader.php0LV0Lh)vendor/composer/semver/src/Comparator.php LV g3 <vendor/composer/semver/src/Constraint/AbstractConstraint.php LV :4vendor/composer/semver/src/Constraint/Constraint.phpDLVDՔ\L=vendor/composer/semver/src/Constraint/ConstraintInterface.phpLVf+a9vendor/composer/semver/src/Constraint/EmptyConstraint.php8LV8)Ͷ9vendor/composer/semver/src/Constraint/MultiConstraint.phpLV %vendor/composer/semver/src/Semver.php LV =nQA,vendor/composer/semver/src/VersionParser.php7GLV7Gޚ1vendor/symfony/finder/Adapter/AbstractAdapter.phpLV=Ķ5vendor/symfony/finder/Adapter/AbstractFindAdapter.php)LV) C2vendor/symfony/finder/Adapter/AdapterInterface.php LV j0vendor/symfony/finder/Adapter/BsdFindAdapter.php LV kA0vendor/symfony/finder/Adapter/GnuFindAdapter.php LV ,vendor/symfony/finder/Adapter/PhpAdapter.phpy LVy ]Ķ/vendor/symfony/finder/Comparator/Comparator.phpLV3vendor/symfony/finder/Comparator/DateComparator.phpLV 'e5vendor/symfony/finder/Comparator/NumberComparator.php LV ldJ9vendor/symfony/finder/Exception/AccessDeniedException.phpLVcW޶;vendor/symfony/finder/Exception/AdapterFailureException.phpLV>@6vendor/symfony/finder/Exception/ExceptionInterface.phpLV7Avendor/symfony/finder/Exception/OperationNotPermitedException.phpLVP@vendor/symfony/finder/Exception/ShellCommandFailureException.php9LV9/vendor/symfony/finder/Expression/Expression.php% LV% KL)vendor/symfony/finder/Expression/Glob.phpLV/ݒ*vendor/symfony/finder/Expression/Regex.phpLV;A3vendor/symfony/finder/Expression/ValueInterface.phpHLVH"vendor/symfony/finder/Glob.php LV S:vendor/symfony/finder/Iterator/DateRangeFilterIterator.phpLV'Q;vendor/symfony/finder/Iterator/DepthRangeFilterIterator.phpLV-,4vendor/symfony/finder/Iterator/FilePathsIterator.php LV ׷]9vendor/symfony/finder/Iterator/FileTypeFilterIterator.phpYLVY]%<vendor/symfony/finder/Iterator/FilecontentFilterIterator.phpLV$l9vendor/symfony/finder/Iterator/FilenameFilterIterator.phpLVJc1vendor/symfony/finder/Iterator/FilterIterator.phpLV,G=vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.phpLVW)<:vendor/symfony/finder/Iterator/SizeRangeFilterIterator.phpLV3vendor/symfony/finder/Iterator/SortableIterator.php LV pe7vendor/symfony/finder/Iterator/CustomFilterIterator.phpLVg3,Avendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php LV "5vendor/symfony/finder/Iterator/PathFilterIterator.phpLVN_=vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.phpLV:%vendor/symfony/finder/Shell/Shell.phpLVq'vendor/symfony/finder/Shell/Command.phpLV_LF%vendor/symfony/finder/SplFileInfo.php LV s1 vendor/symfony/finder/Finder.phpoTLVoTxvendor/nb/oxymel/Oxymel.php)LV)(rնvendor/nb/oxymel/OxymelTest.phpLVBLV/vendor/ramsey/array_column/src/array_column.php LV "vendor/mustangostang/spyc/Spyc.php{LV{0vendor/mustangostang/spyc/examples/yaml-dump.phpLVZiN0vendor/mustangostang/spyc/examples/yaml-load.phpLVoꆶ'vendor/mustangostang/spyc/php4/5to4.phpLVuS])templates/.editorconfigLVBݶtemplates/.travis.mustacheLVtemplates/.travis.package.ymlLV 2ntemplates/bootstrap.mustacheLV*ȞEtemplates/child_theme.mustacheLV(templates/child_theme_functions.mustacheLVR"templates/install-package-tests.shLVEtemplates/install-wp-tests.sh-LV-̿Ltemplates/load-wp-cli.featureLVQ|templates/man-params.mustacheLV{5templates/man.mustacheLVEtemplates/phpunit.xml.distALVAdA #templates/plugin-gitignore.mustacheLV¥#templates/plugin-gruntfile.mustacheLVY_"templates/plugin-packages.mustacheLV`4 templates/plugin-readme.mustacheLV߰n templates/plugin-status.mustacheLVo[5templates/plugin.mustache!LV!rtemplates/post_type.mustachemLVm/8%templates/post_type_extended.mustacheLV )Stemplates/taxonomy.mustachemLVmƦo[$templates/taxonomy_extended.mustachefLVfYtemplates/test-sample.phpLVtemplates/theme-status.mustachexLVxǁgѶtemplates/versions.mustachegLVgStemplates/wp-config.mustacheLV+߶vendor/autoload.phpLV\u Dci/behat-tags.phpfLVfZ+utils/get-package-require-from-composer.phpLVǹf<vendor/rmccue/requests/library/Requests/Transport/cacert.pem~LV~a#^VERSIONLV?Zobj_id_key ] ); $obj_id = $callback( $assoc_args ); if ( is_wp_error( $obj_id ) ) { \WP_CLI::error( $obj_id ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) \WP_CLI::line( $obj_id ); else \WP_CLI::success( "Created $this->obj_type $obj_id." ); } /** * Update a given database object. * Exits with status. * * @param array $args Collection of one or more object ids to update. * @param array $assoc_args Fields => values to update on each object. * @param string $callback Function used to update object. */ protected function _update( $args, $assoc_args, $callback ) { $status = 0; if ( empty( $assoc_args ) ) { \WP_CLI::error( "Need some fields to update." ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'defer-term-counting' ) ) { wp_defer_term_counting( true ); } foreach ( $args as $obj_id ) { $params = array_merge( $assoc_args, array( $this->obj_id_key => $obj_id ) ); $status = $this->success_or_failure( $this->wp_error_to_resp( $callback( $params ), "Updated $this->obj_type $obj_id." ) ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'defer-term-counting' ) ) { wp_defer_term_counting( false ); } exit( $status ); } /** * Delete a given database object. * Exits with status. * * @param array $args Collection of one or more object ids to delete. * @param array $assoc_args Any arguments needed for the callback function. * @param string $callback Function used to delete object. */ protected function _delete( $args, $assoc_args, $callback ) { $status = 0; if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'defer-term-counting' ) ) { wp_defer_term_counting( true ); } foreach ( $args as $obj_id ) { $r = $callback( $obj_id, $assoc_args ); $status = $this->success_or_failure( $r ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'defer-term-counting' ) ) { wp_defer_term_counting( false ); } exit( $status ); } /** * Format callback response to consistent format. * * @param WP_Error|true $r Response from CRUD callback. * @param string $success_msg * @return array */ protected function wp_error_to_resp( $r, $success_msg ) { if ( is_wp_error( $r ) ) return array( 'error', $r->get_error_message() ); else return array( 'success', $success_msg ); } /** * Display success or warning based on response; return proper exit code. * * @param array $r Formatted from a CRUD callback. * @return int $status */ protected function success_or_failure( $r ) { list( $type, $msg ) = $r; if ( 'success' == $type ) { \WP_CLI::success( $msg ); $status = 0; } else { \WP_CLI::warning( $msg ); $status = 1; } return $status; } /** * Get Formatter object based on supplied parameters. * * @param array $assoc_args Parameters passed to command. Determines formatting. * @return \WP_CLI\Formatter */ protected function get_formatter( &$assoc_args ) { if ( ! empty( $assoc_args['fields'] ) ) { if ( is_string( $assoc_args['fields'] ) ) { $fields = explode( ',', $assoc_args['fields'] ); } else { $fields = $assoc_args['fields']; } } else { $fields = $this->obj_fields; } return new \WP_CLI\Formatter( $assoc_args, $fields, $this->obj_type ); } /** * Given a callback, display the URL for one or more objects. * * @param array $args One or more object references. * @param string $callback Function to get URL for the object. */ protected function _url( $args, $callback ) { foreach ( $args as $obj_id ) { $object = $this->fetcher->get_check( $obj_id ); \WP_CLI::line( $callback( $object->{$this->obj_id_key} ) ); } } } * : ID for the object. * * [--keys=] * : Limit output to metadata of specific keys. * * [--fields=] * : Limit the output to specific row fields. Defaults to id,meta_key,meta_value. * * [--format=] * : Accepted values: table, csv, json, count. Default: table * * @subcommand list */ public function list_( $args, $assoc_args ) { list( $object_id ) = $args; $keys = ! empty( $assoc_args['keys'] ) ? explode( ',', $assoc_args['keys'] ) : array(); $object_id = $this->check_object_id( $object_id ); $metadata = get_metadata( $this->meta_type, $object_id ); if ( ! $metadata ) { $metadata = array(); } $items = array(); foreach( $metadata as $key => $values ) { // Skip if not requested if ( ! empty( $keys ) && ! in_array( $key, $keys ) ) { continue; } foreach( $values as $item_value ) { $item_value = maybe_unserialize( $item_value ); $items[] = (object) array( "{$this->meta_type}_id" => $object_id, 'meta_key' => $key, 'meta_value' => $item_value, ); } } if ( ! empty( $assoc_args['fields'] ) ) { $fields = explode( ',', $assoc_args['fields'] ); } else { $fields = $this->get_fields(); } $formatter = new \WP_CLI\Formatter( $assoc_args, $fields, $this->meta_type ); $formatter->display_items( $items ); } /** * Get meta field value. * * * : The ID of the object. * * * : The name of the meta field to get. * * [--format=] * : Accepted values: table, json. Default: table */ public function get( $args, $assoc_args ) { list( $object_id, $meta_key ) = $args; $object_id = $this->check_object_id( $object_id ); $value = get_metadata( $this->meta_type, $object_id, $meta_key, true ); if ( '' === $value ) die(1); WP_CLI::print_value( $value, $assoc_args ); } /** * Delete a meta field. * * * : The ID of the object. * * [] * : The name of the meta field to delete. * * [] * : The value to delete. If omitted, all rows with key will deleted. * * [--all] * : Delete all meta for the object. */ public function delete( $args, $assoc_args ) { list( $object_id ) = $args; $meta_key = ! empty( $args[1] ) ? $args[1] : ''; $meta_value = ! empty( $args[2] ) ? $args[2] : ''; if ( empty( $meta_key ) && ! Utils\get_flag_value( $assoc_args, 'all' ) ) { WP_CLI::error( 'Please specify a meta key, or use the --all flag.' ); } $object_id = $this->check_object_id( $object_id ); if ( Utils\get_flag_value( $assoc_args, 'all' ) ) { $errors = false; foreach( get_metadata( $this->meta_type, $object_id ) as $meta_key => $values ) { $success = delete_metadata( $this->meta_type, $object_id, $meta_key ); if ( $success ) { WP_CLI::log( "Deleted '{$meta_key}' custom field." ); } else { WP_CLI::warning( "Failed to delete '{$meta_key}' custom field." ); $errors = true; } } if ( $errors ) { WP_CLI::error( 'Failed to delete all custom fields.' ); } else { WP_CLI::success( 'Deleted all custom fields.' ); } } else { $success = delete_metadata( $this->meta_type, $object_id, $meta_key, $meta_value ); if ( $success ) { WP_CLI::success( "Deleted custom field." ); } else { WP_CLI::error( "Failed to delete custom field." ); } } } /** * Add a meta field. * * ## OPTIONS * * * : The ID of the object. * * * : The name of the meta field to create. * * [] * : The value of the meta field. If ommited, the value is read from STDIN. * * [--format=] * : The serialization format for the value. Default is plaintext. */ public function add( $args, $assoc_args ) { list( $object_id, $meta_key ) = $args; $meta_value = WP_CLI::get_value_from_arg_or_stdin( $args, 2 ); $meta_value = WP_CLI::read_value( $meta_value, $assoc_args ); $object_id = $this->check_object_id( $object_id ); $success = add_metadata( $this->meta_type, $object_id, $meta_key, $meta_value ); if ( $success ) { WP_CLI::success( "Added custom field." ); } else { WP_CLI::error( "Failed to add custom field." ); } } /** * Update a meta field. * * ## OPTIONS * * * : The ID of the object. * * * : The name of the meta field to update. * * [] * : The new value. If ommited, the value is read from STDIN. * * [--format=] * : The serialization format for the value. Default is plaintext. * * @alias set */ public function update( $args, $assoc_args ) { list( $object_id, $meta_key ) = $args; $meta_value = WP_CLI::get_value_from_arg_or_stdin( $args, 2 ); $meta_value = WP_CLI::read_value( $meta_value, $assoc_args ); $object_id = $this->check_object_id( $object_id ); $meta_value = sanitize_meta( $meta_key, $meta_value, $this->meta_type ); $old_value = sanitize_meta( $meta_key, get_metadata( $this->meta_type, $object_id, $meta_key, true ), $this->meta_type ); if ( $meta_value === $old_value ) { WP_CLI::success( "Value passed for custom field '$meta_key' is unchanged." ); } else { $success = update_metadata( $this->meta_type, $object_id, $meta_key, $meta_value ); if ( $success ) { WP_CLI::success( "Updated custom field '$meta_key'." ); } else { WP_CLI::error( "Failed to update custom field '$meta_key'." ); } } } /** * Get the fields for this object's meta * * @return array */ private function get_fields() { return array( "{$this->meta_type}_id", 'meta_key', 'meta_value', ); } /** * Check that the object ID exists * * @param int */ protected function check_object_id( $object_id ) { // Needs to be set in subclass return $object_id; } } * : ID for the object. * * ... * : One or more taxonomies to list. * * [--fields=] * : Limit the output to specific row fields. * * [--format=] * : Accepted values: table, csv, json, count, ids. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each term: * * * term_id * * name * * slug * * taxonomy * * These fields are optionally available: * * * term_taxonomy_id * * description * * term_group * * parent * * count * * @subcommand list */ public function list_( $args, $assoc_args ) { $defaults = array( 'format' => 'table', ); $assoc_args = array_merge( $defaults, $assoc_args ); $object_id = array_shift( $args ); $taxonomy_names = $args; $taxonomy_args = array(); $this->set_obj_id( $object_id ); foreach ( $taxonomy_names as $taxonomy ) { $this->taxonomy_exists( $taxonomy ); } if ( $assoc_args['format'] == 'ids' ) { $taxonomy_args['fields'] = 'ids'; } $items = wp_get_object_terms( $object_id, $taxonomy_names, $taxonomy_args ); $formatter = $this->get_formatter( $assoc_args ); $formatter->display_items( $items ); } /** * Remove a term. * * * : The ID of the object. * * * : The name of the taxonomy type to deleted. * * ... * : The name of the term or terms to deleted. */ public function remove( $args, $assoc_args ) { $object_id = array_shift( $args ); $taxonomy = array_shift( $args ); $terms = $args; $this->set_obj_id( $object_id ); $this->taxonomy_exists( $taxonomy ); $result = self::wp_remove_object_terms( $object_id, $terms, $taxonomy ); if ( ! is_wp_error( $result ) ) { WP_CLI::success( "Deleted term." ); } else { WP_CLI::error( "Failed to delete term." ); } } /** * Add a term. Appends to existing set of terms on the object. * * * : The ID of the object. * * * : The name of the taxonomy type to be added. * * ... * : The slug of the term or terms to be added. */ public function add( $args, $assoc_args ) { $object_id = array_shift( $args ); $taxonomy = array_shift( $args ); $terms = $args; $this->set_obj_id( $object_id ); $this->taxonomy_exists( $taxonomy ); $result = wp_set_object_terms( $object_id, $terms, $taxonomy, true ); if ( ! is_wp_error( $result ) ) { WP_CLI::success( "Added term." ); } else { WP_CLI::error( "Failed to add term." ); } } /** * Set terms. Replaces existing terms on the object. * * * : The ID of the object. * * * : The name of the taxonomy type to be updated. * * ... * : The slug of the term or terms to be updated. */ public function set( $args, $assoc_args ) { $object_id = array_shift( $args ); $taxonomy = array_shift( $args ); $terms = $args; $this->set_obj_id( $object_id ); $this->taxonomy_exists( $taxonomy ); $result = wp_set_object_terms( $object_id, $terms, $taxonomy, false ); if ( ! is_wp_error( $result ) ) { WP_CLI::success( "Set terms." ); } else { WP_CLI::error( "Failed to set terms." ); } } /** * Remove term(s) associated with a given object. * * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $object_id The ID of the object from which the terms will be removed. * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to remove. * @param array|string $taxonomy Taxonomy name. * @return bool|WP_Error True on success, false or WP_Error on failure. */ private static function wp_remove_object_terms( $object_id, $terms, $taxonomy ) { global $wpdb; // Remove notices in below 3.6 and support backwards compatibility if( function_exists( 'wp_remove_object_terms' ) ){ return wp_remove_object_terms( $object_id, $terms, $taxonomy ); } $object_id = (int) $object_id; if ( ! taxonomy_exists( $taxonomy ) ) { return new WP_Error( 'invalid_taxonomy', __( 'Invalid Taxonomy' ) ); } if ( ! is_array( $terms ) ) { $terms = array( $terms ); } $tt_ids = array(); foreach ( (array) $terms as $term ) { if ( ! strlen( trim( $term ) ) ) { continue; } if ( ! $term_info = term_exists( $term, $taxonomy ) ) { // Skip if a non-existent term ID is passed. if ( is_int( $term ) ) { continue; } } if ( is_wp_error( $term_info ) ) { return $term_info; } $tt_ids[] = $term_info['term_taxonomy_id']; } if ( $tt_ids ) { $in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'"; /** * Fires immediately before an object-term relationship is deleted. * * @since 2.9.0 * * @param int $object_id Object ID. * @param array $tt_ids An array of term taxonomy IDs. */ do_action( 'delete_term_relationships', $object_id, $tt_ids ); $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) ); /** * Fires immediately after an object-term relationship is deleted. * * @since 2.9.0 * * @param int $object_id Object ID. * @param array $tt_ids An array of term taxonomy IDs. */ do_action( 'deleted_term_relationships', $object_id, $tt_ids ); wp_update_term_count( $tt_ids, $taxonomy ); return (bool) $deleted; } return false; } /** * Check if taxonomy exists * * @param $taxonomy */ protected function taxonomy_exists( $taxonomy ) { $taxonomy_names = get_object_taxonomies( $this->get_object_type() ); if ( ! in_array( $taxonomy, $taxonomy_names ) ) { WP_CLI::error( "Invalid taxonomy {$taxonomy}." ); } } /** * Set obj_id Class variable * * @param string $obj_id */ protected function set_obj_id( $obj_id ) { $this->obj_id = $obj_id; } /** * Get obj_id Class variable * * @return string */ protected function get_obj_id() { return $this->obj_id; } /** * Get obj_type Class variable * * @return string $obj_type */ protected function get_object_type() { return $this->obj_type; } /** * Get Formatter object based on supplied parameters. * * @param array $assoc_args Parameters passed to command. Determines formatting. * * @return WP_CLI\Formatter */ protected function get_formatter( &$assoc_args ) { return new WP_CLI\Formatter( $assoc_args, $this->obj_fields, $this->obj_type ); } } ] * : Display the value of a single field * * [--=] * : Filter results by key=value pairs. * * [--fields=] * : Limit the output to specific fields. * * [--format=] * : Accepted values: table, csv, json. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each translation: * * * language * * english_name * * native_name * * status * * update * * updated * * These fields are optionally available: * * * version * * package * * @subcommand list */ public function list_( $args, $assoc_args ) { $translations = $this->get_all_languages(); $available = $this->get_installed_languages(); $updates = $this->get_translation_updates(); $current_locale = get_locale(); $translations = array_map( function( $translation ) use ( $available, $current_locale, $updates ) { $translation['status'] = ( in_array( $translation['language'], $available ) ) ? 'installed' : 'uninstalled'; if ( $current_locale == $translation['language'] ) { $translation['status'] = 'active'; } $update = wp_list_filter( $updates, array( 'language' => $translation['language'] ) ); if ( $update ) { $translation['update'] = 'available'; } else { $translation['update'] = 'none'; } return $translation; }, $translations ); foreach( $translations as $key => $translation ) { $fields = array_keys( $translation ); foreach( $fields as $field ) { if ( isset( $assoc_args[ $field ] ) && $assoc_args[ $field ] != $translation[ $field ] ) { unset( $translations[ $key ] ); } } } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_items( $translations ); } /** * Callback to sort array by a 'language' key. */ protected function sort_translations_callback( $a, $b ) { return strnatcasecmp( $a['language'], $b['language'] ); } /** * Install a given language. * * * : Language code to install. * * [--activate] * : If set, the language will be activated immediately after install. * * @subcommand install */ public function install( $args, $assoc_args ) { list( $language_code ) = $args; $available = $this->get_installed_languages(); if ( in_array( $language_code, $available ) ) { \WP_CLI::warning( "Language already installed." ); exit; } $response = $this->download_language_pack( $language_code ); if ( ! is_wp_error( $response ) ) { \WP_CLI::success( "Language installed." ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'activate' ) ) { $this->activate( array( $language_code ), array() ); } } else { \WP_CLI::error( $response ); } } /** * Updates the active translation of core, plugins, and themes. * * [--dry-run] * : Preview which translations would be updated. * * @subcommand update */ public function update( $args, $assoc_args ) { // Ignore updates for the default locale. if ( 'en_US' == get_locale() ) { \WP_CLI::success( "Translations updates are not needed for the 'English (US)' locale." ); return; } $updates = $this->get_translation_updates(); if ( empty( $updates ) ) { \WP_CLI::success( 'Translations are up to date.' ); return; } // Gets a list of all languages. $all_languages = $this->get_all_languages(); // Formats the updates list. foreach ( $updates as $update ) { if ( 'plugin' == $update->type ) { $plugin_data = array_shift( get_plugins( '/' . $update->slug ) ); $name = $plugin_data['Name']; } elseif ( 'theme' == $update->type ) { $theme_data = wp_get_theme( $update->slug ); $name = $theme_data['Name']; } else { // Core $name = 'WordPress'; } // Gets the translation data. $translation = (object) reset( wp_list_filter( $all_languages, array( 'language' => $update->language ) ) ); $update->Type = ucfirst( $update->type ); $update->Name = $name; $update->Version = $update->version; $update->Language = $translation->english_name; } // Only preview which translations would be updated. if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'dry-run' ) ) { \WP_CLI::line( sprintf( 'Available %d translations updates:', count( $updates ) ) ); \WP_CLI\Utils\format_items( 'table', $updates, array( 'Type', 'Name', 'Version', 'Language' ) ); return; } require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $upgrader = new \Language_Pack_Upgrader( new \Automatic_Upgrader_Skin() ); $results = array(); // Update translations. foreach ( $updates as $update ) { \WP_CLI::line( "Updating '{$update->Language}' translation for {$update->Name} {$update->Version}..." ); \WP_CLI::line( "Downloading translation from {$update->package}..." ); $result = $upgrader->upgrade( $update ); if ( $result ) { \WP_CLI::line( 'Translation updated successfully.' ); } else { \WP_CLI::line( 'Translation update failed.' ); } $results[] = $result; } $num_to_update = count( $updates ); $num_updated = count( array_filter( $results ) ); $line = "Updated $num_updated/$num_to_update translations."; if ( $num_to_update == $num_updated ) { \WP_CLI::success( $line ); } else if ( $num_updated > 0 ) { \WP_CLI::warning( $line ); } else { \WP_CLI::error( $line ); } } /** * Activate a given language. * * * : Language code to activate. * * @subcommand activate */ public function activate( $args, $assoc_args ) { list( $language_code ) = $args; $available = $this->get_installed_languages(); if ( ! in_array( $language_code, $available ) ) { \WP_CLI::error( "Language not installed." ); } if ( $language_code == 'en_US' ) { $language_code = ''; } update_option( 'WPLANG', $language_code ); \WP_CLI::success( "Language activated." ); } /** * Get all updates available for all translations * * @return array */ private function get_translation_updates() { $available = $this->get_installed_languages(); $func = function() use ( $available ) { return $available; }; $filters = array( 'plugins_update_check_locales', 'themes_update_check_locales' ); foreach( $filters as $filter ) { add_filter( $filter, $func ); } $this->wp_clean_update_cache(); // Clear existing update caches. wp_version_check(); // Check for Core translation updates. wp_update_themes(); // Check for Theme translation updates. wp_update_plugins(); // Check for Plugin translation updates. foreach( $filters as $filter ) { remove_filter( $filter, $func ); } $updates = wp_get_translation_updates(); // Retrieves a list of all translations updates available. return $updates; } /** * Download a language pack. * * @see wp_download_language_pack() * * @param string $download Language code to download. * @return string|WP_Error Returns the language code if successfully downloaded, or a WP_Error object on failure. */ private function download_language_pack( $download ) { $translations = $this->get_all_languages(); foreach ( $translations as $translation ) { if ( $translation['language'] === $download ) { $translation_to_load = true; break; } } if ( empty( $translation_to_load ) ) { return new \WP_Error( 'not_found', "Language '{$download}' not found." ); } $translation = (object) $translation; require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $skin = new \Automatic_Upgrader_Skin; $upgrader = new \Language_Pack_Upgrader( $skin ); $translation->type = 'core'; $result = $upgrader->upgrade( $translation, array( 'clear_update_cache' => false ) ); if ( is_wp_error( $result ) ) { return $result; } else if ( ! $result ) { return new \WP_Error( 'not_installed', "Could not install language '{$download}'." ); } return $translation->language; } /** * Return a list of installed languages. * * @return array */ protected function get_installed_languages() { $available = wp_get_installed_translations( $this->obj_type ); $available = ! empty( $available['default'] ) ? array_keys( $available['default'] ) : array(); $available[] = 'en_US'; return $available; } /** * Return a list of all languages * * @return array */ protected function get_all_languages() { require_once ABSPATH . '/wp-admin/includes/translation-install.php'; $response = translations_api( $this->obj_type ); $translations = ! empty( $response['translations'] ) ? $response['translations'] : array(); $en_us = array( 'language' => 'en_US', 'english_name' => 'English (United States)', 'native_name' => 'English (United States)', 'updated' => '', ); array_push( $translations, $en_us ); uasort( $translations, array( $this, 'sort_translations_callback' ) ); return $translations; } /** * Uninstall a given language. * * * : Language code to uninstall. * * @subcommand uninstall */ public function uninstall( $args, $assoc_args ) { global $wp_filesystem; list( $language_code ) = $args; $available = $this->get_installed_languages(); if ( ! in_array( $language_code, $available ) ) { \WP_CLI::error( "Language not installed." ); } $dir = 'core' === $this->obj_type ? '' : "/$this->obj_type"; $files = scandir( WP_LANG_DIR . $dir ); if ( ! $files ) { \WP_CLI::error( "No files found in language directory." ); } $current_locale = get_locale(); if ( $language_code === $current_locale ) { \WP_CLI::warning( "The '{$language_code}' language is active." ); exit; } // As of WP 4.0, no API for deleting a language pack WP_Filesystem(); $deleted = false; foreach ( $files as $file ) { if ( '.' === $file[0] || is_dir( $file ) ) { continue; } $extension_length = strlen( $language_code ) + 4; $ending = substr( $file, -$extension_length ); if ( ! in_array( $file, array( $language_code . '.po', $language_code . '.mo' ) ) && ! in_array( $ending, array( '-' . $language_code . '.po', '-' . $language_code . '.mo' ) ) ) { continue; } $deleted = $wp_filesystem->delete( WP_LANG_DIR . $dir . '/' . $file ); } if ( $deleted ) { \WP_CLI::success( "Language uninstalled." ); } else { \WP_CLI::error( "Couldn't uninstall language." ); } } /** * Get Formatter object based on supplied parameters. * * @param array $assoc_args Parameters passed to command. Determines formatting. * @return \WP_CLI\Formatter */ protected function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->obj_fields, $this->obj_type ); } /** * Replicates wp_clean_update_cache() for use in WP 4.0 */ private static function wp_clean_update_cache() { if ( function_exists( 'wp_clean_plugins_cache' ) ) { wp_clean_plugins_cache(); } else { delete_site_transient( 'update_plugins' ); } wp_clean_themes_cache(); delete_site_transient( 'update_core' ); } } =' ) ) { \WP_CLI::run_command( array( 'core', 'language', 'update' ), array( 'dry-run' => false ) ); } }, 1 ); } abstract protected function get_upgrader_class( $force ); abstract protected function get_item_list(); /** * @param array List of update candidates * @param array List of item names * @return array List of update candidates */ abstract protected function filter_item_list( $items, $args ); abstract protected function get_all_items(); abstract protected function get_status( $file ); abstract protected function status_single( $args ); abstract protected function install_from_repo( $slug, $assoc_args ); function status( $args ) { // Force WordPress to check for updates call_user_func( $this->upgrade_refresh ); if ( empty( $args ) ) { $this->status_all(); } else { $this->status_single( $args ); } } private function status_all() { $items = $this->get_all_items(); $n = count( $items ); // Not interested in the translation, just the number logic \WP_CLI::log( sprintf( _n( "%d installed {$this->item_type}:", "%d installed {$this->item_type}s:", $n ), $n ) ); $padding = $this->get_padding($items); foreach ( $items as $file => $details ) { if ( $details['update'] ) { $line = ' %yU%n'; } else { $line = ' '; } $line .= $this->format_status( $details['status'], 'short' ); $line .= " " . str_pad( $details['name'], $padding ). "%n"; if ( !empty( $details['version'] ) ) { $line .= " " . $details['version']; } \WP_CLI::line( \WP_CLI::colorize( $line ) ); } \WP_CLI::line(); $this->show_legend( $items ); } private function get_padding( $items ) { $max_len = 0; foreach ( $items as $details ) { $len = strlen( $details['name'] ); if ( $len > $max_len ) { $max_len = $len; } } return $max_len; } private function show_legend( $items ) { $statuses = array_unique( wp_list_pluck( $items, 'status' ) ); $legend_line = array(); foreach ( $statuses as $status ) { $legend_line[] = sprintf( '%s%s = %s%%n', $this->get_color( $status ), $this->map['short'][ $status ], $this->map['long'][ $status ] ); } if ( in_array( true, wp_list_pluck( $items, 'update' ) ) ) $legend_line[] = '%yU = Update Available%n'; \WP_CLI::line( 'Legend: ' . \WP_CLI::colorize( implode( ', ', $legend_line ) ) ); } function install( $args, $assoc_args ) { foreach ( $args as $slug ) { $local_or_remote_zip_file = false; $result = false; // Check if a URL to a remote or local zip has been specified if ( strpos( $slug, '://' ) !== false || ( pathinfo( $slug, PATHINFO_EXTENSION ) === 'zip' && is_file( $slug ) ) ) { $local_or_remote_zip_file = $slug; } if ( $local_or_remote_zip_file ) { // Install from local or remote zip file $file_upgrader = $this->get_upgrader( $assoc_args ); if ( $file_upgrader->install( $local_or_remote_zip_file ) ) { $slug = $file_upgrader->result['destination_name']; $result = true; } } else { // Assume a plugin/theme slug from the WordPress.org repository has been specified $result = $this->install_from_repo( $slug, $assoc_args ); if ( is_wp_error( $result ) ) { $key = $result->get_error_code(); if ( in_array( $result->get_error_code(), array( 'plugins_api_failed', 'themes_api_failed' ) ) && ! empty( $result->error_data[ $key ] ) && in_array( $result->error_data[ $key ], array( 'N;', 'b:0;' ) ) ) { \WP_CLI::warning( "Couldn't find '$slug' in the WordPress.org {$this->item_type} directory." ); } else { \WP_CLI::warning( "$slug: " . $result->get_error_message() ); } } } if ( $result ) { if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'activate-network' ) ) { \WP_CLI::log( "Network-activating '$slug'..." ); $this->activate( array( $slug ), array( 'network' => true ) ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'activate' ) ) { \WP_CLI::log( "Activating '$slug'..." ); $this->activate( array( $slug ) ); } } } } /** * Prepare an API response for downloading a particular version of an item. * * @param object $response wordpress.org API response * @param string $version The desired version of the package */ protected static function alter_api_response( $response, $version ) { if ( $response->version == $version ) return; // WordPress.org forces https, but still sometimes returns http // See https://twitter.com/nacin/status/512362694205140992 $response->download_link = str_replace( 'http://', 'https://', $response->download_link ); list( $link ) = explode( $response->slug, $response->download_link ); if ( false !== strpos( $response->download_link, '/theme/' ) ) $download_type = 'theme'; else if ( false !== strpos( $response->download_link, '/plugin/' ) ) $download_type = 'plugin'; else $download_type = 'plugin/theme'; if ( 'dev' == $version ) { $response->download_link = $link . $response->slug . '.zip'; $response->version = 'Development Version'; } else { $response->download_link = $link . $response->slug . '.' . $version .'.zip'; $response->version = $version; // check if the requested version exists $response = wp_remote_head( $response->download_link ); $response_code = wp_remote_retrieve_response_code( $response ); if ( 200 !== $response_code ) { \WP_CLI::error( sprintf( "Can't find the requested %s's version %s in the WordPress.org %s repository (HTTP code %d).", $download_type, $version, $download_type, $response_code ) ); } } } protected function get_upgrader( $assoc_args ) { $upgrader_class = $this->get_upgrader_class( \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ) ); return \WP_CLI\Utils\get_upgrader( $upgrader_class ); } protected function update_many( $args, $assoc_args ) { call_user_func( $this->upgrade_refresh ); if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) { \WP_CLI::error( "Please specify one or more {$this->item_type}s, or use --all." ); } $items = $this->get_item_list(); if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { $items = $this->filter_item_list( $items, $args ); } $items_to_update = wp_list_filter( $items, array( 'update' => true ) ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'dry-run' ) ) { if ( empty( $items_to_update ) ) { \WP_CLI::line( "No {$this->item_type} updates available." ); return; } \WP_CLI::line( "Available {$this->item_type} updates:" ); \WP_CLI\Utils\format_items( 'table', $items_to_update, array( 'name', 'status', 'version', 'update_version' ) ); return; } $result = array(); // Only attempt to update if there is something to update if ( !empty( $items_to_update ) ) { $cache_manager = \WP_CLI::get_http_cache_manager(); foreach ($items_to_update as $item) { $cache_manager->whitelist_package($item['update_package'], $this->item_type, $item['name'], $item['update_version']); } $upgrader = $this->get_upgrader( $assoc_args ); $result = $upgrader->bulk_upgrade( wp_list_pluck( $items_to_update, 'update_id' ) ); } // Let the user know the results. $num_to_update = count( $items_to_update ); $num_updated = count( array_filter( $result ) ); $line = "Updated $num_updated/$num_to_update {$this->item_type}s."; if ( $num_to_update == $num_updated ) { \WP_CLI::success( $line ); } else if ( $num_updated > 0 ) { \WP_CLI::warning( $line ); } else { \WP_CLI::error( $line ); } if ( $num_to_update > 0 ) { if ( ! empty( $assoc_args['format'] ) && 'summary' === $assoc_args['format'] ) { foreach( $items_to_update as $item_to_update => $info ) { $message = $result[ $info['update_id'] ] !== null ? 'updated successfully' : 'did not update'; \WP_CLI::log( "{$info['title']} {$message} from version {$info['version']} to version {$info['update_version']}" ); } } else { $status = array(); foreach($items_to_update as $item_to_update => $info) { $status[$item_to_update] = array( 'name' => $info['name'], 'old_version' => $info['version'], 'new_version' => $info['update_version'], 'status' => $result[ $info['update_id'] ] !== null ? 'Updated' : 'Error', ); } \WP_CLI\Utils\format_items( 'table', $status, array( 'name', 'old_version', 'new_version', 'status' ) ); } } } protected function _list( $_, $assoc_args ) { // Force WordPress to check for updates call_user_func( $this->upgrade_refresh ); $all_items = $this->get_all_items(); if ( !is_array( $all_items ) ) \WP_CLI::error( "No {$this->item_type}s found." ); foreach ( $all_items as $key => &$item ) { if ( empty( $item['version'] ) ) $item['version'] = ''; foreach ( $item as $field => &$value ) { if ( $value === true ) { $value = 'available'; } else if ( $value === false ) { $value = 'none'; } } foreach ( $this->obj_fields as $field ) { if ( isset( $assoc_args[$field] ) && $assoc_args[$field] != $item[$field] ) unset( $all_items[$key] ); } } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_items( $all_items ); } /** * Check whether an item has an update available or not. * * @param string $slug The plugin/theme slug * * @return bool */ protected function has_update( $slug ) { $update_list = get_site_transient( $this->upgrade_transient ); return isset( $update_list->response[ $slug ] ); } /** * Get the available update info * * @param string $slug The plugin/theme slug * * @return array|null */ protected function get_update_info( $slug ) { $update_list = get_site_transient( $this->upgrade_transient ); if ( !isset( $update_list->response[ $slug ] ) ) return null; return (array) $update_list->response[ $slug ]; } private $map = array( 'short' => array( 'inactive' => 'I', 'active' => 'A', 'active-network' => 'N', 'must-use' => 'M', ), 'long' => array( 'inactive' => 'Inactive', 'active' => 'Active', 'active-network' => 'Network Active', 'must-use' => 'Must Use', ) ); protected function format_status( $status, $format ) { return $this->get_color( $status ) . $this->map[ $format ][ $status ]; } private function get_color( $status ) { static $colors = array( 'inactive' => '', 'active' => '%g', 'active-network' => '%g', 'must-use' => '%c', ); return $colors[ $status ]; } /** * Search wordpress.org repo. * * @param array $args A arguments array containing the search term in the first element. * @param array $assoc_args Data passed in from command. */ protected function _search( $args, $assoc_args ) { $term = $args[0]; $defaults = array( 'per-page' => 10, 'fields' => array( 'name', 'slug', 'rating' ) ); $assoc_args = array_merge( $defaults, $assoc_args ); $formatter = $this->get_formatter( $assoc_args ); $api_args = array( 'per_page' => (int) $assoc_args['per-page'], 'search' => $term, ); if ( 'plugin' == $this->item_type ) { $api = plugins_api( 'query_plugins', $api_args ); } else { $api = themes_api( 'query_themes', $api_args ); } if ( is_wp_error( $api ) ) \WP_CLI::error( $api->get_error_message() . __( ' Try again' ) ); $plural = $this->item_type . 's'; if ( ! isset( $api->$plural ) ) \WP_CLI::error( __( 'API error. Try Again.' ) ); $items = $api->$plural; $count = \WP_CLI\Utils\get_flag_value( $api->info, 'results', 'unknown' ); \WP_CLI::success( sprintf( 'Showing %s of %s %s.', count( $items ), $count, $plural ) ); $formatter->display_items( $items ); } protected function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->obj_fields, $this->item_type ); } } words = explode( ' ', $line ); // first word is always `wp` array_shift( $this->words ); // last word is either empty or an incomplete subcommand $this->cur_word = end( $this->words ); $r = $this->get_command( $this->words ); if ( !is_array( $r ) ) { return; } list( $command, $args, $assoc_args ) = $r; $spec = SynopsisParser::parse( $command->get_synopsis() ); foreach ( $spec as $arg ) { if ( $arg['type'] == 'positional' && $arg['name'] == 'file' ) { $this->add( ' ' ); return; } } if ( $command->can_have_subcommands() ) { foreach ( $command->get_subcommands() as $name => $_ ) { $this->add( "$name " ); } } else { foreach ( $spec as $arg ) { if ( in_array( $arg['type'], array( 'flag', 'assoc' ) ) ) { if ( isset( $assoc_args[ $arg['name'] ] ) ) { continue; } $opt = "--{$arg['name']}"; if ( $arg['type'] == 'flag' ) { $opt .= ' '; } elseif ( !$arg['value']['optional'] ) { $opt .= '='; } $this->add( $opt ); } } } } private function get_command( $words ) { $positional_args = $assoc_args = array(); foreach ( $words as $arg ) { if ( preg_match( '|^--([^=]+)=?|', $arg, $matches ) ) { $assoc_args[ $matches[1] ] = true; } else { $positional_args[] = $arg; } } $r = \WP_CLI::get_runner()->find_command_to_run( $positional_args ); if ( !is_array( $r ) && array_pop( $positional_args ) == $this->cur_word ) { $r = \WP_CLI::get_runner()->find_command_to_run( $positional_args ); } if ( !is_array( $r ) ) { return $r; } list( $command, $args ) = $r; return array( $command, $args, $assoc_args ); } private function add( $opt ) { if ( $this->cur_word !== '' ) { if ( strpos( $opt, $this->cur_word ) !== 0 ) { return; } } $this->opts[] = $opt; } public function render() { foreach ( $this->opts as $opt ) { \WP_CLI::line( $opt ); } } } spec = include $path; $defaults = array( 'runtime' => false, 'file' => false, 'synopsis' => '', 'default' => null, 'multiple' => false, ); foreach ( $this->spec as $key => &$details ) { $details = array_merge( $defaults, $details ); $this->config[ $key ] = $details['default']; } } /** * Get declared configuration values as an array. * * @return array */ function to_array() { return array( $this->config, $this->extra_config ); } /** * Get configuration specification, i.e. list of accepted keys. * * @return array */ function get_spec() { return $this->spec; } /** * Splits a list of arguments into positional, associative and config. * * @param array(string) * @return array(array) */ public function parse_args( $arguments ) { list( $positional_args, $mixed_args ) = self::extract_assoc( $arguments ); list( $assoc_args, $runtime_config ) = $this->unmix_assoc_args( $mixed_args ); return array( $positional_args, $assoc_args, $runtime_config ); } /** * Splits positional args from associative args. * * @param array * @return array(array) */ public static function extract_assoc( $arguments ) { $positional_args = $assoc_args = array(); foreach ( $arguments as $arg ) { if ( preg_match( '|^--no-([^=]+)$|', $arg, $matches ) ) { $assoc_args[] = array( $matches[1], false ); } elseif ( preg_match( '|^--([^=]+)$|', $arg, $matches ) ) { $assoc_args[] = array( $matches[1], true ); } elseif ( preg_match( '|^--([^=]+)=(.*)|s', $arg, $matches ) ) { $assoc_args[] = array( $matches[1], $matches[2] ); } else { $positional_args[] = $arg; } } return array( $positional_args, $assoc_args ); } /** * Separate runtime parameters from command-specific parameters. * * @param array $mixed_args * @return array */ private function unmix_assoc_args( $mixed_args ) { $assoc_args = $runtime_config = array(); foreach ( $mixed_args as $tmp ) { list( $key, $value ) = $tmp; if ( isset( $this->spec[ $key ] ) && $this->spec[ $key ]['runtime'] !== false ) { $details = $this->spec[ $key ]; if ( isset( $details['deprecated'] ) ) { fwrite( STDERR, "WP-CLI: The --{$key} global parameter is deprecated. {$details['deprecated']}\n" ); } if ( $details['multiple'] ) { $runtime_config[ $key ][] = $value; } else { $runtime_config[ $key ] = $value; } } else { $assoc_args[ $key ] = $value; } } return array( $assoc_args, $runtime_config ); } /** * Load a YAML file of parameters into scope. * * @param string $path Path to YAML file. */ public function merge_yml( $path ) { foreach ( self::load_yml( $path ) as $key => $value ) { if ( !isset( $this->spec[ $key ] ) || false === $this->spec[ $key ]['file'] ) { $this->extra_config[ $key ] = $value; } elseif ( $this->spec[ $key ]['multiple'] ) { self::arrayify( $value ); $this->config[ $key ] = array_merge( $this->config[ $key ], $value ); } else { $this->config[ $key ] = $value; } } } /** * Merge an array of values into the configurator config. * * @param array $config */ public function merge_array( $config ) { foreach ( $this->spec as $key => $details ) { if ( false !== $details['runtime'] && isset( $config[ $key ] ) ) { $value = $config[ $key ]; if ( $details['multiple'] ) { self::arrayify( $value ); $this->config[ $key ] = array_merge( $this->config[ $key ], $value ); } else { $this->config[ $key ] = $value; } } } } /** * Load values from a YAML file. * * @param string $yml_file Path to the YAML file * @return array $config Declared configuration values */ private static function load_yml( $yml_file ) { if ( !$yml_file ) return array(); $config = spyc_load_file( $yml_file ); // Make sure config-file-relative paths are made absolute. $yml_file_dir = dirname( $yml_file ); if ( isset( $config['path'] ) ) self::absolutize( $config['path'], $yml_file_dir ); if ( isset( $config['require'] ) ) { self::arrayify( $config['require'] ); foreach ( $config['require'] as &$path ) { self::absolutize( $path, $yml_file_dir ); } } return $config; } /** * Conform a variable to an array. * * @param mixed $val A string or an array */ private static function arrayify( &$val ) { if ( !is_array( $val ) ) { $val = array( $val ); } } /** * Make a path absolute. * * @param string $path Path to file. * @param string $base Base path to prepend. */ private static function absolutize( &$path, $base ) { if ( !empty( $path ) && !\WP_CLI\Utils\is_path_absolute( $path ) ) { $path = $base . DIRECTORY_SEPARATOR . $path; } } } strings['no_package'] ); $filename = pathinfo( $package, PATHINFO_FILENAME ); $ext = pathinfo( $package, PATHINFO_EXTENSION ); $temp = \WP_CLI\Utils\get_temp_dir() . uniqid('wp_') . '.' . $ext; $cache = WP_CLI::get_cache(); $update = $GLOBALS['wp_cli_update_obj']; $cache_key = "core/{$filename}-{$update->locale}.{$ext}"; $cache_file = $cache->has( $cache_key ); if ( $cache_file ) { WP_CLI::log( "Using cached file '$cache_file'..." ); copy( $cache_file, $temp ); return $temp; } else { // We need to use a temporary file because piping from cURL to tar is flaky // on MinGW (and probably in other environments too). $temp = sys_get_temp_dir() . '/' . uniqid('wp_') . '.' . $ext; $headers = array('Accept' => 'application/json'); $options = array( 'timeout' => 600, // 10 minutes ought to be enough for everybody 'filename' => $temp ); $this->skin->feedback( 'downloading_package', $package ); Utils\http_request( 'GET', $package, null, $headers, $options ); $cache->import( $cache_key, $temp ); return $temp; } } } hasMethod( '__invoke' ) ) { $command = self::create_subcommand( $parent, $name, $reflection->name, $reflection->getMethod( '__invoke' ) ); } else { $command = self::create_composite_command( $parent, $name, $reflection ); } return $command; } /** * Create a new Subcommand instance. * * @param mixed $parent The new command's parent Composite command * @param string $name Represents how the command should be invoked * @param string $class A subclass of WP_CLI_Command * @param string $method Class method to be called upon invocation. */ private static function create_subcommand( $parent, $name, $class_name, $method ) { $docparser = new \WP_CLI\DocParser( $method->getDocComment() ); if ( !$name ) $name = $docparser->get_tag( 'subcommand' ); if ( !$name ) $name = $method->name; $method_name = $method->name; $when_invoked = function ( $args, $assoc_args ) use ( $class_name, $method_name ) { call_user_func( array( new $class_name, $method_name ), $args, $assoc_args ); }; return new Subcommand( $parent, $name, $docparser, $when_invoked ); } /** * Create a new Composite command instance. * * @param mixed $parent The new command's parent Root or Composite command * @param string $name Represents how the command should be invoked * @param ReflectionClass $reflection */ private static function create_composite_command( $parent, $name, $reflection ) { $docparser = new \WP_CLI\DocParser( $reflection->getDocComment() ); $container = new CompositeCommand( $parent, $name, $docparser ); foreach ( $reflection->getMethods() as $method ) { if ( !self::is_good_method( $method ) ) continue; $subcommand = self::create_subcommand( $container, false, $reflection->name, $method ); $subcommand_name = $subcommand->get_name(); $container->add_subcommand( $subcommand_name, $subcommand ); } return $container; } /** * Check whether a method is actually callable. * * @param ReflectionMethod $method * @return bool */ private static function is_good_method( $method ) { return $method->isPublic() && !$method->isStatic() && 0 !== strpos( $method->getName(), '__' ); } } parent = $parent; $this->name = $name; $this->shortdesc = $docparser->get_shortdesc(); $this->longdesc = $docparser->get_longdesc(); $this->longdesc .= $this->get_global_params(); $this->docparser = $docparser; $when_to_invoke = $docparser->get_tag( 'when' ); if ( $when_to_invoke ) { \WP_CLI::get_runner()->register_early_invoke( $when_to_invoke, $this ); } } /** * Get the parent composite (or root) command * * @return mixed */ public function get_parent() { return $this->parent; } /** * Add a named subcommand to this composite command's * set of contained subcommands. * * @param string $name Represents how subcommand should be invoked * @param \WP_CLI\Dispatcher\Subcommand */ public function add_subcommand( $name, $command ) { $this->subcommands[ $name ] = $command; } /** * Composite commands always contain subcommands. * * @return true */ public function can_have_subcommands() { return true; } /** * Get the subcommands contained by this composite * command. * * @return array */ public function get_subcommands() { ksort( $this->subcommands ); return $this->subcommands; } /** * Get the name of this composite command. * * @return string */ public function get_name() { return $this->name; } /** * Get the short description for this composite * command. * * @return string */ public function get_shortdesc() { return $this->shortdesc; } /** * Get the long description for this composite * command. * * @return string */ public function get_longdesc() { return $this->longdesc; } /** * Get the synopsis for this composite command. * As a collection of subcommands, the composite * command is only intended to invoke those * subcommands. * * @return string */ public function get_synopsis() { return ''; } /** * Get the usage for this composite command. * * @return string */ public function get_usage( $prefix ) { return sprintf( "%s%s %s", $prefix, implode( ' ', get_path( $this ) ), $this->get_synopsis() ); } /** * Show the usage for all subcommands contained * by the composite command. */ public function show_usage() { $methods = $this->get_subcommands(); $i = 0; foreach ( $methods as $name => $subcommand ) { $prefix = ( 0 == $i++ ) ? 'usage: ' : ' or: '; if ( \WP_CLI::get_runner()->is_command_disabled( $subcommand ) ) { continue; } \WP_CLI::line( $subcommand->get_usage( $prefix ) ); } $cmd_name = implode( ' ', array_slice( get_path( $this ), 1 ) ); \WP_CLI::line(); \WP_CLI::line( "See 'wp help $cmd_name ' for more information on a specific command." ); } /** * When a composite command is invoked, it shows usage * docs for its subcommands. * * @param array $args * @param array $assoc_args * @param array $extra_args */ public function invoke( $args, $assoc_args, $extra_args ) { $this->show_usage(); } /** * Given supplied arguments, find a contained * subcommand * * @param array $args * @return \WP_CLI\Dispatcher\Subcommand|false */ public function find_subcommand( &$args ) { $name = array_shift( $args ); $subcommands = $this->get_subcommands(); if ( !isset( $subcommands[ $name ] ) ) { $aliases = self::get_aliases( $subcommands ); if ( isset( $aliases[ $name ] ) ) { $name = $aliases[ $name ]; } } if ( !isset( $subcommands[ $name ] ) ) return false; return $subcommands[ $name ]; } /** * Get any registered aliases for this composite command's * subcommands. * * @param array $subcommands * @return array */ private static function get_aliases( $subcommands ) { $aliases = array(); foreach ( $subcommands as $name => $subcommand ) { $alias = $subcommand->get_alias(); if ( $alias ) $aliases[ $alias ] = $name; } return $aliases; } /** * Composite commands can only be known by one name. * * @return false */ public function get_alias() { return false; } /*** * Get the list of global parameters * * @param string $root_command whether to include or not root command specific description * @return string */ protected function get_global_params( $root_command = false ) { $binding = array(); $binding['root_command'] = $root_command; if (! $this->can_have_subcommands() || ( is_object( $this->parent ) && get_class( $this->parent ) == 'WP_CLI\Dispatcher\CompositeCommand' )) { $binding['is_subcommand'] = true; } foreach ( \WP_CLI::get_configurator()->get_spec() as $key => $details ) { if ( false === $details['runtime'] ) continue; if ( isset( $details['deprecated'] ) ) continue; if ( isset( $details['hidden'] ) ) continue; if ( true === $details['runtime'] ) $synopsis = "--[no-]$key"; else $synopsis = "--$key" . $details['runtime']; $binding['parameters'][] = array( 'synopsis' => $synopsis, 'desc' => $details['desc'] ); } return Utils\mustache_render( 'man-params.mustache', $binding ); } } parent = false; $this->name = 'wp'; $this->shortdesc = 'Manage WordPress through the command-line.'; } /** * Get the human-readable long description. * * @return string */ public function get_longdesc() { return $this->get_global_params( true ); } /** * Find a subcommand registered on the root * command. * * @param array $args * @return \WP_CLI\Dispatcher\Subcommand|false */ public function find_subcommand( &$args ) { $command = array_shift( $args ); Utils\load_command( $command ); if ( !isset( $this->subcommands[ $command ] ) ) { return false; } return $this->subcommands[ $command ]; } /** * Get all registered subcommands. * * @return array */ public function get_subcommands() { Utils\load_all_commands(); return parent::get_subcommands(); } } when_invoked = $when_invoked; $this->alias = $docparser->get_tag( 'alias' ); $this->synopsis = $docparser->get_synopsis(); if ( !$this->synopsis && $this->longdesc ) { $this->synopsis = self::extract_synopsis( $this->longdesc ); } } /** * Extract the synopsis from PHPdoc string. * * @param string $longdesc Command docs via PHPdoc * @return string */ private static function extract_synopsis( $longdesc ) { preg_match_all( '/(.+?)[\r\n]+:/', $longdesc, $matches ); return implode( ' ', $matches[1] ); } /** * Subcommands can't have subcommands because they * represent code to be executed. * * @return bool */ function can_have_subcommands() { return false; } /** * Get the synopsis string for this subcommand. * A synopsis defines what runtime arguments are * expected, useful to humans and argument validation. * * @return string */ function get_synopsis() { return $this->synopsis; } /** * If an alias is set, grant access to it. * Aliases permit subcommands to be instantiated * with a secondary identity. * * @return string */ function get_alias() { return $this->alias; } /** * Print the usage details to the end user. * * @param string $prefix */ function show_usage( $prefix = 'usage: ' ) { \WP_CLI::line( $this->get_usage( $prefix ) ); } /** * Get the usage of the subcommand as a formatted string. * * @param string $prefix * @return string */ function get_usage( $prefix ) { return sprintf( "%s%s %s", $prefix, implode( ' ', get_path( $this ) ), $this->get_synopsis() ); } /** * Wrapper for CLI Tools' prompt() method. * * @param string $question * @param string $default * @return string|false */ private function prompt( $question, $default ) { $question .= ': '; if ( function_exists( 'readline' ) ) { return readline( $question ); } else { echo $question; return stream_get_line( STDIN, 1024, PHP_EOL ); } } /** * Interactively prompt the user for input * based on defined synopsis and passed arguments. * * @param array $args * @param array $assoc_args * @return array */ private function prompt_args( $args, $assoc_args ) { $synopsis = $this->get_synopsis(); if ( ! $synopsis ) return array( $args, $assoc_args ); $spec = array_filter( \WP_CLI\SynopsisParser::parse( $synopsis ), function( $spec_arg ) { return in_array( $spec_arg['type'], array( 'generic', 'positional', 'assoc', 'flag' ) ); }); $spec = array_values( $spec ); // 'positional' arguments are positional (aka zero-indexed) // so $args needs to be reset before prompting for new arguments $args = array(); foreach( $spec as $key => $spec_arg ) { $current_prompt = ( $key + 1 ) . '/' . count( $spec ) . ' '; $default = ( $spec_arg['optional'] ) ? '' : false; // 'generic' permits arbitrary key=value (e.g. [--=] ) if ( 'generic' == $spec_arg['type'] ) { list( $key_token, $value_token ) = explode( '=', $spec_arg['token'] ); $repeat = false; do { if ( ! $repeat ) $key_prompt = $current_prompt . $key_token; else $key_prompt = str_repeat( " ", strlen( $current_prompt ) ) . $key_token; $key = $this->prompt( $key_prompt, $default ); if ( false === $key ) return array( $args, $assoc_args ); if ( $key ) { $key_prompt_count = strlen( $key_prompt ) - strlen( $value_token ) - 1; $value_prompt = str_repeat( " ", $key_prompt_count ) . '=' . $value_token; $value = $this->prompt( $value_prompt, $default ); if ( false === $value ) return array( $args, $assoc_args ); $assoc_args[$key] = $value; $repeat = true; $required = false; } else { $repeat = false; } } while( $required || $repeat ); } else { $prompt = $current_prompt . $spec_arg['token']; if ( 'flag' == $spec_arg['type'] ) $prompt .= ' (Y/n)'; $response = $this->prompt( $prompt, $default ); if ( false === $response ) return array( $args, $assoc_args ); if ( $response ) { switch ( $spec_arg['type'] ) { case 'positional': if ( $spec_arg['repeating'] ) $response = explode( ' ', $response ); else $response = array( $response ); $args = array_merge( $args, $response ); break; case 'assoc': $assoc_args[$spec_arg['name']] = $response; break; case 'flag': if ( 'Y' == $response ) $assoc_args[$spec_arg['name']] = true; break; } } } } return array( $args, $assoc_args ); } /** * Validate the supplied arguments to the command. * Throws warnings or errors if arguments are missing * or invalid. * * @param array $args * @param array $assoc_args * @param array $extra_args * @return array list of invalid $assoc_args keys to unset */ private function validate_args( $args, $assoc_args, $extra_args ) { $synopsis = $this->get_synopsis(); if ( !$synopsis ) return array(); $validator = new \WP_CLI\SynopsisValidator( $synopsis ); $cmd_path = implode( ' ', get_path( $this ) ); foreach ( $validator->get_unknown() as $token ) { \WP_CLI::warning( sprintf( "The `%s` command has an invalid synopsis part: %s", $cmd_path, $token ) ); } if ( !$validator->enough_positionals( $args ) ) { $this->show_usage(); exit(1); } $unknown_positionals = $validator->unknown_positionals( $args ); if ( !empty( $unknown_positionals ) ) { \WP_CLI::error( 'Too many positional arguments: ' . implode( ' ', $unknown_positionals ) ); } list( $errors, $to_unset ) = $validator->validate_assoc( array_merge( \WP_CLI::get_config(), $extra_args, $assoc_args ) ); if ( $this->name != 'help' ) { foreach ( $validator->unknown_assoc( $assoc_args ) as $key ) { $errors['fatal'][] = "unknown --$key parameter"; } } if ( !empty( $errors['fatal'] ) ) { $out = 'Parameter errors:'; foreach ( $errors['fatal'] as $key => $error ) { $out .= "\n {$error}"; if ( $desc = $this->docparser->get_param_desc( $key ) ) { $out .= " ({$desc})"; } } \WP_CLI::error( $out ); } array_map( '\\WP_CLI::warning', $errors['warning'] ); return $to_unset; } /** * Invoke the subcommand with the supplied arguments. * Given a --prompt argument, interactively request input * from the end user. * * @param array $args * @param array $assoc_args */ public function invoke( $args, $assoc_args, $extra_args ) { if ( \WP_CLI::get_config( 'prompt' ) ) list( $args, $assoc_args ) = $this->prompt_args( $args, $assoc_args ); $to_unset = $this->validate_args( $args, $assoc_args, $extra_args ); foreach ( $to_unset as $key ) { unset( $assoc_args[ $key ] ); } $path = get_path( $this->get_parent() ); \WP_CLI::do_hook( 'before_invoke:' . implode( ' ', array_slice( $path, 1 ) ) ); call_user_func( $this->when_invoked, $args, array_merge( $extra_args, $assoc_args ) ); \WP_CLI::do_hook( 'after_invoke:' . implode( ' ', array_slice( $path, 1 ) ) ); } } docComment = self::remove_decorations( $docComment ); } /** * Remove unused cruft from PHPdoc comment. * * @param string $comment PHPdoc comment. * @return string */ private static function remove_decorations( $comment ) { $comment = preg_replace( '|^/\*\*[\r\n]+|', '', $comment ); $comment = preg_replace( '|\n[\t ]*\*/$|', '', $comment ); $comment = preg_replace( '|^[\t ]*\* ?|m', '', $comment ); return $comment; } /** * Get the command's short description (e.g. summary). * * @return string */ public function get_shortdesc() { if ( !preg_match( '|^([^@][^\n]+)\n*|', $this->docComment, $matches ) ) return ''; return $matches[1]; } /** * Get the command's full description * * @return string */ public function get_longdesc() { $shortdesc = $this->get_shortdesc(); if ( !$shortdesc ) return ''; $longdesc = substr( $this->docComment, strlen( $shortdesc ) ); $lines = array(); foreach ( explode( "\n", $longdesc ) as $line ) { if ( 0 === strpos( $line, '@' ) ) break; $lines[] = $line; } $longdesc = trim( implode( $lines, "\n" ) ); return $longdesc; } /** * Get the value for a given tag (e.g. "@alias" or "@subcommand") * * @param string $name Name for the tag, without '@' * @return string */ public function get_tag( $name ) { if ( preg_match( '|^@' . $name . '\s+([a-z-_]+)|m', $this->docComment, $matches ) ) return $matches[1]; return ''; } /** * Get the command's synopsis. * * @return string */ public function get_synopsis() { if ( !preg_match( '|^@synopsis\s+(.+)|m', $this->docComment, $matches ) ) return ''; return $matches[1]; } /** * Get the description for a given argument. * * @param string $name Argument's doc name. * @return string */ public function get_arg_desc( $name ) { if ( preg_match( "/\[?<{$name}>.+\n: (.+?)(\n|$)/", $this->docComment, $matches ) ) { return $matches[1]; } return ''; } /** * Get the description for a given parameter. * * @param string $key Parameter's key. * @return string */ public function get_param_desc( $key ) { if ( preg_match( "/\[?--{$key}=.+\n: (.+?)(\n|$)/", $this->docComment, $matches ) ) { return $matches[1]; } return ''; } } get( $arg ); if ( ! $item ) { \WP_CLI::error( sprintf( $this->msg, $arg ) ); } return $item; } /** * @param array The raw CLI arguments * @return array The list of found items */ public function get_many( $args ) { $items = array(); foreach ( $args as $arg ) { $item = $this->get( $arg ); if ( $item ) { $items[] = $item; } else { \WP_CLI::warning( sprintf( $this->msg, $arg ) ); } } return $items; } } $_ ) { if ( $file === "$name.php" || ( $name && $file === $name ) || ( dirname( $file ) === $name && $name !== '.' ) ) { return (object) compact( 'name', 'file' ); } } return false; } } _get_site( $site_id ); } /** * Get site (blog) data for a given id. * * @param int $site_id * @return bool|array False if no site found with given id, array otherwise */ private function _get_site( $site_id ) { global $wpdb; // Load site data $site = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE blog_id = %d", $site_id ) ); if ( !empty( $site ) ) { // Only care about domain and path which are set here return $site; } return false; } } exists() ) { return false; } return $theme; } } * Jordi Boggiano */ namespace WP_CLI; use Symfony\Component\Finder\Finder; /** * Reads/writes to a filesystem cache */ class FileCache { /** * @var string cache path */ protected $root; /** * @var bool */ protected $enabled = true; /** * @var int files time to live */ protected $ttl; /** * @var int max total size */ protected $maxSize; /** * @var string key allowed chars (regex class) */ protected $whitelist; /** * @param string $cacheDir location of the cache * @param int $ttl cache files default time to live (expiration) * @param int $maxSize max total cache size * @param string $whitelist List of characters that are allowed in path names (used in a regex character class) */ public function __construct( $cacheDir, $ttl, $maxSize, $whitelist = 'a-z0-9._-' ) { $this->root = rtrim( $cacheDir, '/\\' ) . '/'; $this->ttl = (int) $ttl; $this->maxSize = (int) $maxSize; $this->whitelist = $whitelist; if ( !$this->ensure_dir_exists( $this->root ) ) { $this->enabled = false; } } /** * Cache is enabled * * @return bool */ public function is_enabled() { return $this->enabled; } /** * Cache root * * @return string */ public function get_root() { return $this->root; } /** * Check if a file is in cache and return its filename * * @param string $key cache key * @param int $ttl time to live * @return bool|string filename or false */ public function has( $key, $ttl = null ) { if ( !$this->enabled ) { return false; } $filename = $this->filename( $key ); if ( !file_exists( $filename ) ) { return false; } // use ttl param or global ttl if ( $ttl === null ) { $ttl = $this->ttl; } elseif ( $this->ttl > 0 ) { $ttl = min( (int) $ttl, $this->ttl ); } else { $ttl = (int) $ttl; } // if ( $ttl > 0 && filemtime( $filename ) + $ttl < time() ) { if ( $this->ttl > 0 && $ttl >= $this->ttl ) { unlink( $filename ); } return false; } return $filename; } /** * Write to cache file * * @param string $key cache key * @param string $contents file contents * @return bool */ public function write( $key, $contents ) { $filename = $this->prepare_write( $key ); if ( $filename ) { return file_put_contents( $filename, $contents ) && touch( $filename ); } else { return false; } } /** * Read from cache file * * @param string $key cache key * @param int $ttl time to live * @return bool|string file contents or false */ public function read( $key, $ttl = null ) { $filename = $this->has( $key, $ttl ); if ( $filename ) { return file_get_contents( $filename ); } else { return false; } } /** * Copy a file into the cache * * @param string $key cache key * @param string $source source filename * @return bool */ public function import( $key, $source ) { $filename = $this->prepare_write( $key ); if ( $filename ) { return copy( $source, $filename ) && touch( $filename ); } else { return false; } } /** * Copy a file out of the cache * * @param string $key cache key * @param string $target target filename * @param int $ttl time to live * @return bool */ public function export( $key, $target, $ttl = null ) { $filename = $this->has( $key, $ttl ); if ( $filename ) { return copy( $filename, $target ); } else { return false; } } /** * Remove file from cache * * @param string $key cache key * @return bool */ public function remove( $key ) { if ( !$this->enabled ) { return false; } $filename = $this->filename( $key ); if ( file_exists( $filename ) ) { return unlink( $filename ); } else { return false; } } /** * Clean cache based on time to live and max size * * @return bool */ public function clean() { if ( !$this->enabled ) { return false; } $ttl = $this->ttl; $maxSize = $this->maxSize; // unlink expired files if ( $ttl > 0 ) { try { $expire = new \DateTime(); } catch ( \Exception $e ) { \WP_CLI::error( $e->getMessage() ); } $expire->modify( '-' . $ttl . ' seconds' ); $finder = $this->get_finder()->date( 'until ' . $expire->format( 'Y-m-d H:i:s' ) ); foreach ( $finder as $file ) { unlink( $file->getRealPath() ); } } // unlink older files if max cache size is exceeded if ( $maxSize > 0 ) { $files = array_reverse( iterator_to_array( $this->get_finder()->sortByAccessedTime()->getIterator() ) ); $total = 0; foreach ( $files as $file ) { if ( $total + $file->getSize() <= $maxSize ) { $total += $file->getSize(); } else { unlink( $file->getRealPath() ); } } } return true; } /** * Ensure directory exists * * @param string $dir directory * @return bool */ protected function ensure_dir_exists( $dir ) { if ( !is_dir( $dir ) ) { if ( file_exists( $dir ) ) { // exists and not a dir return false; } if ( !@mkdir( $dir, 0777, true ) ) { return false; } } return true; } /** * Prepare cache write * * @param string $key cache key * @return bool|string filename or false */ protected function prepare_write( $key ) { if ( !$this->enabled ) { return false; } $filename = $this->filename( $key ); if ( !$this->ensure_dir_exists( dirname( $filename ) ) ) { return false; } return $filename; } /** * Validate cache key * * @param string $key cache key * @return string relative filename */ protected function validate_key( $key ) { $url_parts = parse_url( $key ); if ( ! empty($url_parts['scheme']) ) { // is url $parts = array('misc'); $parts[] = $url_parts['scheme'] . '-' . $url_parts['host'] . ( empty( $url_parts['port'] ) ? '' : '-' . $url_parts['port'] ); $parts[] = substr($url_parts['path'], 1) . ( empty( $url_parts['query'] ) ? '' : '-' . $url_parts['query'] ); } else { $key = str_replace( '\\', '/', $key ); $parts = explode( '/', ltrim( $key ) ); } $parts = preg_replace( "#[^{$this->whitelist}]#i", '-', $parts ); return implode( '/', $parts ); } /** * Filename from key * * @param string $key * @return string filename */ protected function filename( $key ){ return $this->root . $this->validate_key( $key ); } /** * Get a Finder that iterates in cache root only the files * * @return Finder */ protected function get_finder() { return Finder::create()->in( $this->root )->files(); } } 'table', 'fields' => $fields, 'field' => null ); foreach ( array( 'format', 'fields', 'field' ) as $key ) { if ( isset( $assoc_args[ $key ] ) ) { $format_args[ $key ] = $assoc_args[ $key ]; unset( $assoc_args[ $key ] ); } } if ( ! is_array( $format_args['fields'] ) ) { $format_args['fields'] = explode( ',', $format_args['fields'] ); } $this->args = $format_args; $this->prefix = $prefix; } /** * Magic getter for arguments. * * @param string $key * @return mixed */ public function __get( $key ) { return $this->args[ $key ]; } /** * Display multiple items according to the output arguments. * * @param array $items */ public function display_items( $items ) { if ( $this->args['field'] ) { $this->show_single_field( $items, $this->args['field'] ); } else { if ( in_array( $this->args['format'], array( 'csv', 'json', 'table' ) ) ) { $item = is_array( $items ) && ! empty( $items ) ? array_shift( $items ) : false; if ( $item && ! empty( $this->args['fields'] ) ) { foreach( $this->args['fields'] as &$field ) { $field = $this->find_item_key( $item, $field ); } array_unshift( $items, $item ); } } if ( in_array( $this->args['format'], array( 'table', 'csv' ) ) ) { if ( is_object( $items ) && is_a( $items, 'Iterator' ) ) { $items = \WP_CLI\Utils\iterator_map( $items, array( $this, 'transform_item_values_to_json' ) ); } else { $items = array_map( array( $this, 'transform_item_values_to_json' ), $items ); } } $this->format( $items ); } } /** * Display a single item according to the output arguments. * * @param mixed $item */ public function display_item( $item ) { if ( isset( $this->args['field'] ) ) { $item = (object) $item; $key = $this->find_item_key( $item, $this->args['field'] ); $value = $item->$key; if ( in_array( $this->args['format'], array( 'table', 'csv' ) ) && ( is_object( $value ) || is_array( $value ) ) ) { $value = json_encode( $value ); } \WP_CLI::print_value( $value, array( 'format' => $this->args['format'] ) ); } else { $this->show_multiple_fields( $item, $this->args['format'] ); } } /** * Format items according to arguments. * * @param array $items */ private function format( $items ) { $fields = $this->args['fields']; switch ( $this->args['format'] ) { case 'count': if ( !is_array( $items ) ) { $items = iterator_to_array( $items ); } echo count( $items ); break; case 'ids': if ( !is_array( $items ) ) { $items = iterator_to_array( $items ); } echo implode( ' ', $items ); break; case 'table': self::show_table( $items, $fields ); break; case 'csv': \WP_CLI\Utils\write_csv( STDOUT, $items, $fields ); break; case 'json': $out = array(); foreach ( $items as $item ) { $out[] = \WP_CLI\Utils\pick_fields( $item, $fields ); } echo json_encode( $out ); break; default: \WP_CLI::error( 'Invalid format: ' . $this->args['format'] ); } } /** * Show a single field from a list of items. * * @param array Array of objects to show fields from * @param string The field to show */ private function show_single_field( $items, $field ) { $key = null; $values = array(); foreach ( $items as $item ) { $item = (object) $item; if ( null === $key ) { $key = $this->find_item_key( $item, $field ); } if ( 'json' == $this->args['format'] ) { $values[] = $item->$key; } else { \WP_CLI::print_value( $item->$key, array( 'format' => $this->args['format'] ) ); } } if ( 'json' == $this->args['format'] ) { echo json_encode( $values ); } } /** * Find an object's key. * If $prefix is set, a key with that prefix will be prioritized. * * @param object $item * @param string $field * @return string $key */ private function find_item_key( $item, $field ) { foreach ( array( $field, $this->prefix . '_' . $field ) as $maybe_key ) { if ( ( is_object( $item ) && ( property_exists( $item, $maybe_key ) || isset( $item->$maybe_key ) ) ) || ( is_array( $item ) && array_key_exists( $maybe_key, $item ) ) ) { $key = $maybe_key; break; } } if ( ! isset( $key ) ) { \WP_CLI::error( "Invalid field: $field." ); } return $key; } /** * Show multiple fields of an object. * * @param object|array Data to display * @param string Format to display the data in */ private function show_multiple_fields( $data, $format ) { $true_fields = array(); foreach( $this->args['fields'] as $field ) { $true_fields[] = $this->find_item_key( $data, $field ); } foreach( $data as $key => $value ) { if ( ! in_array( $key, $true_fields ) ) { if ( is_array( $data ) ) { unset( $data[ $key ] ); } else if ( is_object( $data ) ) { unset( $data->$key ); } } } switch ( $format ) { case 'table': case 'csv': $rows = $this->assoc_array_to_rows( $data ); $fields = array( 'Field', 'Value' ); if ( 'table' == $format ) { self::show_table( $rows, $fields ); } else if ( 'csv' == $format ) { \WP_CLI\Utils\write_csv( STDOUT, $rows, $fields ); } break; case 'json': \WP_CLI::print_value( $data, array( 'format' => $format ) ); break; default: \WP_CLI::error( "Invalid format: " . $format ); break; } } /** * Show items in a \cli\Table. * * @param array $items * @param array $fields */ private static function show_table( $items, $fields ) { $table = new \cli\Table(); $table->setHeaders( $fields ); foreach ( $items as $item ) { $table->addRow( array_values( \WP_CLI\Utils\pick_fields( $item, $fields ) ) ); } $table->display(); } /** * Format an associative array as a table. * * @param array $fields Fields and values to format * @return array $rows */ private function assoc_array_to_rows( $fields ) { $rows = array(); foreach ( $fields as $field => $value ) { if ( ! is_string( $value ) ) { $value = json_encode( $value ); } $rows[] = (object) array( 'Field' => $field, 'Value' => $value ); } return $rows; } /** * Transforms objects and arrays to JSON as necessary * * @param mixed $item * @return mixed */ public function transform_item_values_to_json( $item ) { foreach( $this->args['fields'] as $field ) { $true_field = $this->find_item_key( $item, $field ); $value = is_object( $item ) ? $item->$true_field : $item[ $true_field ]; if ( is_array( $value ) || is_object( $value ) ) { if ( is_object( $item ) ) { $item->$true_field = json_encode( $value ); } else if ( is_array( $item ) ) { $item[ $true_field ] = json_encode( $value ); } } } return $item; } } filePointer = fopen( $filename, 'r' ); if ( !$this->filePointer ) { \WP_CLI::error( sprintf( 'Could not open file: %s', $filename ) ); } $this->delimiter = $delimiter; } public function rewind() { rewind( $this->filePointer ); $this->columns = fgetcsv( $this->filePointer, self::ROW_SIZE, $this->delimiter ); $this->currentIndex = -1; $this->next(); } public function current() { return $this->currentElement; } public function key() { return $this->currentIndex; } public function next() { $this->currentElement = false; while ( true ) { $str = fgets( $this->filePointer ); if ( false === $str ) break; $row = str_getcsv( $str, $this->delimiter ); $element = array(); foreach ( $this->columns as $i => $key ) { if ( isset( $row[ $i ] ) ) $element[ $key ] = $row[ $i ]; } if ( !empty( $element ) ) { $this->currentElement = $element; $this->currentIndex++; break; } } } public function valid() { return is_array( $this->currentElement ); } } * foreach( new Iterators\Query( 'SELECT * FROM users', 100 ) as $user ) { * tickle( $user ); * } * * * @param string $query The query as a string. It shouldn't include any LIMIT clauses * @param number $chunk_size How many rows to retrieve at once; default value is 500 (optional) */ public function __construct( $query, $chunk_size = 500 ) { $this->query = $query; $this->count_query = preg_replace( '/^.*? FROM /', 'SELECT COUNT(*) FROM ', $query, 1, $replacements ); if ( $replacements != 1 ) $this->count_query = ''; $this->chunk_size = $chunk_size; $this->db = $GLOBALS['wpdb']; } /** * Reduces the offset when the query row count shrinks * * In cases where the iterated rows are being updated such that they will no * longer be returned by the original query, the offset must be reduced to * iterate over all remaining rows. */ private function adjust_offset_for_shrinking_result_set() { if ( empty( $this->count_query ) ) return; $row_count = $this->db->get_var( $this->count_query ); if ( $row_count < $this->row_count ) $this->offset -= $this->row_count - $row_count; $this->row_count = $row_count; } private function load_items_from_db() { $this->adjust_offset_for_shrinking_result_set(); $query = $this->query . sprintf( ' LIMIT %d OFFSET %d', $this->chunk_size, $this->offset ); $this->results = $this->db->get_results( $query ); if ( !$this->results ) { if ( $this->db->last_error ) { throw new Exception( 'Database error: ' . $this->db->last_error ); } else { return false; } } $this->offset += $this->chunk_size; return true; } function current() { return $this->results[ $this->index_in_results ]; } function key() { return $this->global_index; } function next() { $this->index_in_results++; $this->global_index++; } function rewind() { $this->results = array(); $this->global_index = 0; $this->index_in_results = 0; $this->offset = 0; $this->depleted = false; } function valid() { if ( $this->depleted ) { return false; } if ( !isset( $this->results[ $this->index_in_results ] ) ) { $items_loaded = $this->load_items_from_db(); if ( !$items_loaded ) { $this->rewind(); $this->depleted = true; return false; } $this->index_in_results = 0; } return true; } } * foreach( new Iterators\Table( array( 'table' => $wpdb->posts, 'fields' => array( 'ID', 'post_content' ) ) ) as $post ) { * count_words_for( $post->ID, $post->post_content ); * } * * * * foreach( new Iterators\Table( array( 'table' => $wpdb->posts, 'where' => 'ID = 8 OR post_status = "publish"' ) ) as $post ) { * … * } * * * * foreach( new PostIterator( array( 'table' => $wpdb->posts, 'where' => array( 'post_status' => 'publish', 'post_date_gmt BETWEEN x AND y' ) ) ) as $post ) { * … * } * * * * @param array $args Supported arguments: * table – the name of the database table * fields – an array of columns to get from the table, '*' is a valid value and the default * where – conditions for filtering rows. Supports two formats: * = string – this will be the where clause * = array – each element is treated as a condition if it's positional, or as column => value if * it's a key/value pair. In the latter case the value is automatically quoted and escaped * append - add arbitrary extra SQL */ function __construct( $args = array() ) { $defaults = array( 'fields' => '*', 'where' => array(), 'append' => '', 'table' => null, 'chunk_size' => 500 ); $table = $args['table']; $args = array_merge( $defaults, $args ); $fields = self::build_fields( $args['fields'] ); $conditions = self::build_where_conditions( $args['where'] ); $where_sql = $conditions ? " WHERE $conditions" : ''; $query = "SELECT $fields FROM $table $where_sql {$args['append']}"; parent::__construct( $query, $args['chunk_size'] ); } private static function build_fields( $fields ) { if ( '*' === $fields ) return $fields; return implode( ', ', array_map( function ($v) { return "`$v`"; }, $fields ) ); } private static function build_where_conditions( $where ) { global $wpdb; if ( is_array( $where ) ) { $conditions = array(); foreach( $where as $key => $value ) { if ( is_array( $value ) ) { $conditions[] = $key . ' IN (' . esc_sql( implode( ',', $value ) ) . ')'; } else if ( is_numeric( $key ) ) { $conditions[] = $value; } else { $conditions[] = $key . $wpdb->prepare( ' = %s', $value ); } } $where = implode( ' AND ', $conditions ); } return $where; } } transformers[] = $fn; } public function current() { $value = parent::current(); foreach ( $this->transformers as $fn ) { $value = call_user_func( $fn, $value ); } return $value; } } get_runner()->config['debug'] ) { $time = round( microtime( true ) - WP_CLI_START_MICROTIME, 3 ); $this->_line( "$message ({$time}s)", 'Debug', '%B', STDERR ); } } /** * Write a string to a resource. * * @param resource $handle Commonly STDOUT or STDERR. * @param string $str Message to write. */ protected function write( $handle, $str ) { fwrite( $handle, $str ); } /** * Output one line of message to a resource. * * @param string $message Message to write. * @param string $label Prefix message with a label. * @param string $color Colorize label with a given color. * @param resource $handle Resource to write to. Defaults to STDOUT. */ protected function _line( $message, $label, $color, $handle = STDOUT ) { $label = \cli\Colors::colorize( "$color$label:%n", $this->in_color ); $this->write( $handle, "$label $message\n" ); } } write( STDERR, \WP_CLI::colorize( "%RError:%n $message\n" ) ); } /** * Similar to error( $message ), but outputs $message in a red box * * @param array $message Message to write. */ public function error_multi_line( $message_lines ) { $message = implode( "\n", $message_lines ); $this->write( STDERR, \WP_CLI::colorize( "%RError:%n\n$message\n" ) ); $this->write( STDERR, \WP_CLI::colorize( "%R---------%n\n\n" ) ); } } in_color = $in_color; } /** * Write an informational message to STDOUT. * * @param string $message Message to write. */ public function info( $message ) { $this->write( STDOUT, $message . "\n" ); } /** * Write a success message, prefixed with "Success: ". * * @param string $message Message to write. */ public function success( $message ) { $this->_line( $message, 'Success', '%G' ); } /** * Write a warning message to STDERR, prefixed with "Warning: ". * * @param string $message Message to write. */ public function warning( $message ) { $this->_line( $message, 'Warning', '%C', STDERR ); } /** * Write an message to STDERR, prefixed with "Error: ". * * @param string $message Message to write. */ public function error( $message ) { $this->_line( $message, 'Error', '%R', STDERR ); } /** * Similar to error( $message ), but outputs $message in a red box * * @param array $message Message to write. */ public function error_multi_line( $message_lines ) { // convert tabs to four spaces, as some shells will output the tabs as variable-length $message_lines = array_map( function( $line ) { return str_replace( "\t", ' ', $line ); } , $message_lines ); $longest = max( array_map( 'strlen', $message_lines ) ); // write an empty line before the message $empty_line = \cli\Colors::colorize( '%w%1 ' . str_repeat( ' ', $longest ) . ' %n' ); $this->write( STDERR, "\n\t$empty_line\n" ); foreach ( $message_lines as $line ) { $padding = str_repeat( ' ', $longest - strlen( $line ) ); $line = \cli\Colors::colorize( "%w%1 $line $padding%n" ); $this->write( STDERR, "\t$line\n" ); } // write an empty line after the message $this->write( STDERR, "\t$empty_line\n\n" ); } } command = $command; $proc->cwd = $cwd; $proc->env = $env; return $proc; } private $command, $cwd, $env; private function __construct() {} /** * Run the command. * * @return ProcessRun */ public function run() { $cwd = $this->cwd; $descriptors = array( 0 => STDIN, 1 => array( 'pipe', 'w' ), 2 => array( 'pipe', 'w' ), ); $proc = proc_open( $this->command, $descriptors, $pipes, $cwd, $this->env ); $stdout = stream_get_contents( $pipes[1] ); fclose( $pipes[1] ); $stderr = stream_get_contents( $pipes[2] ); fclose( $pipes[2] ); return new ProcessRun( array( 'stdout' => $stdout, 'stderr' => $stderr, 'return_code' => proc_close( $proc ), 'command' => $this->command, 'cwd' => $cwd, 'env' => $this->env ) ); } /** * Run the command, but throw an Exception on error. * * @return ProcessRun */ public function run_check() { $r = $this->run(); if ( $r->return_code || !empty( $r->STDERR ) ) { throw new \RuntimeException( $r ); } return $r; } } /** * Results of an executed command. */ class ProcessRun { /** * @var array $props Properties of executed command. */ public function __construct( $props ) { foreach ( $props as $key => $value ) { $this->$key = $value; } } /** * Return properties of executed command as a string. * * @return string */ public function __toString() { $out = "$ $this->command\n"; $out .= "$this->stdout\n$this->stderr"; $out .= "cwd: $this->cwd\n"; $out .= "exit status: $this->return_code"; return $out; } } prompt = $prompt; $this->set_history_file(); } public function start() { while ( true ) { $line = $this->prompt(); if ( '' === $line ) continue; $line = rtrim( $line, ';' ) . ';'; if ( self::starts_with( self::non_expressions(), $line ) ) { eval( $line ); } else { if ( !self::starts_with( 'return', $line ) ) $line = 'return ' . $line; // Write directly to STDOUT, to sidestep any output buffers created by plugins ob_start(); var_dump( eval( $line ) ); fwrite( STDOUT, ob_get_clean() ); } } } private static function non_expressions() { return implode( '|', array( 'echo', 'global', 'unset', 'function', 'while', 'for', 'foreach', 'if', 'switch', 'include', 'include\_once', 'require', 'require\_once' ) ); } private function prompt() { $full_line = false; $done = false; do { $prompt = ( !$done && $full_line !== false ) ? '--> ' : $this->prompt; $fp = popen( self::create_prompt_cmd( $prompt, $this->history_file ), 'r' ); $line = fgets( $fp ); if ( !$line ) { break; } $line = rtrim( $line, "\n" ); if ( $line && '\\' == $line[ strlen( $line ) - 1 ] ) { $line = substr( $line, 0, -1 ); } else { $done = true; } $full_line .= $line; } while ( !$done ); if ( $full_line === false ) { return 'exit'; } return $full_line; } private static function create_prompt_cmd( $prompt, $history_path ) { $prompt = escapeshellarg( $prompt ); $history_path = escapeshellarg( $history_path ); $cmd = <<history_file = \WP_CLI\Utils\get_temp_dir() . 'wp-cli-history-' . md5( $data ); } private static function starts_with( $tokens, $line ) { return preg_match( "/^($tokens)[\(\s]+/", $line ); } } $key; } /** * Register a command for early invocation, generally before WordPress loads. * * @param string $when Named execution hook * @param WP_CLI\Dispatcher\Subcommand $command */ public function register_early_invoke( $when, $command ) { $this->_early_invoke[ $when ][] = array_slice( Dispatcher\get_path( $command ), 1 ); } /** * Perform the early invocation of a command. * * @param string $when Named execution hook */ private function do_early_invoke( $when ) { if ( !isset( $this->_early_invoke[ $when ] ) ) return; foreach ( $this->_early_invoke[ $when ] as $path ) { if ( $this->cmd_starts_with( $path ) ) { $this->_run_command(); exit; } } } /** * Get the path to the global configuration YAML file. * * @return string|false */ private function get_global_config_path() { if ( isset( $runtime_config['config'] ) ) { $config_path = $runtime_config['config']; $this->_global_config_path_debug = 'Using global config from config runtime arg: ' . $config_path; } else if ( getenv( 'WP_CLI_CONFIG_PATH' ) ) { $config_path = getenv( 'WP_CLI_CONFIG_PATH' ); $this->_global_config_path_debug = 'Using global config from WP_CLI_CONFIG_PATH env var: ' . $config_path; } else { $config_path = getenv( 'HOME' ) . '/.wp-cli/config.yml'; $this->_global_config_path_debug = 'Using default global config: ' . $config_path; } if ( is_readable( $config_path ) ) { return $config_path; } else { $this->_global_config_path_debug = 'No readable global config found'; return false; } } /** * Get the path to the project-specific configuration * YAML file. * wp-cli.local.yml takes priority over wp-cli.yml. * * @return string|false */ private function get_project_config_path() { $config_files = array( 'wp-cli.local.yml', 'wp-cli.yml' ); // Stop looking upward when we find we have emerged from a subdirectory // install into a parent install $project_config_path = Utils\find_file_upward( $config_files, getcwd(), function ( $dir ) { static $wp_load_count = 0; $wp_load_path = $dir . DIRECTORY_SEPARATOR . 'wp-load.php'; if ( file_exists( $wp_load_path ) ) { $wp_load_count += 1; } return $wp_load_count > 1; } ); if ( ! empty( $project_config_path ) ) { $this->_project_config_path_debug = 'Using project config: ' . $project_config_path; } else { $this->_project_config_path_debug = 'No project config found'; } return $project_config_path; } /** * Attempts to find the path to the WP install inside index.php * * @param string $index_path * @return string|false */ private static function extract_subdir_path( $index_path ) { $index_code = file_get_contents( $index_path ); if ( !preg_match( '|^\s*require\s*\(?\s*(.+?)/wp-blog-header\.php([\'"])|m', $index_code, $matches ) ) { return false; } $wp_path_src = $matches[1] . $matches[2]; $wp_path_src = Utils\replace_path_consts( $wp_path_src, $index_path ); $wp_path = eval( "return $wp_path_src;" ); if ( !Utils\is_path_absolute( $wp_path ) ) { $wp_path = dirname( $index_path ) . "/$wp_path"; } return $wp_path; } /** * Find the directory that contains the WordPress files. * Defaults to the current working dir. * * @return string An absolute path */ private function find_wp_root() { if ( !empty( $this->config['path'] ) ) { $path = $this->config['path']; if ( !Utils\is_path_absolute( $path ) ) $path = getcwd() . '/' . $path; return $path; } if ( $this->cmd_starts_with( array( 'core', 'download' ) ) ) { return getcwd(); } $dir = getcwd(); while ( is_readable( $dir ) ) { if ( file_exists( "$dir/wp-load.php" ) ) { return $dir; } if ( file_exists( "$dir/index.php" ) ) { if ( $path = self::extract_subdir_path( "$dir/index.php" ) ) return $path; } $parent_dir = dirname( $dir ); if ( empty($parent_dir) || $parent_dir === $dir ) { break; } $dir = $parent_dir; } } /** * Set WordPress root as a given path. * * @param string $path */ private static function set_wp_root( $path ) { define( 'ABSPATH', rtrim( $path, '/' ) . '/' ); WP_CLI::debug( 'ABSPATH defined: ' . ABSPATH ); $_SERVER['DOCUMENT_ROOT'] = realpath( $path ); } /** * Set a specific user context for WordPress. * * @param array $assoc_args */ private static function set_user( $assoc_args ) { if ( isset( $assoc_args['user'] ) ) { $fetcher = new \WP_CLI\Fetchers\User; $user = $fetcher->get_check( $assoc_args['user'] ); wp_set_current_user( $user->ID ); } else { kses_remove_filters(); } } /** * Guess which URL context WP-CLI has been invoked under. * * @param array $assoc_args * @return string|false */ private static function guess_url( $assoc_args ) { if ( isset( $assoc_args['blog'] ) ) { $assoc_args['url'] = $assoc_args['blog']; } if ( isset( $assoc_args['url'] ) ) { $url = $assoc_args['url']; if ( true === $url ) { WP_CLI::warning( 'The --url parameter expects a value.' ); } } if ( isset( $url ) ) { return $url; } return false; } private function cmd_starts_with( $prefix ) { return $prefix == array_slice( $this->arguments, 0, count( $prefix ) ); } /** * Given positional arguments, find the command to execute. * * @param array $args * @return array|string Command, args, and path on success; error message on failure */ public function find_command_to_run( $args ) { $command = \WP_CLI::get_root_command(); $cmd_path = array(); while ( !empty( $args ) && $command->can_have_subcommands() ) { $cmd_path[] = $args[0]; $full_name = implode( ' ', $cmd_path ); $subcommand = $command->find_subcommand( $args ); if ( !$subcommand ) { if ( count( $cmd_path ) > 1 ) { $child = array_pop( $cmd_path ); $parent_name = implode( ' ', $cmd_path ); return sprintf( "'%s' is not a registered subcommand of '%s'. See 'wp help %s'.", $child, $parent_name, $parent_name ); } else { return sprintf( "'%s' is not a registered wp command. See 'wp help'.", $full_name ); } } if ( $this->is_command_disabled( $subcommand ) ) { return sprintf( "The '%s' command has been disabled from the config file.", $full_name ); } $command = $subcommand; } return array( $command, $args, $cmd_path ); } /** * Find the WP-CLI command to run given arguments, * and invoke it. * * @param array $args Positional arguments including command name * @param array $assoc_args */ public function run_command( $args, $assoc_args = array() ) { $r = $this->find_command_to_run( $args ); if ( is_string( $r ) ) { WP_CLI::error( $r ); } list( $command, $final_args, $cmd_path ) = $r; $name = implode( ' ', $cmd_path ); if ( isset( $this->extra_config[ $name ] ) ) { $extra_args = $this->extra_config[ $name ]; } else { $extra_args = array(); } WP_CLI::debug( 'Running command: ' . $name ); try { $command->invoke( $final_args, $assoc_args, $extra_args ); } catch ( WP_CLI\Iterators\Exception $e ) { WP_CLI::error( $e->getMessage() ); } } private function _run_command() { $this->run_command( $this->arguments, $this->assoc_args ); } /** * Check whether a given command is disabled by the config * * @return bool */ public function is_command_disabled( $command ) { $path = implode( ' ', array_slice( \WP_CLI\Dispatcher\get_path( $command ), 1 ) ); return in_array( $path, $this->config['disabled_commands'] ); } /** * Returns wp-config.php code, skipping the loading of wp-settings.php * * @return string */ public function get_wp_config_code() { $wp_config_path = Utils\locate_wp_config(); $wp_config_code = explode( "\n", file_get_contents( $wp_config_path ) ); $found_wp_settings = false; $lines_to_run = array(); foreach ( $wp_config_code as $line ) { if ( preg_match( '/^\s*require.+wp-settings\.php/', $line ) ) { $found_wp_settings = true; continue; } $lines_to_run[] = $line; } if ( !$found_wp_settings ) { WP_CLI::error( 'Strange wp-config.php file: wp-settings.php is not loaded directly.' ); } $source = implode( "\n", $lines_to_run ); $source = Utils\replace_path_consts( $source, $wp_config_path ); return preg_replace( '|^\s*\<\?php\s*|', '', $source ); } /** * Transparently convert deprecated syntaxes * * @param array $args * @param array $assoc_args * @return array */ private static function back_compat_conversions( $args, $assoc_args ) { $top_level_aliases = array( 'sql' => 'db', 'blog' => 'site' ); if ( count( $args ) > 0 ) { foreach ( $top_level_aliases as $old => $new ) { if ( $old == $args[0] ) { $args[0] = $new; break; } } } // *-meta -> * meta if ( !empty( $args ) && preg_match( '/(post|comment|user|network)-meta/', $args[0], $matches ) ) { array_shift( $args ); array_unshift( $args, 'meta' ); array_unshift( $args, $matches[1] ); } // core (multsite-)install --admin_name= -> --admin_user= if ( count( $args ) > 0 && 'core' == $args[0] && isset( $assoc_args['admin_name'] ) ) { $assoc_args['admin_user'] = $assoc_args['admin_name']; unset( $assoc_args['admin_name'] ); } // site --site_id= -> site --network_id= if ( count( $args ) > 0 && 'site' == $args[0] && isset( $assoc_args['site_id'] ) ) { $assoc_args['network_id'] = $assoc_args['site_id']; unset( $assoc_args['site_id'] ); } // {plugin|theme} update-all -> {plugin|theme} update --all if ( count( $args ) > 1 && in_array( $args[0], array( 'plugin', 'theme' ) ) && $args[1] == 'update-all' ) { $args[1] = 'update'; $assoc_args['all'] = true; } // plugin scaffold -> scaffold plugin if ( array( 'plugin', 'scaffold' ) == array_slice( $args, 0, 2 ) ) { list( $args[0], $args[1] ) = array( $args[1], $args[0] ); } // foo --help -> help foo if ( isset( $assoc_args['help'] ) ) { array_unshift( $args, 'help' ); unset( $assoc_args['help'] ); } // {post|user} list --ids -> {post|user} list --format=ids if ( count( $args ) > 1 && in_array( $args[0], array( 'post', 'user' ) ) && $args[1] == 'list' && isset( $assoc_args['ids'] ) ) { $assoc_args['format'] = 'ids'; unset( $assoc_args['ids'] ); } // --json -> --format=json if ( isset( $assoc_args['json'] ) ) { $assoc_args['format'] = 'json'; unset( $assoc_args['json'] ); } // --{version|info} -> cli {version|info} if ( empty( $args ) ) { $special_flags = array( 'version', 'info' ); foreach ( $special_flags as $key ) { if ( isset( $assoc_args[ $key ] ) ) { $args = array( 'cli', $key ); unset( $assoc_args[ $key ] ); break; } } } // (post|site) url --> (post|site) list --*__in --field=url if ( count( $args ) >= 2 && in_array( $args[0], array( 'post', 'site' ) ) && 'url' === $args[1] ) { switch ( $args[0] ) { case 'post': $post_ids = array_slice( $args, 2 ); $args = array( 'post', 'list' ); $assoc_args['post__in'] = implode( ',', $post_ids ); $assoc_args['post_type'] = 'any'; $assoc_args['orderby'] = 'post__in'; $assoc_args['field'] = 'url'; break; case 'site': $site_ids = array_slice( $args, 2 ); $args = array( 'site', 'list' ); $assoc_args['site__in'] = implode( ',', $site_ids ); $assoc_args['field'] = 'url'; break; } } return array( $args, $assoc_args ); } /** * Whether or not the output should be rendered in color * * @return bool */ public function in_color() { return $this->colorize; } private function init_colorization() { if ( 'auto' === $this->config['color'] ) { $this->colorize = ( !\cli\Shell::isPiped() && !\WP_CLI\Utils\is_windows() ); } else { $this->colorize = $this->config['color']; } } private function init_logger() { if ( $this->config['quiet'] ) $logger = new \WP_CLI\Loggers\Quiet; else $logger = new \WP_CLI\Loggers\Regular( $this->in_color() ); WP_CLI::set_logger( $logger ); } /** * Do WordPress core files exist? * * @return bool */ private function wp_exists() { return is_readable( ABSPATH . 'wp-includes/version.php' ); } private function check_wp_version() { if ( !$this->wp_exists() ) { WP_CLI::error( "This does not seem to be a WordPress install.\n" . "Pass --path=`path/to/wordpress` or run `wp core download`." ); } include ABSPATH . 'wp-includes/version.php'; $minimum_version = '3.7'; // @codingStandardsIgnoreStart if ( version_compare( $wp_version, $minimum_version, '<' ) ) { WP_CLI::error( "WP-CLI needs WordPress $minimum_version or later to work properly. " . "The version currently installed is $wp_version.\n" . "Try running `wp core download --force`." ); } // @codingStandardsIgnoreEnd } private function init_config() { $configurator = \WP_CLI::get_configurator(); // File config { $this->global_config_path = $this->get_global_config_path(); $this->project_config_path = $this->get_project_config_path(); $configurator->merge_yml( $this->global_config_path ); $config = $configurator->to_array(); $this->_required_files['global'] = $config[0]['require']; $configurator->merge_yml( $this->project_config_path ); $config = $configurator->to_array(); $this->_required_files['project'] = $config[0]['require']; } // Runtime config and args { list( $args, $assoc_args, $runtime_config ) = $configurator->parse_args( array_slice( $GLOBALS['argv'], 1 ) ); list( $this->arguments, $this->assoc_args ) = self::back_compat_conversions( $args, $assoc_args ); $configurator->merge_array( $runtime_config ); } list( $this->config, $this->extra_config ) = $configurator->to_array(); $this->_required_files['runtime'] = $this->config['require']; } private function check_root() { if ( $this->config['allow-root'] ) return; # they're aware of the risks! if ( !function_exists( 'posix_geteuid') ) return; # posix functions not available if ( posix_geteuid() !== 0 ) return; # not root WP_CLI::error( "YIKES! It looks like you're running this as root. You probably meant to " . "run this as the user that your WordPress install exists under.\n" . "\n" . "If you REALLY mean to run this as root, we won't stop you, but just " . "bear in mind that any code on this site will then have full control of " . "your server, making it quite DANGEROUS.\n" . "\n" . "If you'd like to continue as root, please run this again, adding this " . "flag: --allow-root\n" . "\n" . "If you'd like to run it as the user that this site is under, you can " . "run the following to become the respective user:\n" . "\n" . " sudo -u USER -i -- wp \n" . "\n" ); } public function start() { $this->init_config(); $this->init_colorization(); $this->init_logger(); WP_CLI::debug( $this->_global_config_path_debug ); WP_CLI::debug( $this->_project_config_path_debug ); $this->check_root(); if ( empty( $this->arguments ) ) $this->arguments[] = 'help'; // Protect 'cli info' from most of the runtime if ( 'cli' === $this->arguments[0] && ! empty( $this->arguments[1] ) && 'info' === $this->arguments[1] ) { $this->_run_command(); exit; } // Load bundled commands early, so that they're forced to use the same // APIs as non-bundled commands. Utils\load_command( $this->arguments[0] ); if ( isset( $this->config['require'] ) ) { foreach ( $this->config['require'] as $path ) { if ( ! file_exists( $path ) ) { $context = ''; foreach( array( 'global', 'project', 'runtime' ) as $scope ) { if ( in_array( $path, $this->_required_files[ $scope ] ) ) { switch ( $scope ) { case 'global': $context = ' (from global ' . basename( $this->global_config_path ) . ')'; break; case 'project': $context = ' (from project\'s ' . basename( $this->project_config_path ) . ')'; break; case 'runtime': $context = ' (from runtime argument)'; break; } break; } } WP_CLI::error( sprintf( "Required file '%s' doesn't exist%s.", basename( $path ), $context ) ); } Utils\load_file( $path ); WP_CLI::debug( 'Required file from config: ' . $path ); } } // Show synopsis if it's a composite command. $r = $this->find_command_to_run( $this->arguments ); if ( is_array( $r ) ) { list( $command ) = $r; if ( $command->can_have_subcommands() ) { $command->show_usage(); exit; } } // Handle --path parameter self::set_wp_root( $this->find_wp_root() ); // First try at showing man page if ( 'help' === $this->arguments[0] && ( ! $this->wp_exists() || ! Utils\locate_wp_config() ) ) { $this->_run_command(); } // Handle --url parameter $url = self::guess_url( $this->config ); if ( $url ) \WP_CLI::set_url( $url ); $this->do_early_invoke( 'before_wp_load' ); $this->check_wp_version(); if ( $this->cmd_starts_with( array( 'core', 'config' ) ) ) { $this->_run_command(); exit; } if ( !Utils\locate_wp_config() ) { WP_CLI::error( "wp-config.php not found.\n" . "Either create one manually or use `wp core config`." ); } if ( $this->cmd_starts_with( array( 'db' ) ) && !$this->cmd_starts_with( array( 'db', 'tables' ) ) ) { eval( $this->get_wp_config_code() ); $this->_run_command(); exit; } if ( $this->cmd_starts_with( array( 'core', 'is-installed' ) ) ) { define( 'WP_INSTALLING', true ); } if ( count( $this->arguments ) >= 2 && $this->arguments[0] == 'core' && in_array( $this->arguments[1], array( 'install', 'multisite-install' ) ) ) { define( 'WP_INSTALLING', true ); // We really need a URL here if ( !isset( $_SERVER['HTTP_HOST'] ) ) { $url = 'http://example.com'; \WP_CLI::set_url( $url ); } if ( 'multisite-install' == $this->arguments[1] ) { // need to fake some globals to skip the checks in wp-includes/ms-settings.php $url_parts = Utils\parse_url( $url ); self::fake_current_site_blog( $url_parts ); if ( !defined( 'COOKIEHASH' ) ) { define( 'COOKIEHASH', md5( $url_parts['host'] ) ); } } } if ( $this->cmd_starts_with( array( 'import') ) ) { define( 'WP_LOAD_IMPORTERS', true ); define( 'WP_IMPORTING', true ); } if ( $this->cmd_starts_with( array( 'plugin' ) ) ) { $GLOBALS['pagenow'] = 'plugins.php'; } $this->load_wordpress(); $this->_run_command(); } /** * Load WordPress, if it hasn't already been loaded */ public function load_wordpress() { static $wp_cli_is_loaded; // Globals not explicitly globalized in WordPress global $site_id, $public, $current_site, $current_blog, $path, $shortcode_tags; if ( ! empty( $wp_cli_is_loaded ) ) { return; } $wp_cli_is_loaded = true; WP_CLI::debug( 'Begin WordPress load' ); $this->check_wp_version(); $wp_config_path = Utils\locate_wp_config(); if ( ! $wp_config_path ) { WP_CLI::error( "wp-config.php not found.\n" . "Either create one manually or use `wp core config`." ); } WP_CLI::debug( 'wp-config.php path: ' . $wp_config_path ); // Load wp-config.php code, in the global scope $wp_cli_original_defined_vars = get_defined_vars(); eval( $this->get_wp_config_code() ); foreach( get_defined_vars() as $key => $var ) { if ( array_key_exists( $key, $wp_cli_original_defined_vars ) || 'wp_cli_original_defined_vars' === $key ) { continue; } $GLOBALS[ $key ] = $var; } $this->maybe_update_url_from_domain_constant(); // Load WP-CLI utilities require WP_CLI_ROOT . '/php/utils-wp.php'; // Load Core, mu-plugins, plugins, themes etc. require WP_CLI_ROOT . '/php/wp-settings-cli.php'; // Fix memory limit. See http://core.trac.wordpress.org/ticket/14889 @ini_set( 'memory_limit', -1 ); // Load all the admin APIs, for convenience require ABSPATH . 'wp-admin/includes/admin.php'; add_filter( 'filesystem_method', function() { return 'direct'; }, 99 ); // Handle --user parameter if ( ! defined( 'WP_INSTALLING' ) ) { self::set_user( $this->config ); } WP_CLI::debug( 'Loaded WordPress' ); } private static function fake_current_site_blog( $url_parts ) { global $current_site, $current_blog; if ( !isset( $url_parts['path'] ) ) { $url_parts['path'] = '/'; } $current_site = (object) array( 'id' => 1, 'blog_id' => 1, 'domain' => $url_parts['host'], 'path' => $url_parts['path'], 'cookie_domain' => $url_parts['host'], 'site_name' => 'Fake Site', ); $current_blog = (object) array( 'blog_id' => 1, 'site_id' => 1, 'domain' => $url_parts['host'], 'path' => $url_parts['path'], 'public' => '1', 'archived' => '0', 'mature' => '0', 'spam' => '0', 'deleted' => '0', 'lang_id' => '0', ); } /** * Called after wp-config.php is eval'd, to potentially reset `--url` */ private function maybe_update_url_from_domain_constant() { if ( ! empty( $this->config['url'] ) || ! empty( $this->config['blog'] ) ) { return; } if ( defined( 'DOMAIN_CURRENT_SITE' ) ) { $url = DOMAIN_CURRENT_SITE; if ( defined( 'PATH_CURRENT_SITE' ) ) { $url .= PATH_CURRENT_SITE; } \WP_CLI::set_url( $url ); } } } from = $from; $this->to = $to; $this->recurse_objects = $recurse_objects; $this->regex = $regex; // Get the XDebug nesting level. Will be zero (no limit) if no value is set $this->max_recursion = intval( ini_get( 'xdebug.max_nesting_level' ) ); } /** * Take a serialised array and unserialise it replacing elements as needed and * unserialising any subordinate arrays and performing the replace on those too. * Ignores any serialized objects unless $recurse_objects is set to true. * * @param array|string $data The data to operate on. * @param bool $serialised Does the value of $data need to be unserialized? * * @return array The original array with all elements replaced as needed. */ function run( $data, $serialised = false ) { return $this->_run( $data, $serialised ); } /** * @param int $recursion_level Current recursion depth within the original data. * @param array $visited_data Data that has been seen in previous recursion iterations. */ private function _run( $data, $serialised, $recursion_level = 0, &$visited_data = array() ) { // some unseriliased data cannot be re-serialised eg. SimpleXMLElements try { if ( $this->recurse_objects ) { // If we've reached the maximum recursion level, short circuit if ( $this->max_recursion != 0 && $recursion_level >= $this->max_recursion ) { return $data; } if ( is_array( $data ) || is_object( $data ) ) { // If we've seen this exact object or array before, short circuit if ( in_array( $data, $visited_data, true ) ) { return $data; // Avoid infinite loops when there's a cycle } // Add this data to the list of $visited_data[] = $data; } } if ( is_string( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) { $data = $this->_run( $unserialized, true, $recursion_level + 1 ); } elseif ( is_array( $data ) ) { $keys = array_keys( $data ); foreach ( $keys as $key ) { $data[ $key ]= $this->_run( $data[$key], false, $recursion_level + 1, $visited_data ); } } elseif ( $this->recurse_objects && is_object( $data ) ) { foreach ( $data as $key => $value ) { $data->$key = $this->_run( $value, false, $recursion_level + 1, $visited_data ); } } else if ( is_string( $data ) ) { if ( $this->regex ) { $data = preg_replace( "/$this->from/", $this->to, $data ); } else { $data = str_replace( $this->from, $this->to, $data ); } } if ( $serialised ) return serialize( $data ); } catch( Exception $error ) { } return $data; } } ..." * into [ optional=>false, type=>positional, repeating=>true, name=>object-id ] */ class SynopsisParser { /** * @param string A synopsis * @return array List of parameters */ public static function parse( $synopsis ) { $tokens = array_filter( preg_split( '/[\s\t]+/', $synopsis ) ); $params = array(); foreach ( $tokens as $token ) { $param = self::classify_token( $token ); // Some types of parameters shouldn't be mandatory if ( isset( $param['optional'] ) && !$param['optional'] ) { if ( 'flag' === $param['type'] || ( 'assoc' === $param['type'] && $param['value']['optional'] ) ) { $param['type'] = 'unknown'; } } $param['token'] = $token; $params[] = $param; } return $params; } /** * Classify argument attributes based on its syntax. * * @param string $token * @return array $param */ private static function classify_token( $token ) { $param = array(); list( $param['optional'], $token ) = self::is_optional( $token ); list( $param['repeating'], $token ) = self::is_repeating( $token ); $p_name = '([a-z-_]+)'; $p_value = '([a-zA-Z-_|,]+)'; if ( '--=' === $token ) { $param['type'] = 'generic'; } elseif ( preg_match( "/^<($p_value)>$/", $token, $matches ) ) { $param['type'] = 'positional'; $param['name'] = $matches[1]; } elseif ( preg_match( "/^--(?:\\[no-\\])?$p_name/", $token, $matches ) ) { $param['name'] = $matches[1]; $value = substr( $token, strlen( $matches[0] ) ); // substr returns false <= PHP 5.6, and '' PHP 7+ if ( false === $value || '' === $value ) { $param['type'] = 'flag'; } else { $param['type'] = 'assoc'; list( $param['value']['optional'], $value ) = self::is_optional( $value ); if ( preg_match( "/^=<$p_value>$/", $value, $matches ) ) { $param['value']['name'] = $matches[1]; } else { $param = array( 'type' => 'unknown' ); } } } else { $param['type'] = 'unknown'; } return $param; } /** * An optional parameter is surrounded by square brackets. * * @param string $token * @return array */ private static function is_optional( $token ) { if ( '[' == substr( $token, 0, 1 ) && ']' == substr( $token, -1 ) ) { return array( true, substr( $token, 1, -1 ) ); } else { return array( false, $token ); } } /** * A repeating parameter is followed by an ellipsis. * * @param string $token * @return array */ private static function is_repeating( $token ) { if ( '...' === substr( $token, -3 ) ) { return array( true, substr( $token, 0, -3 ) ); } else { return array( false, $token ); } } } spec = SynopsisParser::parse( $synopsis ); } /** * Get any unknown arugments. * * @return array */ public function get_unknown() { return array_column( $this->query_spec( array( 'type' => 'unknown', ) ), 'token' ); } /** * Check whether there are enough positional arguments. * * @param array $args Positional arguments. * @return bool */ public function enough_positionals( $args ) { $positional = $this->query_spec( array( 'type' => 'positional', 'optional' => false, ) ); return count( $args ) >= count( $positional ); } /** * Check for any unknown positionals. * * @param array $args Positional arguments. * @return array */ public function unknown_positionals( $args ) { $positional_repeating = $this->query_spec( array( 'type' => 'positional', 'repeating' => true, ) ); // At least one positional supports as many as possible. if ( !empty( $positional_repeating ) ) return array(); $positional = $this->query_spec( array( 'type' => 'positional', 'repeating' => false, ) ); return array_slice( $args, count( $positional ) ); } /** * Check that all required keys are present and that they have values. * * @param array $assoc_args Parameters passed to command. * @return array */ public function validate_assoc( $assoc_args ) { $assoc_spec = $this->query_spec( array( 'type' => 'assoc', ) ); $errors = array( 'fatal' => array(), 'warning' => array() ); $to_unset = array(); foreach ( $assoc_spec as $param ) { $key = $param['name']; if ( !isset( $assoc_args[ $key ] ) ) { if ( !$param['optional'] ) { $errors['fatal'][$key] = "missing --$key parameter"; } } else { if ( true === $assoc_args[ $key ] && !$param['value']['optional'] ) { $error_type = ( !$param['optional'] ) ? 'fatal' : 'warning'; $errors[ $error_type ][$key] = "--$key parameter needs a value"; $to_unset[] = $key; } } } return array( $errors, $to_unset ); } /** * Check whether there are unknown parameters supplied. * * @param array $assoc_args Parameters passed to command. * @return array|false */ public function unknown_assoc( $assoc_args ) { $generic = $this->query_spec( array( 'type' => 'generic', ) ); if ( count( $generic ) ) return array(); $known_assoc = array(); foreach ( $this->spec as $param ) { if ( in_array( $param['type'], array( 'assoc', 'flag' ) ) ) $known_assoc[] = $param['name']; } return array_diff( array_keys( $assoc_args ), $known_assoc ); } /** * Filters a list of associative arrays, based on a set of key => value arguments. * * @param array $args An array of key => value arguments to match against * @param string $operator * @return array */ private function query_spec( $args, $operator = 'AND' ) { $operator = strtoupper( $operator ); $count = count( $args ); $filtered = array(); foreach ( $this->spec as $key => $to_match ) { $matched = 0; foreach ( $args as $m_key => $m_value ) { if ( array_key_exists( $m_key, $to_match ) && $m_value == $to_match[ $m_key ] ) $matched++; } if ( ( 'AND' == $operator && $matched == $count ) || ( 'OR' == $operator && $matched > 0 ) || ( 'NOT' == $operator && 0 == $matched ) ) { $filtered[$key] = $to_match; } } return $filtered; } } upgrader->strings[ $error ] ) ) $error = $this->upgrader->strings[ $error ]; // TODO: show all errors, not just the first one \WP_CLI::warning( $error ); } function feedback( $string ) { if ( isset( $this->upgrader->strings[$string] ) ) $string = $this->upgrader->strings[$string]; if ( strpos($string, '%') !== false ) { $args = func_get_args(); $args = array_splice($args, 1); if ( !empty($args) ) $string = vsprintf($string, $args); } if ( empty($string) ) return; $string = str_replace( '…', '...', strip_tags( $string ) ); \WP_CLI::line( $string ); } } cache = $cache; // hook into wp http api add_filter( 'pre_http_request', array($this, 'filter_pre_http_request'), 10, 3 ); add_filter( 'http_response', array($this, 'filter_http_response'), 10, 3 ); } /** * short circuit wp http api with cached file */ public function filter_pre_http_request( $response, $args, $url ) { // check if whitelisted if ( !isset( $this->whitelist[$url] ) ) { return $response; } // check if downloading if ( 'GET' !== $args['method'] || empty( $args['filename'] ) ) { return $response; } // check cache and export to designated location $filename = $this->cache->has( $this->whitelist[$url]['key'], $this->whitelist[$url]['ttl'] ); if ( $filename ) { WP_CLI::log( sprintf( 'Using cached file \'%s\'...', $filename, $url ) ); if ( copy( $filename, $args['filename'] ) ) { // simulate successful download response return array( 'response' => array('code' => ( 200 ), 'message' => 'OK'), 'filename' => $args['filename'] ); } else { WP_CLI::error( sprintf( 'Error copying cached file %s to %s', $filename, $url ) ); } } return $response; } /** * cache wp http api downloads * * @param array $response * @param array $args * @param string $url */ public function filter_http_response( $response, $args, $url ) { // check if whitelisted if ( !isset( $this->whitelist[$url] ) ) { return $response; } // check if downloading if ( 'GET' !== $args['method'] || empty( $args['filename'] ) ) { return $response; } // check if download was successful if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return $response; } // cache downloaded file $this->cache->import( $this->whitelist[$url]['key'], $response['filename']); return $response; } /** * whitelist a package url * * @param string $url * @param string $group package group (themes, plugins, ...) * @param string $slug package slug * @param string $version package version * @param int $ttl */ public function whitelist_package( $url, $group, $slug, $version, $ttl = null ) { $ext = pathinfo( parse_url( $url, PHP_URL_PATH ), PATHINFO_EXTENSION ); $key = "$group/$slug-$version.$ext"; $this->whitelist_url( $url, $key, $ttl ); wp_update_plugins(); } /** * whitelist a url * * @param string $url * @param string $key * @param int $ttl */ public function whitelist_url( $url, $key = null, $ttl = null ) { $key = $key ? : $url; $this->whitelist[$url] = compact( 'key', 'ttl' ); } /** * check if url is whitelisted * * @param string $url * @return bool */ public function is_whitelisted( $url ) { return isset( $this->whitelist[$url] ); } } clean(); } ); } } return $cache; } /** * Set the context in which WP-CLI should be run */ public static function set_url( $url ) { WP_CLI::debug( 'Set URL: ' . $url ); $url_parts = Utils\parse_url( $url ); self::set_url_params( $url_parts ); } private static function set_url_params( $url_parts ) { $f = function( $key ) use ( $url_parts ) { return \WP_CLI\Utils\get_flag_value( $url_parts, $key, '' ); }; if ( isset( $url_parts['host'] ) ) { if ( isset( $url_parts['scheme'] ) && 'https' === strtolower( $url_parts['scheme'] ) ) { $_SERVER['HTTPS'] = 'on'; } $_SERVER['HTTP_HOST'] = $url_parts['host']; if ( isset( $url_parts['port'] ) ) { $_SERVER['HTTP_HOST'] .= ':' . $url_parts['port']; } $_SERVER['SERVER_NAME'] = $url_parts['host']; } $_SERVER['REQUEST_URI'] = $f('path') . ( isset( $url_parts['query'] ) ? '?' . $url_parts['query'] : '' ); $_SERVER['SERVER_PORT'] = \WP_CLI\Utils\get_flag_value( $url_parts, 'port', '80' ); $_SERVER['QUERY_STRING'] = $f('query'); } /** * @return WpHttpCacheManager */ public static function get_http_cache_manager() { static $http_cacher; if ( !$http_cacher ) { $http_cacher = new WpHttpCacheManager( self::get_cache() ); } return $http_cacher; } public static function colorize( $string ) { return \cli\Colors::colorize( $string, self::get_runner()->in_color() ); } /** * Schedule a callback to be executed at a certain point (before WP is loaded). */ public static function add_hook( $when, $callback ) { if ( in_array( $when, self::$hooks_passed ) ) call_user_func( $callback ); self::$hooks[ $when ][] = $callback; } /** * Execute registered callbacks. */ public static function do_hook( $when ) { self::$hooks_passed[] = $when; if ( !isset( self::$hooks[ $when ] ) ) return; foreach ( self::$hooks[ $when ] as $callback ) { call_user_func( $callback ); } } /** * Add a command to the wp-cli list of commands * * @param string $name The name of the command that will be used in the CLI * @param string $class The command implementation * @param array $args An associative array with additional parameters: * 'before_invoke' => callback to execute before invoking the command */ public static function add_command( $name, $class, $args = array() ) { if ( is_string( $class ) && ! class_exists( (string) $class ) ) { WP_CLI::error( sprintf( "Class '%s' does not exist.", $class ) ); } if ( isset( $args['before_invoke'] ) ) { self::add_hook( "before_invoke:$name", $args['before_invoke'] ); } $path = preg_split( '/\s+/', $name ); $leaf_name = array_pop( $path ); $full_path = $path; $command = self::get_root_command(); while ( !empty( $path ) ) { $subcommand_name = $path[0]; $subcommand = $command->find_subcommand( $path ); // create an empty container if ( !$subcommand ) { $subcommand = new Dispatcher\CompositeCommand( $command, $subcommand_name, new \WP_CLI\DocParser( '' ) ); $command->add_subcommand( $subcommand_name, $subcommand ); } $command = $subcommand; } $leaf_command = Dispatcher\CommandFactory::create( $leaf_name, $class, $command ); if ( ! $command->can_have_subcommands() ) { throw new Exception( sprintf( "'%s' can't have subcommands.", implode( ' ' , Dispatcher\get_path( $command ) ) ) ); } $command->add_subcommand( $leaf_name, $leaf_command ); } /** * Display a message in the CLI and end with a newline * * @param string $message */ public static function line( $message = '' ) { echo $message . "\n"; } /** * Log an informational message. * * @param string $message */ public static function log( $message ) { self::$logger->info( $message ); } /** * Display a success in the CLI and end with a newline * * @param string $message */ public static function success( $message ) { self::$logger->success( $message ); } /** * Log debug information * * @param string $message */ public static function debug( $message ) { self::$logger->debug( self::error_to_string( $message ) ); } /** * Display a warning in the CLI and end with a newline * * @param string $message */ public static function warning( $message ) { self::$logger->warning( self::error_to_string( $message ) ); } /** * Display an error in the CLI and end with a newline * * @param string|WP_Error $message * @param bool $exit if true, the script will exit() */ public static function error( $message, $exit = true ) { if ( ! isset( self::get_runner()->assoc_args[ 'completions' ] ) ) { self::$logger->error( self::error_to_string( $message ) ); } if ( $exit ) { exit(1); } } /** * Display an error in the CLI and end with a newline * * @param array $message each element from the array will be printed on its own line */ public static function error_multi_line( $message_lines ) { if ( ! isset( self::get_runner()->assoc_args[ 'completions' ] ) && is_array( $message_lines ) ) { self::$logger->error_multi_line( array_map( array( __CLASS__, 'error_to_string' ), $message_lines ) ); } } /** * Ask for confirmation before running a destructive operation. */ public static function confirm( $question, $assoc_args = array() ) { if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'yes' ) ) { fwrite( STDOUT, $question . " [y/n] " ); $answer = trim( fgets( STDIN ) ); if ( 'y' != $answer ) exit; } } /** * Read value from a positional argument or from STDIN. * * @param array $args The list of positional arguments. * @param int $index At which position to check for the value. * * @return string */ public static function get_value_from_arg_or_stdin( $args, $index ) { if ( isset( $args[ $index ] ) ) { $raw_value = $args[ $index ]; } else { // We don't use file_get_contents() here because it doesn't handle // Ctrl-D properly, when typing in the value interactively. $raw_value = ''; while ( ( $line = fgets( STDIN ) ) !== false ) { $raw_value .= $line; } } return $raw_value; } /** * Read a value, from various formats. * * @param mixed $value * @param array $assoc_args */ public static function read_value( $raw_value, $assoc_args = array() ) { if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' ) === 'json' ) { $value = json_decode( $raw_value, true ); if ( null === $value ) { WP_CLI::error( sprintf( 'Invalid JSON: %s', $raw_value ) ); } } else { $value = $raw_value; } return $value; } /** * Display a value, in various formats * * @param mixed $value * @param array $assoc_args */ public static function print_value( $value, $assoc_args = array() ) { if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' ) === 'json' ) { $value = json_encode( $value ); } elseif ( is_array( $value ) || is_object( $value ) ) { $value = var_export( $value ); } echo $value . "\n"; } /** * Convert a wp_error into a string * * @param mixed $errors * @return string */ public static function error_to_string( $errors ) { if ( is_string( $errors ) ) { return $errors; } if ( is_object( $errors ) && is_a( $errors, 'WP_Error' ) ) { foreach ( $errors->get_error_messages() as $message ) { if ( $errors->get_error_data() ) return $message . ' ' . $errors->get_error_data(); else return $message; } } } /** * Launch an external process that takes over I/O. * * @param string Command to call * @param bool Whether to exit if the command returns an error status * @param bool Whether to return an exit status (default) or detailed execution results * * @return int|ProcessRun The command exit status, or a ProcessRun instance */ public static function launch( $command, $exit_on_error = true, $return_detailed = false ) { $proc = Process::create( $command ); $results = $proc->run(); if ( $results->return_code && $exit_on_error ) exit( $results->return_code ); if ( $return_detailed ) { return $results; } else { return $results->return_code; } } /** * Launch another WP-CLI command using the runtime arguments for the current process * * @param string Command to call * @param array $args Positional arguments to use * @param array $assoc_args Associative arguments to use * @param bool Whether to exit if the command returns an error status * @param bool Whether to return an exit status (default) or detailed execution results * @param array $runtime_args Override one or more global args (path,url,user,allow-root) * * @return int|ProcessRun The command exit status, or a ProcessRun instance */ public static function launch_self( $command, $args = array(), $assoc_args = array(), $exit_on_error = true, $return_detailed = false, $runtime_args = array() ) { $reused_runtime_args = array( 'path', 'url', 'user', 'allow-root', ); foreach ( $reused_runtime_args as $key ) { if ( isset( $runtime_args[ $key ] ) ) { $assoc_args[ $key ] = $runtime_args[ $key ]; } else if ( $value = self::get_runner()->config[ $key ] ) $assoc_args[ $key ] = $value; } $php_bin = self::get_php_binary(); $script_path = $GLOBALS['argv'][0]; $args = implode( ' ', array_map( 'escapeshellarg', $args ) ); $assoc_args = \WP_CLI\Utils\assoc_args_to_str( $assoc_args ); $full_command = "{$php_bin} {$script_path} {$command} {$args} {$assoc_args}"; return self::launch( $full_command, $exit_on_error, $return_detailed ); } /** * Get the path to the PHP binary used when executing WP-CLI. * Environment values permit specific binaries to be indicated. * * @return string */ public static function get_php_binary() { if ( defined( 'PHP_BINARY' ) ) return PHP_BINARY; if ( getenv( 'WP_CLI_PHP_USED' ) ) return getenv( 'WP_CLI_PHP_USED' ); if ( getenv( 'WP_CLI_PHP' ) ) return getenv( 'WP_CLI_PHP' ); return 'php'; } public static function get_config( $key = null ) { if ( null === $key ) { return self::get_runner()->config; } if ( !isset( self::get_runner()->config[ $key ] ) ) { self::warning( "Unknown config option '$key'." ); return null; } return self::get_runner()->config[ $key ]; } /** * Run a given command within the current process using the same global parameters. * * To run a command using a new process with the same global parameters, use WP_CLI::launch_self() * To run a command using a new process with different global parameters, use WP_CLI::launch() * * @param array * @param array */ public static function run_command( $args, $assoc_args = array() ) { self::get_runner()->run_command( $args, $assoc_args ); } // DEPRECATED STUFF public static function add_man_dir() { trigger_error( 'WP_CLI::add_man_dir() is deprecated. Add docs inline.', E_USER_WARNING ); } // back-compat public static function out( $str ) { fwrite( STDOUT, $str ); } // back-compat public static function addCommand( $name, $class ) { trigger_error( sprintf( 'wp %s: %s is deprecated. use WP_CLI::add_command() instead.', $name, __FUNCTION__ ), E_USER_WARNING ); self::add_command( $name, $class ); } } * : Cache key. * * * : Value to add to the key. * * [] * : Method for grouping data within the cache which allows the same key to be used across groups. * * [] * : Define how long to keep the value, in seconds. Defaults to 0 (as long as possible). */ public function add( $args, $assoc_args ) { list( $key, $value ) = $args; $group = \WP_CLI\Utils\get_flag_value( $args, 2, '' ); $expiration = \WP_CLI\Utils\get_flag_value( $args, 3, 0 ); if ( ! wp_cache_add( $key, $value, $group, $expiration ) ) { WP_CLI::error( "Could not add object '$key' in group '$group'. Does it already exist?" ); } WP_CLI::success( "Added object '$key' in group '$group'." ); } /** * Decrement a value in the object cache. * * * : Cache key. * * [] * : The amount by which to decrement the item's value. Default is 1. * * [] * : Method for grouping data within the cache which allows the same key to be used across groups. */ public function decr( $args, $assoc_args ) { $key = $args[0]; $offset = \WP_CLI\Utils\get_flag_value( $args, 1, 1 ); $group = \WP_CLI\Utils\get_flag_value( $args, 2, '' ); $value = wp_cache_decr( $key, $offset, $group ); if ( false === $value ) { WP_CLI::error( 'The value was not decremented.' ); } WP_CLI::print_value( $value, $assoc_args ); } /** * Remove a value from the object cache. * * * : Cache key. * * [] * : Method for grouping data within the cache which allows the same key to be used across groups. */ public function delete( $args, $assoc_args ) { $key = $args[0]; $group = \WP_CLI\Utils\get_flag_value( $args, 1, '' ); $result = wp_cache_delete( $key, $group ); if ( false === $result ) { WP_CLI::error( 'The object was not deleted.' ); } WP_CLI::success( 'Object deleted.' ); } /** * Flush the object cache. * * For sites using a persistent object cache, because WordPress Multisite simply adds a blog id * to the cache key, flushing cache is typically a global operation. */ public function flush( $args, $assoc_args ) { $value = wp_cache_flush(); if ( false === $value ) { WP_CLI::error( 'The object cache could not be flushed.' ); } WP_CLI::success( 'The cache was flushed.' ); } /** * Get a value from the object cache. * * * : Cache key. * * [] * : Method for grouping data within the cache which allows the same key to be used across groups. */ public function get( $args, $assoc_args ) { $key = $args[0]; $group = \WP_CLI\Utils\get_flag_value( $args, 1, '' ); $value = wp_cache_get( $key, $group ); if ( false === $value ) { WP_CLI::error( "Object with key '$key' and group '$group' not found." ); } WP_CLI::print_value( $value, $assoc_args ); } /** * Increment a value in the object cache. * * * : Cache key. * * [] * : The amount by which to increment the item's value. Default is 1. * * [] * : Method for grouping data within the cache which allows the same key to be used across groups. */ public function incr( $args, $assoc_args ) { $key = $args[0]; $offset = \WP_CLI\Utils\get_flag_value( $args, 1, 1 ); $group = \WP_CLI\Utils\get_flag_value( $args, 2, '' ); $value = wp_cache_incr( $key, $offset, $group ); if ( false === $value ) { WP_CLI::error( 'The value was not incremented.' ); } WP_CLI::print_value( $value, $assoc_args ); } /** * Replace a value in the object cache, if the value already exists. * * * : Cache key. * * * : Value to replace. * * [] * : Method for grouping data within the cache which allows the same key to be used across groups. * * [] * : Define how long to keep the value, in seconds. Defaults to 0 (as long as possible). */ public function replace( $args, $assoc_args ) { list( $key, $value ) = $args; $group = \WP_CLI\Utils\get_flag_value( $args, 2, '' ); $expiration = \WP_CLI\Utils\get_flag_value( $args, 3, 0 ); $result = wp_cache_replace( $key, $value, $group, $expiration ); if ( false === $result ) { WP_CLI::error( "Could not replace object '$key' in group '$group'. Does it already exist?" ); } WP_CLI::success( "Replaced object '$key' in group '$group'." ); } /** * Set a value to the object cache, regardless of whether it already exists. * * * : Cache key. * * * : Value to set on the key. * * [] * : Method for grouping data within the cache which allows the same key to be used across groups. * * [] * : Define how long to keep the value, in seconds. Defaults to 0 (as long as possible). */ public function set( $args, $assoc_args ) { list( $key, $value ) = $args; $group = \WP_CLI\Utils\get_flag_value( $args, 2, '' ); $expiration = \WP_CLI\Utils\get_flag_value( $args, 3, 0 ); $result = wp_cache_set( $key, $value, $group, $expiration ); if ( false === $result ) { WP_CLI::error( "Could not add object '$key' in group '$group'." ); } WP_CLI::success( "Set object '$key' in group '$group'." ); } /** * Attempts to determine which object cache is being used. * * Note that the guesses made by this function are based on the WP_Object_Cache classes * that define the 3rd party object cache extension. Changes to those classes could render * problems with this function's ability to determine which object cache is being used. */ public function type( $args, $assoc_args ) { $message = WP_CLI\Utils\wp_get_cache_type(); WP_CLI::line( $message ); } } WP_CLI::add_command( 'cache', 'Cache_Command' ); * : Key for the role. * * ## EXAMPLES * * # Display alphabetical list of bbPress moderator capabilities * wp cap list 'bbp_moderator' | sort * * @subcommand list */ public function list_( $args ) { $role_obj = self::get_role( $args[0] ); foreach ( array_keys( $role_obj->capabilities ) as $cap ) WP_CLI::line( $cap ); } /** * Add capabilities to a given role. * * * : Key for the role. * * ... * : One or more capabilities to add. */ public function add( $args ) { self::persistence_check(); $role = array_shift( $args ); $role_obj = self::get_role( $role ); $count = 0; foreach ( $args as $cap ) { if ( $role_obj->has_cap( $cap ) ) continue; $role_obj->add_cap( $cap ); $count++; } WP_CLI::success( sprintf( "Added %d capabilities to '%s' role." , $count, $role ) ); } /** * Remove capabilities from a given role. * * * : Key for the role. * * ... * : One or more capabilities to remove. */ public function remove( $args ) { self::persistence_check(); $role = array_shift( $args ); $role_obj = self::get_role( $role ); $count = 0; foreach ( $args as $cap ) { if ( !$role_obj->has_cap( $cap ) ) continue; $role_obj->remove_cap( $cap ); $count++; } WP_CLI::success( sprintf( "Removed %d capabilities from '%s' role." , $count, $role ) ); } private static function get_role( $role ) { global $wp_roles; $role_obj = $wp_roles->get_role( $role ); if ( !$role_obj ) WP_CLI::error( "'$role' role not found." ); return $role_obj; } private static function persistence_check() { global $wp_roles; if ( !$wp_roles->use_db ) WP_CLI::error( "Role definitions are not persistent." ); } } WP_CLI::add_command( 'cap', 'Capabilities_Command' ); $command->get_name(), 'description' => $command->get_shortdesc(), 'longdesc' => $command->get_longdesc(), ); foreach ( $command->get_subcommands() as $subcommand ) { $dump['subcommands'][] = self::command_to_array( $subcommand ); } if ( empty( $dump['subcommands'] ) ) { $dump['synopsis'] = (string) $command->get_synopsis(); } return $dump; } /** * Print WP-CLI version. */ public function version() { WP_CLI::line( 'WP-CLI ' . WP_CLI_VERSION ); } /** * Print various data about the CLI environment. * * ## OPTIONS * * [--format=] * : Accepted values: json */ public function info( $_, $assoc_args ) { $php_bin = WP_CLI::get_php_binary(); $runner = WP_CLI::get_runner(); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' ) === 'json' ) { $info = array( 'php_binary_path' => $php_bin, 'global_config_path' => $runner->global_config_path, 'project_config_path' => $runner->project_config_path, 'wp_cli_dir_path' => WP_CLI_ROOT, 'wp_cli_version' => WP_CLI_VERSION, ); WP_CLI::line( json_encode( $info ) ); } else { WP_CLI::line( "PHP binary:\t" . $php_bin ); WP_CLI::line( "PHP version:\t" . PHP_VERSION ); WP_CLI::line( "php.ini used:\t" . get_cfg_var( 'cfg_file_path' ) ); WP_CLI::line( "WP-CLI root dir:\t" . WP_CLI_ROOT ); WP_CLI::line( "WP-CLI global config:\t" . $runner->global_config_path ); WP_CLI::line( "WP-CLI project config:\t" . $runner->project_config_path ); WP_CLI::line( "WP-CLI version:\t" . WP_CLI_VERSION ); } } /** * Check for update via Github API. Returns the available versions if there are updates, or empty if no update available. * * ## OPTIONS * * [--patch] * : Only list patch updates * * [--minor] * : Only list minor updates * * [--major] * : Only list major updates * * [--field=] * : Prints the value of a single field for each update. * * [--fields=] * : Limit the output to specific object fields. Defaults to version,update_type,package_url. * * [--format=] * : Accepted values: table, csv, json, count. Default: table * * @subcommand check-update */ public function check_update( $_, $assoc_args ) { $updates = $this->get_updates( $assoc_args ); if ( $updates ) { $formatter = new \WP_CLI\Formatter( $assoc_args, array( 'version', 'update_type', 'package_url' ) ); $formatter->display_items( $updates ); } else if ( empty( $assoc_args['format'] ) || 'table' == $assoc_args['format'] ) { $update_type = $this->get_update_type_str( $assoc_args ); WP_CLI::success( "WP-CLI is at the latest{$update_type}version." ); } } /** * Fetch most recent update matching the requirements. Returns the available versions if there are updates, or empty if no update available. * * ## OPTIONS * * [--patch] * : Only perform patch updates * * [--minor] * : Only perform minor updates * * [--major] * : Only perform major updates * * [--nightly] * : Update to the latest built version of the master branch. Potentially unstable. * * [--yes] * : Do not prompt for confirmation */ public function update( $_, $assoc_args ) { if ( ! Utils\inside_phar() ) { WP_CLI::error( "You can only self-update Phar files." ); } $old_phar = realpath( $_SERVER['argv'][0] ); if ( ! is_writable( $old_phar ) ) { WP_CLI::error( sprintf( "%s is not writable by current user", $old_phar ) ); } else if ( ! is_writeable( dirname( $old_phar ) ) ) { WP_CLI::error( sprintf( "%s is not writable by current user", dirname( $old_phar ) ) ); } if ( isset( $assoc_args['nightly'] ) ) { WP_CLI::confirm( sprintf( 'You have version %s. Would you like to update to the latest nightly?', WP_CLI_VERSION ), $assoc_args ); $download_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar'; } else { $updates = $this->get_updates( $assoc_args ); if ( empty( $updates ) ) { $update_type = $this->get_update_type_str( $assoc_args ); WP_CLI::success( "WP-CLI is at the latest{$update_type}version." ); exit(0); } $newest = $updates[0]; WP_CLI::confirm( sprintf( 'You have version %s. Would you like to update to %s?', WP_CLI_VERSION, $newest['version'] ), $assoc_args ); $download_url = $newest['package_url']; } WP_CLI::log( sprintf( 'Downloading from %s...', $download_url ) ); $temp = \WP_CLI\Utils\get_temp_dir() . uniqid('wp_') . '.phar'; $headers = array(); $options = array( 'timeout' => 600, // 10 minutes ought to be enough for everybody 'filename' => $temp ); Utils\http_request( 'GET', $download_url, null, $headers, $options ); $allow_root = WP_CLI::get_runner()->config['allow-root'] ? '--allow-root' : ''; $php_binary = WP_CLI::get_php_binary(); $process = WP_CLI\Process::create( "{$php_binary} $temp --version {$allow_root}" ); $result = $process->run(); if ( 0 !== $result->return_code ) { $multi_line = explode( PHP_EOL, $result->stderr ); WP_CLI::error_multi_line( $multi_line ); WP_CLI::error( 'The downloaded PHAR is broken, try running wp cli update again.' ); } WP_CLI::log( 'New version works. Proceeding to replace.' ); $mode = fileperms( $old_phar ) & 511; if ( false === @chmod( $temp, $mode ) ) { WP_CLI::error( sprintf( "Cannot chmod %s", $temp ) ); } class_exists( '\cli\Colors' ); // this autoloads \cli\Colors - after we move the file we no longer have access to this class if ( false === @rename( $temp, $old_phar ) ) { WP_CLI::error( sprintf( "Cannot move %s to %s", $temp, $old_phar ) ); } if ( isset( $assoc_args['nightly'] ) ) { $updated_version = 'the latest nightly release'; } else { $updated_version = $newest['version']; } WP_CLI::success( sprintf( 'Updated WP-CLI to %s', $updated_version ) ); } /** * Returns update information */ private function get_updates( $assoc_args ) { $url = 'https://api.github.com/repos/wp-cli/wp-cli/releases'; $options = array( 'timeout' => 30 ); $headers = array( 'Accept' => 'application/json' ); $response = Utils\http_request( 'GET', $url, $headers, $options ); if ( ! $response->success || 200 !== $response->status_code ) { WP_CLI::error( sprintf( "Failed to get latest version (HTTP code %d)", $response->status_code ) ); } $release_data = json_decode( $response->body ); $updates = array( 'major' => false, 'minor' => false, 'patch' => false, ); foreach ( $release_data as $release ) { // get rid of leading "v" if there is one set $release_version = $release->tag_name; if ( 'v' === substr( $release_version, 0, 1 ) ) { $release_version = ltrim( $release_version, 'v' ); } $update_type = Utils\get_named_sem_ver( $release_version, WP_CLI_VERSION ); if ( ! $update_type ) { continue; } if ( ! empty( $updates[ $update_type ] ) && ! Comparator::greaterThan( $release_version, $updates[ $update_type ]['version'] ) ) { continue; } $updates[ $update_type ] = array( 'version' => $release_version, 'update_type' => $update_type, 'package_url' => $release->assets[0]->browser_download_url ); } foreach( $updates as $type => $value ) { if ( empty( $value ) ) { unset( $updates[ $type ] ); } } foreach( array( 'major', 'minor', 'patch' ) as $type ) { if ( true === \WP_CLI\Utils\get_flag_value( $assoc_args, $type ) ) { return ! empty( $updates[ $type ] ) ? array( $updates[ $type ] ) : false; } } return array_values( $updates ); } /** * Dump the list of global parameters, as JSON or in var_export format. * * ## OPTIONS * * [--with-values] * : Display current values also. * * [--format=] * : Accepted values: var_export, json. Default: json. * * @subcommand param-dump */ function param_dump( $_, $assoc_args ) { $spec = \WP_CLI::get_configurator()->get_spec(); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'with-values' ) ) { $config = \WP_CLI::get_configurator()->to_array(); // Copy current config values to $spec foreach ( $spec as $key => $value ) { if ( isset( $config[0][$key] ) ) { $current = $config[0][$key]; } else { $current = NULL; } $spec[$key]['current'] = $current; } } if ( 'var_export' === \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' ) ) { var_export( $spec ); } else { echo json_encode( $spec ); } } /** * Dump the list of installed commands, as JSON. * * @subcommand cmd-dump */ public function cmd_dump() { echo json_encode( self::command_to_array( WP_CLI::get_root_command() ) ); } /** * Generate tab completion strings. * * ## OPTIONS * * --line= * : The current command line to be executed * * --point= * : The index to the current cursor position relative to the beginning of the command */ public function completions( $_, $assoc_args ) { $line = substr( $assoc_args['line'], 0, $assoc_args['point'] ); $compl = new \WP_CLI\Completions( $line ); $compl->render(); } /** * Get a string representing the type of update being checked for */ private function get_update_type_str( $assoc_args ) { $update_type = ' '; foreach( array( 'major', 'minor', 'patch' ) as $type ) { if ( true === \WP_CLI\Utils\get_flag_value( $assoc_args, $type ) ) { $update_type = ' ' . $type . ' '; break; } } return $update_type; } } WP_CLI::add_command( 'cli', 'CLI_Command' ); fetcher = new \WP_CLI\Fetchers\Comment; } /** * Insert a comment. * * ## OPTIONS * * [--=] * : Associative args for the new comment. See wp_insert_comment(). * * [--porcelain] * : Output just the new comment id. * * ## EXAMPLES * * wp comment create --comment_post_ID=15 --comment_content="hello blog" --comment_author="wp-cli" */ public function create( $args, $assoc_args ) { parent::_create( $args, $assoc_args, function ( $params ) { if ( isset( $params['comment_post_ID'] ) ) { $post_id = $params['comment_post_ID']; $post = get_post( $post_id ); if ( !$post ) { return new WP_Error( 'no_post', "Can't find post $post_id." ); } } // We use wp_insert_comment() instead of wp_new_comment() to stay at a low level and // avoid wp_die() formatted messages or notifications $comment_id = wp_insert_comment( $params ); if ( !$comment_id ) { return new WP_Error( 'db_error', 'Could not create comment.' ); } return $comment_id; } ); } /** * Update one or more comments. * * ## OPTIONS * * ... * : One or more IDs of comments to update. * * --= * : One or more fields to update. See wp_update_comment(). * * ## EXAMPLES * * wp comment update 123 --comment_author='That Guy' */ public function update( $args, $assoc_args ) { parent::_update( $args, $assoc_args, function ( $params ) { if ( !wp_update_comment( $params ) ) { return new WP_Error( 'Could not update comment.' ); } return true; } ); } /** * Generate comments. * * ## OPTIONS * * [--count=] * : How many comments to generate. Default: 100 */ public function generate( $args, $assoc_args ) { $defaults = array( 'count' => 100, ); $assoc_args = array_merge( $defaults, $assoc_args ); $notify = \WP_CLI\Utils\make_progress_bar( 'Generating comments', $assoc_args['count'] ); $comment_count = wp_count_comments(); $total = (int )$comment_count->total_comments; $limit = $total + $assoc_args['count']; for ( $i = $total; $i < $limit; $i++ ) { wp_insert_comment( array( 'comment_content' => "Comment {$i}", ) ); $notify->tick(); } $notify->finish(); } /** * Get a single comment. * * ## OPTIONS * * * : The comment to get. * * [--field=] * : Instead of returning the whole comment, returns the value of a single field. * * [--fields=] * : Limit the output to specific fields. Defaults to all fields. * * [--format=] * : Accepted values: table, json, csv. Default: table * * ## EXAMPLES * * wp comment get 1 --field=content */ public function get( $args, $assoc_args ) { $comment_id = (int)$args[0]; $comment = get_comment( $comment_id ); if ( empty( $comment ) ) { WP_CLI::error( "Invalid comment ID." ); } if ( empty( $assoc_args['fields'] ) ) { $comment_array = get_object_vars( $comment ); $assoc_args['fields'] = array_keys( $comment_array ); } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $comment ); } /** * Get a list of comments. * * ## OPTIONS * * [--=] * : One or more args to pass to WP_Comment_Query. * * [--field=] * : Prints the value of a single field for each comment. * * [--fields=] * : Limit the output to specific object fields. * * [--format=] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each comment: * * * comment_ID * * comment_post_ID * * comment_date * * comment_approved * * comment_author * * comment_author_email * * These fields are optionally available: * * * comment_author_url * * comment_author_IP * * comment_date_gmt * * comment_content * * comment_karma * * comment_agent * * comment_type * * comment_parent * * user_id * * ## EXAMPLES * * wp comment list --field=ID * * wp comment list --post_id=2 * * wp comment list --number=20 --status=approve * * @subcommand list */ public function list_( $_, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); if ( 'ids' == $formatter->format ) $assoc_args['fields'] = 'comment_ID'; $query = new WP_Comment_Query(); $comments = $query->query( $assoc_args ); if ( 'ids' == $formatter->format ) { $comments = wp_list_pluck( $comments, 'comment_ID' ); } $formatter->display_items( $comments ); } /** * Delete a comment. * * ## OPTIONS * * ... * : One or more IDs of comments to delete. * * [--force] * : Skip the trash bin. * * ## EXAMPLES * * wp comment delete 1337 --force * * wp comment delete 1337 2341 --force */ public function delete( $args, $assoc_args ) { parent::_delete( $args, $assoc_args, function ( $comment_id, $assoc_args ) { $r = wp_delete_comment( $comment_id, \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ) ); if ( $r ) { return array( 'success', "Deleted comment $comment_id." ); } else { return array( 'error', "Failed deleting comment $comment_id" ); } } ); } private function call( $args, $status, $success, $failure ) { list( $comment_id ) = $args; $func = sprintf( 'wp_%s_comment', $status ); if ( $func( $comment_id ) ) { WP_CLI::success( "$success comment $comment_id." ); } else { WP_CLI::error( "$failure comment $comment_id" ); } } private function set_status( $args, $status, $success ) { $comment = $this->fetcher->get_check( $args[0] ); $r = wp_set_comment_status( $comment->comment_ID, $status, true ); if ( is_wp_error( $r ) ) { WP_CLI::error( $r ); } else { WP_CLI::success( "$success comment $comment->comment_ID" ); } } /** * Trash a comment. * * ## OPTIONS * * ... * : The IDs of the comments to trash. * * ## EXAMPLES * * wp comment trash 1337 */ public function trash( $args, $assoc_args ) { foreach( $args as $id ) { $this->call( $id, __FUNCTION__, 'Trashed', 'Failed trashing' ); } } /** * Untrash a comment. * * ## OPTIONS * * ... * : The IDs of the comments to untrash. * * ## EXAMPLES * * wp comment untrash 1337 */ public function untrash( $args, $assoc_args ) { foreach( $args as $id ) { $this->call( $id, __FUNCTION__, 'Untrashed', 'Failed untrashing' ); } } /** * Spam a comment. * * ## OPTIONS * * ... * : The IDs of the comments to mark as spam. * * ## EXAMPLES * * wp comment spam 1337 */ public function spam( $args, $assoc_args ) { foreach( $args as $id ) { $this->call( $id, __FUNCTION__, 'Marked as spam', 'Failed marking as spam' ); } } /** * Unspam a comment. * * ## OPTIONS * * ... * : The IDs of the comments to unmark as spam. * * ## EXAMPLES * * wp comment unspam 1337 */ public function unspam( $args, $assoc_args ) { foreach( $args as $id ) { $this->call( $args, __FUNCTION__, 'Unspammed', 'Failed unspamming' ); } } /** * Approve a comment. * * ## OPTIONS * * ... * : The IDs of the comments to approve. * * ## EXAMPLES * * wp comment approve 1337 */ public function approve( $args, $assoc_args ) { foreach( $args as $id ) { $this->set_status( $id, 'approve', "Approved" ); } } /** * Unapprove a comment. * * ## OPTIONS * * ... * : The IDs of the comments to unapprove. * * ## EXAMPLES * * wp comment unapprove 1337 */ public function unapprove( $args, $assoc_args ) { foreach( $args as $id ) { $this->set_status( $id, 'hold', "Unapproved" ); } } /** * Count comments, on whole blog or on a given post. * * ## OPTIONS * * [] * : The ID of the post to count comments in. * * ## EXAMPLES * * wp comment count * wp comment count 42 */ public function count( $args, $assoc_args ) { $post_id = \WP_CLI\Utils\get_flag_value( $args, 0, 0 ); $count = wp_count_comments( $post_id ); // Move total_comments to the end of the object $total = $count->total_comments; unset( $count->total_comments ); $count->total_comments = $total; foreach ( $count as $status => $count ) { WP_CLI::line( str_pad( "$status:", 17 ) . $count ); } } /** * Recount the comment_count value for one or more posts. * * ... * : IDs for one or more posts to update. */ public function recount( $args ) { foreach( $args as $id ) { wp_update_comment_count( $id ); $post = get_post( $id ); if ( $post ) { WP_CLI::log( sprintf( "Updated post %d comment count to %d", $post->ID, $post->comment_count ) ); } else { WP_CLI::warning( sprintf( "Post %d doesn't exist", $post->ID ) ); } } } /** * Get status of a comment. * * ## OPTIONS * * * : The ID of the comment to check. * * ## EXAMPLES * * wp comment status 1337 */ public function status( $args, $assoc_args ) { list( $comment_id ) = $args; $status = wp_get_comment_status( $comment_id ); if ( false === $status ) { WP_CLI::error( "Could not check status of comment $comment_id." ); } else { WP_CLI::line( $status ); } } /** * Verify whether a comment exists. * * ## OPTIONS * * * : The ID of the comment to check. * * ## EXAMPLES * * wp comment exists 1337 */ public function exists( $args ) { if ( $this->fetcher->get( $args[0] ) ) { WP_CLI::success( "Comment with ID $args[0] exists." ); } } /** * Get comment url * * ## OPTIONS * * ... * : One or more IDs of comments to get the URL. * * ## EXAMPLES * * wp comment url 123 */ public function url( $args ) { parent::_url( $args, 'get_comment_link' ); } } /** * Manage comment custom fields. * * ## OPTIONS * * --format=json * : Encode/decode values as JSON. * * ## EXAMPLES * * wp comment meta set 123 description "Mary is a WordPress developer." */ class Comment_Meta_Command extends \WP_CLI\CommandWithMeta { protected $meta_type = 'comment'; /** * Check that the comment ID exists * * @param int */ protected function check_object_id( $object_id ) { $fetcher = new \WP_CLI\Fetchers\Comment; $comment = $fetcher->get_check( $object_id ); return $comment->comment_ID; } } WP_CLI::add_command( 'comment', 'Comment_Command' ); WP_CLI::add_command( 'comment meta', 'Comment_Meta_Command' ); ] * : Prints the value of a single field for each update. * * [--fields=] * : Limit the output to specific object fields. Defaults to version,update_type,package_url. * * [--format=] * : Accepted values: table, csv, json. Default: table * * @subcommand check-update */ function check_update( $_, $assoc_args ) { $updates = $this->get_updates( $assoc_args ); if ( $updates ) { $updates = array_reverse( $updates ); $formatter = new \WP_CLI\Formatter( $assoc_args, array( 'version', 'update_type', 'package_url' ) ); $formatter->display_items( $updates ); } else if ( empty( $assoc_args['format'] ) || 'table' == $assoc_args['format'] ) { WP_CLI::success( "WordPress is at the latest version." ); } } /** * Download core WordPress files. * * ## OPTIONS * * [--path=] * : Specify the path in which to install WordPress. * * [--locale=] * : Select which language you want to download. * * [--version=] * : Select which version you want to download. * * [--force] * : Overwrites existing files, if present. * * ## EXAMPLES * * wp core download --locale=nl_NL * * @when before_wp_load */ public function download( $args, $assoc_args ) { $download_dir = ! empty( $assoc_args['path'] ) ? $assoc_args['path'] : ABSPATH; if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ) && is_readable( $download_dir . 'wp-load.php' ) ) WP_CLI::error( 'WordPress files seem to already be present here.' ); if ( !is_dir( $download_dir ) ) { WP_CLI::log( sprintf( 'Creating directory %s', $download_dir ) ); $mkdir = \WP_CLI\Utils\is_windows() ? 'mkdir %s' : 'mkdir -p %s'; WP_CLI::launch( Utils\esc_cmd( $mkdir, $download_dir ) ); } $locale = \WP_CLI\Utils\get_flag_value( $assoc_args, 'locale', 'en_US' ); if ( isset( $assoc_args['version'] ) ) { $version = $assoc_args['version']; $download_url = $this->get_download_url($version, $locale, 'tar.gz'); } else { $offer = $this->get_download_offer( $locale ); if ( !$offer ) { WP_CLI::error( "The requested locale ($locale) was not found." ); } $version = $offer['current']; $download_url = str_replace( '.zip', '.tar.gz', $offer['download'] ); } WP_CLI::log( sprintf( 'Downloading WordPress %s (%s)...', $version, $locale ) ); $cache = WP_CLI::get_cache(); $cache_key = "core/wordpress-{$version}-{$locale}.tar.gz"; $cache_file = $cache->has($cache_key); $bad_cache = false; if ( $cache_file ) { WP_CLI::log( "Using cached file '$cache_file'..." ); try{ self::_extract( $cache_file, $download_dir ); } catch ( Exception $e ) { WP_CLI::warning( "Extraction failed, downloading a new copy..." ); $bad_cache = true; } } if ( ! $cache_file || $bad_cache ) { // We need to use a temporary file because piping from cURL to tar is flaky // on MinGW (and probably in other environments too). $temp = \WP_CLI\Utils\get_temp_dir() . uniqid('wp_') . '.tar.gz'; $headers = array('Accept' => 'application/json'); $options = array( 'timeout' => 600, // 10 minutes ought to be enough for everybody 'filename' => $temp ); $response = Utils\http_request( 'GET', $download_url, null, $headers, $options ); if ( 404 == $response->status_code ) { WP_CLI::error( "Release not found. Double-check locale or version." ); } else if ( 20 != substr( $response->status_code, 0, 2 ) ) { WP_CLI::error( "Couldn't access download URL (HTTP code {$response->status_code})" ); } try { self::_extract( $temp, $download_dir ); } catch ( Exception $e ) { WP_CLI::error( "Couldn't extract WordPress archive. " . $e->getMessage() ); } $cache->import( $cache_key, $temp ); unlink($temp); } WP_CLI::success( 'WordPress downloaded.' ); } private static function _extract( $tarball, $dest ) { if ( ! class_exists( 'PharData' ) ) { $cmd = "tar xz --strip-components=1 --directory=%s -f $tarball"; WP_CLI::launch( Utils\esc_cmd( $cmd, $dest ) ); return; } $phar = new PharData( $tarball ); $tempdir = implode( DIRECTORY_SEPARATOR, Array ( dirname( $tarball ), basename( $tarball, '.tar.gz' ), $phar->getFileName() ) ); $phar->extractTo( dirname( $tempdir ), null, true ); self::_copy_overwrite_files( $tempdir, $dest ); self::_rmdir( dirname( $tempdir ) ); } private static function _copy_overwrite_files( $source, $dest ) { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $source, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::SELF_FIRST); $error = 0; foreach ( $iterator as $item ) { $dest_path = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName(); if ( $item->isDir() ) { if ( !is_dir( $dest_path ) ) { mkdir( $dest_path ); } } else { if ( file_exists( $dest_path ) && is_writable( $dest_path ) ) { copy( $item, $dest_path ); } elseif ( ! file_exists( $dest_path ) ) { copy( $item, $dest_path ); } else { $error = 1; WP_CLI::warning( 'Unable to copy ' . $iterator->getSubPathName() . ' to current directory.' ); } } } if ( $error ) { WP_CLI::error( 'There was an error downloading all WordPress files.' ); } } private static function _rmdir( $dir ) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $files as $fileinfo ) { $todo = $fileinfo->isDir() ? 'rmdir' : 'unlink'; $todo( $fileinfo->getRealPath() ); } rmdir( $dir ); } private static function _read( $url ) { $headers = array('Accept' => 'application/json'); $response = Utils\http_request( 'GET', $url, null, $headers, array( 'timeout' => 30 ) ); if ( 200 === $response->status_code ) { return $response->body; } else { WP_CLI::error( "Couldn't fetch response from {$url} (HTTP code {$response->status_code})" ); } } private function get_download_offer( $locale ) { $out = unserialize( self::_read( 'https://api.wordpress.org/core/version-check/1.6/?locale=' . $locale ) ); $offer = $out['offers'][0]; if ( $offer['locale'] != $locale ) { return false; } return $offer; } private static function get_initial_locale() { include ABSPATH . '/wp-includes/version.php'; // @codingStandardsIgnoreStart if ( isset( $wp_local_package ) ) return $wp_local_package; // @codingStandardsIgnoreEnd return ''; } /** * Generate a wp-config.php file. * * ## OPTIONS * * --dbname= * : Set the database name. * * --dbuser= * : Set the database user. * * [--dbpass=] * : Set the database user password. * * [--dbhost=] * : Set the database host. Default: 'localhost' * * [--dbprefix=] * : Set the database table prefix. Default: 'wp_' * * [--dbcharset=] * : Set the database charset. Default: 'utf8' * * [--dbcollate=] * : Set the database collation. Default: '' * * [--locale=] * : Set the WPLANG constant. Defaults to $wp_local_package variable. * * [--extra-php] * : If set, the command copies additional PHP code into wp-config.php from STDIN. * * [--skip-salts] * : If set, keys and salts won't be generated, but should instead be passed via `--extra-php`. * * [--skip-check] * : If set, the database connection is not checked. * * ## EXAMPLES * * # Standard wp-config.php file * wp core config --dbname=testing --dbuser=wp --dbpass=securepswd --locale=ro_RO * * # Enable WP_DEBUG and WP_DEBUG_LOG * wp core config --dbname=testing --dbuser=wp --dbpass=securepswd --extra-php < 'localhost', 'dbpass' => '', 'dbprefix' => 'wp_', 'dbcharset' => 'utf8', 'dbcollate' => '', 'locale' => self::get_initial_locale() ); $assoc_args = array_merge( $defaults, $assoc_args ); if ( preg_match( '|[^a-z0-9_]|i', $assoc_args['dbprefix'] ) ) WP_CLI::error( '--dbprefix can only contain numbers, letters, and underscores.' ); // Check DB connection if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-check' ) ) { Utils\run_mysql_command( 'mysql --no-defaults', array( 'execute' => ';', 'host' => $assoc_args['dbhost'], 'user' => $assoc_args['dbuser'], 'pass' => $assoc_args['dbpass'], ) ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'extra-php' ) === true ) { $assoc_args['extra-php'] = file_get_contents( 'php://stdin' ); } // TODO: adapt more resilient code from wp-admin/setup-config.php if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-salts' ) ) { $assoc_args['keys-and-salts'] = self::_read( 'https://api.wordpress.org/secret-key/1.1/salt/' ); } if ( \WP_CLI\Utils\wp_version_compare( '4.0', '<' ) ) { $assoc_args['add-wplang'] = true; } else { $assoc_args['add-wplang'] = false; } $out = Utils\mustache_render( 'wp-config.mustache', $assoc_args ); $bytes_written = file_put_contents( ABSPATH . 'wp-config.php', $out ); if ( ! $bytes_written ) { WP_CLI::error( 'Could not create new wp-config.php file.' ); } else { WP_CLI::success( 'Generated wp-config.php file.' ); } } /** * Determine if the WordPress tables are installed. * * [--network] * : Check if this is a multisite install * * ## EXAMPLES * * if ! $(wp core is-installed); then * wp core install * fi * * @subcommand is-installed */ public function is_installed( $_, $assoc_args ) { if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ) ) { if ( is_blog_installed() && is_multisite() ) { exit( 0 ); } else { exit( 1 ); } } else if ( is_blog_installed() ) { exit( 0 ); } else { exit( 1 ); } } /** * Create the WordPress tables in the database. * * ## OPTIONS * * --url= * : The address of the new site. * * --title= * : The title of the new site. * * --admin_user= * : The name of the admin user. * * --admin_password= * : The password for the admin user. * * --admin_email= * : The email address for the admin user. * * [--skip-email] * : Don't send an email notification to the new admin user. */ public function install( $args, $assoc_args ) { if ( $this->_install( $assoc_args ) ) { WP_CLI::success( 'WordPress installed successfully.' ); } else { WP_CLI::log( 'WordPress is already installed.' ); } } /** * Transform a single-site install into a multi-site install. * * ## OPTIONS * * [--title=] * : The title of the new network. * * [--base=] * : Base path after the domain name that each site url will start with. * Default: '/' * * [--subdomains] * : If passed, the network will use subdomains, instead of subdirectories. Doesn't work with 'localhost'. * * @subcommand multisite-convert * @alias install-network */ public function multisite_convert( $args, $assoc_args ) { if ( is_multisite() ) WP_CLI::error( 'This already is a multisite install.' ); $assoc_args = self::_set_multisite_defaults( $assoc_args ); if ( !isset( $assoc_args['title'] ) ) { $assoc_args['title'] = sprintf( _x('%s Sites', 'Default network name' ), get_option( 'blogname' ) ); } if ( $this->_multisite_convert( $assoc_args ) ) { WP_CLI::success( "Network installed. Don't forget to set up rewrite rules." ); } } /** * Install multisite from scratch. * * ## OPTIONS * * [--url=] * : The address of the new site. * * [--base=] * : Base path after the domain name that each site url in the network will start with. * Default: '/' * * [--subdomains] * : If passed, the network will use subdomains, instead of subdirectories. Doesn't work with 'localhost'. * * --title= * : The title of the new site. * * --admin_user= * : The name of the admin user. Default: 'admin' * * --admin_password= * : The password for the admin user. * * --admin_email= * : The email address for the admin user. * * [--skip-email] * : Don't send an email notification to the new admin user. * * @subcommand multisite-install */ public function multisite_install( $args, $assoc_args ) { if ( $this->_install( $assoc_args ) ) { WP_CLI::log( 'Created single site database tables.' ); } else { WP_CLI::log( 'Single site database tables already present.' ); } $assoc_args = self::_set_multisite_defaults( $assoc_args ); $assoc_args['title'] = sprintf( _x('%s Sites', 'Default network name' ), $assoc_args['title'] ); // Overwrite runtime args, to avoid mismatches. $consts_to_args = array( 'SUBDOMAIN_INSTALL' => 'subdomains', 'PATH_CURRENT_SITE' => 'base', 'SITE_ID_CURRENT_SITE' => 'site_id', 'BLOG_ID_CURRENT_SITE' => 'blog_id', ); foreach ( $consts_to_args as $const => $arg ) { if ( defined( $const ) ) { $assoc_args[ $arg ] = constant( $const ); } } if ( !$this->_multisite_convert( $assoc_args ) ) { return; } // Do the steps that were skipped by populate_network(), // which checks is_multisite(). if ( is_multisite() ) { $site_user = get_user_by( 'email', $assoc_args['admin_email'] ); self::add_site_admins( $site_user ); $domain = self::get_clean_basedomain(); self::create_initial_blog( $assoc_args['site_id'], $assoc_args['blog_id'], $domain, $assoc_args['base'], $assoc_args['subdomains'], $site_user ); } WP_CLI::success( "Network installed. Don't forget to set up rewrite rules." ); } private static function _set_multisite_defaults( $assoc_args ) { $defaults = array( 'subdomains' => false, 'base' => '/', 'site_id' => 1, 'blog_id' => 1, ); return array_merge( $defaults, $assoc_args ); } private function _install( $assoc_args ) { if ( is_blog_installed() ) { return false; } if ( true === \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-email' ) && ! function_exists( 'wp_new_blog_notification' ) ) { function wp_new_blog_notification() { // Silence is golden } } require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); extract( wp_parse_args( $assoc_args, array( 'title' => '', 'admin_user' => '', 'admin_email' => '', 'admin_password' => '' ) ), EXTR_SKIP ); // Support prompting for the `--url=`, // which is normally a runtime argument if ( isset( $assoc_args['url'] ) ) { WP_CLI::set_url( $assoc_args['url'] ); } $public = true; // @codingStandardsIgnoreStart if ( !is_email( $admin_email ) ) { WP_CLI::error( "The '{$admin_email}' email address is invalid." ); } $result = wp_install( $title, $admin_user, $admin_email, $public, '', $admin_password ); if ( is_wp_error( $result ) ) { WP_CLI::error( 'Installation failed (' . WP_CLI::error_to_string($result) . ').' ); } // @codingStandardsIgnoreEnd if ( ! empty( $GLOBALS['wpdb']->last_error ) ) { WP_CLI::error( 'Installation produced database errors, and may have partially or completely failed.' ); } // Confirm the uploads directory exists $upload_dir = wp_upload_dir(); if ( ! empty( $upload_dir['error'] ) ) { WP_CLI::warning( $upload_dir['error'] ); } return true; } private function _multisite_convert( $assoc_args ) { global $wpdb; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); $domain = self::get_clean_basedomain(); if ( 'localhost' === $domain && ! empty( $assoc_args['subdomains'] ) ) { WP_CLI::error( "Multisite with subdomains cannot be configured when domain is 'localhost'." ); } // need to register the multisite tables manually for some reason foreach ( $wpdb->tables( 'ms_global' ) as $table => $prefixed_table ) $wpdb->$table = $prefixed_table; install_network(); $result = populate_network( $assoc_args['site_id'], $domain, get_option( 'admin_email' ), $assoc_args['title'], $assoc_args['base'], $assoc_args['subdomains'] ); if ( true === $result ) { WP_CLI::log( 'Set up multisite database tables.' ); } else if ( is_wp_error( $result ) ) { switch ( $result->get_error_code() ) { case 'siteid_exists': WP_CLI::log( $result->get_error_message() ); return false; case 'no_wildcard_dns': WP_CLI::warning( __( 'Wildcard DNS may not be configured correctly.' ) ); break; default: WP_CLI::error( $result ); } } // delete_site_option() cleans the alloptions cache to prevent dupe option delete_site_option( 'upload_space_check_disabled' ); update_site_option( 'upload_space_check_disabled', 1 ); if ( !is_multisite() ) { $subdomain_export = Utils\get_flag_value( $assoc_args, 'subdomains' ) ? 'true' : 'false'; $ms_config = <<domain = $domain; $current_site->path = $path; $current_site->site_name = ucfirst( $domain ); $wpdb->insert( $wpdb->blogs, array( 'site_id' => $network_id, 'domain' => $domain, 'path' => $path, 'registered' => current_time( 'mysql' ) ) ); $current_site->blog_id = $blog_id = $wpdb->insert_id; update_user_meta( $site_user->ID, 'source_domain', $domain ); update_user_meta( $site_user->ID, 'primary_blog', $blog_id ); if ( $subdomain_install ) $wp_rewrite->set_permalink_structure( '/%year%/%monthnum%/%day%/%postname%/' ); else $wp_rewrite->set_permalink_structure( '/blog/%year%/%monthnum%/%day%/%postname%/' ); flush_rewrite_rules(); } // copied from populate_network() private static function add_site_admins( $site_user ) { $site_admins = array( $site_user->user_login ); $users = get_users( array( 'fields' => array( 'ID', 'user_login' ) ) ); if ( $users ) { foreach ( $users as $user ) { if ( is_super_admin( $user->ID ) && !in_array( $user->user_login, $site_admins ) ) $site_admins[] = $user->user_login; } } update_site_option( 'site_admins', $site_admins ); } private static function modify_wp_config( $content ) { $wp_config_path = Utils\locate_wp_config(); $token = "/* That's all, stop editing!"; list( $before, $after ) = explode( $token, file_get_contents( $wp_config_path ) ); file_put_contents( $wp_config_path, $before . $content . $token . $after ); } private static function get_clean_basedomain() { $domain = preg_replace( '|https?://|', '', get_option( 'siteurl' ) ); if ( $slash = strpos( $domain, '/' ) ) $domain = substr( $domain, 0, $slash ); return $domain; } /** * Display the WordPress version. * * ## OPTIONS * * [--extra] * : Show extended version information. * * @when before_wp_load */ public function version( $args = array(), $assoc_args = array() ) { $versions_path = ABSPATH . 'wp-includes/version.php'; if ( !is_readable( $versions_path ) ) { WP_CLI::error( "This does not seem to be a WordPress install.\n" . "Pass --path=`path/to/wordpress` or run `wp core download`." ); } include $versions_path; // @codingStandardsIgnoreStart if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'extra' ) ) { if ( preg_match( '/(\d)(\d+)-/', $tinymce_version, $match ) ) { $human_readable_tiny_mce = $match[1] . '.' . $match[2]; } else { $human_readable_tiny_mce = ''; } echo \WP_CLI\Utils\mustache_render( 'versions.mustache', array( 'wp-version' => $wp_version, 'db-version' => $wp_db_version, 'mce-version' => ( $human_readable_tiny_mce ? "$human_readable_tiny_mce ($tinymce_version)" : $tinymce_version ) ) ); } else { WP_CLI::line( $wp_version ); } // @codingStandardsIgnoreEnd } /** * Security copy of the core function with Requests - Gets the checksums for the given version of WordPress. * * @param string $version Version string to query. * @param string $locale Locale to query. * @return bool|array False on failure. An array of checksums on success. */ private static function get_core_checksums( $version, $locale ) { $url = 'https://api.wordpress.org/core/checksums/1.0/?' . http_build_query( compact( 'version', 'locale' ), null, '&' ); $options = array( 'timeout' => 30 ); $headers = array( 'Accept' => 'application/json' ); $response = Utils\http_request( 'GET', $url, null, $headers, $options ); if ( ! $response->success || 200 != $response->status_code ) return false; $body = trim( $response->body ); $body = json_decode( $body, true ); if ( ! is_array( $body ) || ! isset( $body['checksums'] ) || ! is_array( $body['checksums'] ) ) return false; return $body['checksums']; } /** * Verify WordPress files against WordPress.org's checksums. * * Specify version to verify checksums without loading WordPress. * * [--version=] * : Verify checksums against a specific version of WordPress. * * [--locale=] * : Verify checksums against a specific locale of WordPress. * * @when before_wp_load * * @subcommand verify-checksums */ public function verify_checksums( $args, $assoc_args ) { global $wp_version, $wp_local_package; if ( ! empty( $assoc_args['version'] ) ) { $wp_version = $assoc_args['version']; } if ( ! empty( $assoc_args['locale'] ) ) { $wp_local_package = $assoc_args['locale']; } if ( empty( $wp_version ) ) { WP_CLI::get_runner()->load_wordpress(); } $checksums = self::get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' ); if ( ! is_array( $checksums ) ) { WP_CLI::error( "Couldn't get checksums from WordPress.org." ); } $has_errors = false; foreach ( $checksums as $file => $checksum ) { // Skip files which get updated if ( 'wp-content' == substr( $file, 0, 10 ) ) { continue; } if ( ! file_exists( ABSPATH . $file ) ) { WP_CLI::warning( "File doesn't exist: {$file}" ); $has_errors = true; continue; } $md5_file = md5_file( ABSPATH . $file ); if ( $md5_file !== $checksum ) { WP_CLI::warning( "File doesn't verify against checksum: {$file}" ); $has_errors = true; } } if ( ! $has_errors ) { WP_CLI::success( "WordPress install verifies against checksums." ); } else { WP_CLI::error( "WordPress install doesn't verify against checksums." ); } } /** * Update WordPress. * * ## OPTIONS * * [] * : Path to zip file to use, instead of downloading from wordpress.org. * * [--minor] * : Only perform updates for minor releases. * * [--version=] * : Update to a specific version, instead of to the latest version. * * [--force] * : Update even when installed WP version is greater than the requested version. * * [--locale=] * : Select which language you want to download. * * ## EXAMPLES * * wp core update * * wp core update --version=3.8 ../latest.zip * * wp core update --version=3.1 --force * * @alias upgrade */ function update( $args, $assoc_args ) { global $wp_version; $update = $from_api = null; $upgrader = 'WP_CLI\\CoreUpgrader'; if ( empty( $args[0] ) && empty( $assoc_args['version'] ) && \WP_CLI\Utils\get_flag_value( $assoc_args, 'minor' ) ) { $updates = $this->get_updates( array( 'minor' => true ) ); if ( ! empty( $updates ) ) { $assoc_args['version'] = $updates[0]['version']; } else { WP_CLI::success( 'WordPress is at the latest minor release.' ); return; } } if ( ! empty( $args[0] ) ) { $upgrader = 'WP_CLI\\NonDestructiveCoreUpgrader'; $version = \WP_CLI\Utils\get_flag_value( $assoc_args, 'version' ); $update = (object) array( 'response' => 'upgrade', 'current' => $version, 'download' => $args[0], 'packages' => (object) array ( 'partial' => null, 'new_bundled' => null, 'no_content' => null, 'full' => $args[0], ), 'version' => $version, 'locale' => null ); } else if ( empty( $assoc_args['version'] ) ) { wp_version_check(); $from_api = get_site_transient( 'update_core' ); if ( ! empty( $from_api->updates ) ) { list( $update ) = $from_api->updates; } } else if ( \WP_CLI\Utils\wp_version_compare( $assoc_args['version'], '<' ) || \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ) ) { $version = $assoc_args['version']; $locale = \WP_CLI\Utils\get_flag_value( $assoc_args, 'locale', get_locale() ); $new_package = $this->get_download_url($version, $locale); $update = (object) array( 'response' => 'upgrade', 'current' => $assoc_args['version'], 'download' => $new_package, 'packages' => (object) array ( 'partial' => null, 'new_bundled' => null, 'no_content' => null, 'full' => $new_package, ), 'version' => $version, 'locale' => $locale ); } if ( ! empty( $update ) && ( $update->version != $wp_version || \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ) ) ) { require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); if ( $update->version ) { WP_CLI::log( "Updating to version {$update->version} ({$update->locale})..." ); } else { WP_CLI::log( "Starting update..." ); } $GLOBALS['wp_cli_update_obj'] = $update; $result = Utils\get_upgrader( $upgrader )->upgrade( $update ); unset( $GLOBALS['wp_cli_update_obj'] ); if ( is_wp_error($result) ) { $msg = WP_CLI::error_to_string( $result ); if ( 'up_to_date' != $result->get_error_code() ) { WP_CLI::error( $msg ); } else { WP_CLI::success( $msg ); } } else { WP_CLI::success( 'WordPress updated successfully.' ); } } else { WP_CLI::success( 'WordPress is up to date.' ); } } /** * Update the WordPress database. * * [--network] * : Update databases for all sites on a network * * [--dry-run] * : Compare database versions without performing the update. * * @subcommand update-db */ function update_db( $_, $assoc_args ) { global $wpdb, $wp_db_version, $wp_current_db_version; $network = Utils\get_flag_value( $assoc_args, 'network' ); if ( $network && ! is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } $dry_run = Utils\get_flag_value( $assoc_args, 'dry-run' ); if ( $dry_run ) { WP_CLI::log( 'Performing a dry run, with no database modification.' ); } if ( $network ) { $iterator_args = array( 'table' => $wpdb->blogs, 'where' => array( 'spam' => 0, 'deleted' => 0, 'archived' => 0 ), ); $it = new \WP_CLI\Iterators\Table( $iterator_args ); $success = $total = 0; foreach( $it as $blog ) { $total++; $url = $blog->domain . $blog->path; $process = WP_CLI::launch_self( 'core update-db', array(), array(), false, true, array( 'url' => $url, 'dry-run' => $dry_run ) ); if ( 0 == $process->return_code ) { // See if we can parse the stdout if ( preg_match( '#Success: (.+)#', $process->stdout, $matches ) ) { $message = "{$matches[1]} on {$url}"; } else { $message = "Database upgraded successfully on {$url}"; } WP_CLI::log( $message ); $success++; } else { WP_CLI::warning( "Database failed to upgrade on {$url}" ); } } if ( ! $dry_run && $total && $success == $total ) { update_site_option( 'wpmu_upgrade_site', $wp_db_version ); } WP_CLI::success( sprintf( 'WordPress database upgraded on %d/%d sites', $success, $total ) ); } else { require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); $wp_current_db_version = __get_option( 'db_version' ); if ( $wp_db_version != $wp_current_db_version ) { if ( ! $dry_run ) { wp_upgrade(); } WP_CLI::success( "WordPress database upgraded successfully from db version {$wp_current_db_version} to {$wp_db_version}" ); } else { WP_CLI::success( "WordPress database already at latest db version {$wp_db_version}" ); } } } /** * Gets download url based on version, locale and desired file type. * * @param $version * @param string $locale * @param string $file_type * @return string */ private function get_download_url($version, $locale = 'en_US', $file_type = 'zip') { if ('en_US' === $locale) { $url = 'https://wordpress.org/wordpress-' . $version . '.' . $file_type; return $url; } else { $url = sprintf( 'https://%s.wordpress.org/wordpress-%s-%s.' . $file_type, substr($locale, 0, 2), $version, $locale ); return $url; } } /** * Returns update information */ private function get_updates( $assoc_args ) { global $wp_version; $versions_path = ABSPATH . 'wp-includes/version.php'; include $versions_path; $url = 'https://api.wordpress.org/core/stable-check/1.0/'; $options = array( 'timeout' => 30 ); $headers = array( 'Accept' => 'application/json' ); $response = Utils\http_request( 'GET', $url, $headers, $options ); if ( ! $response->success || 200 !== $response->status_code ) { WP_CLI::error( "Failed to get latest version list." ); } $release_data = json_decode( $response->body ); $release_versions = array_keys( (array) $release_data ); usort( $release_versions, function( $a, $b ){ return 1 === version_compare( $a, $b ); }); $locale = get_locale(); $compare_version = str_replace( '-src', '', $GLOBALS['wp_version'] ); $updates = array( 'major' => false, 'minor' => false, ); foreach ( $release_versions as $release_version ) { $update_type = Utils\get_named_sem_ver( $release_version, $compare_version ); if ( ! $update_type ) { continue; } // WordPress follow its own versioning which is roughly equivalent to semver if ( 'minor' === $update_type ) { $update_type = 'major'; } else if ( 'patch' === $update_type ) { $update_type = 'minor'; } if ( ! empty( $updates[ $update_type ] ) && ! Comparator::greaterThan( $release_version, $updates[ $update_type ]['version'] ) ) { continue; } $updates[ $update_type ] = array( 'version' => $release_version, 'update_type' => $update_type, 'package_url' => $this->get_download_url( $release_version, $locale ) ); } foreach( $updates as $type => $value ) { if ( empty( $value ) ) { unset( $updates[ $type ] ); } } foreach( array( 'major', 'minor' ) as $type ) { if ( true === \WP_CLI\Utils\get_flag_value( $assoc_args, $type ) ) { return ! empty( $updates[ $type ] ) ? array( $updates[ $type ] ) : false; } } return array_values( $updates ); } } WP_CLI::add_command( 'core', 'Core_Command' ); class Core_Language_Command extends WP_CLI\CommandWithTranslation { protected $obj_type = 'core'; } WP_CLI::add_command( 'core language', 'Core_Language_Command', array( 'before_invoke' => function() { if ( \WP_CLI\Utils\wp_version_compare( '4.0', '<' ) ) { WP_CLI::error( "Requires WordPress 4.0 or greater." ); } }) ); ] * : Limit the output to specific object fields. * * [--format=] * : Accepted values: table, json, csv, ids. Default: table. * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each cron event: * * hook * * next_run_gmt * * next_run_relative * * recurrence * * These fields are optionally available: * * time * * sig * * args * * schedule * * interval * * next_run * * ## EXAMPLES * * wp cron event list * * wp cron event list --fields=hook,next_run --format=json * * @subcommand list */ public function list_( $args, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); $events = self::get_cron_events(); if ( is_wp_error( $events ) ) { $events = array(); } if ( 'ids' == $formatter->format ) { echo implode( ' ', wp_list_pluck( $events, 'hook' ) ); } else { $formatter->display_items( $events ); } } /** * Schedule a new cron event. * * ## OPTIONS * * * : The hook name * * [] * : A Unix timestamp or an English textual datetime description compatible with `strtotime()`. Defaults to now. * * [] * : How often the event should recur. See `wp cron schedule list` for available schedule names. Defaults to no recurrence. * * [--=] * : Associative args for the event. * * ## EXAMPLES * * wp cron event schedule cron_test * * wp cron event schedule cron_test now hourly * * wp cron event schedule cron_test '+1 hour' --foo=1 --bar=2 */ public function schedule( $args, $assoc_args ) { $hook = $args[0]; $next_run = \WP_CLI\Utils\get_flag_value( $args, 1, 'now' ); $recurrence = \WP_CLI\Utils\get_flag_value( $args, 2, false ); if ( empty( $next_run ) ) { $timestamp = time(); } else if ( is_numeric( $next_run ) ) { $timestamp = absint( $next_run ); } else { $timestamp = strtotime( $next_run ); } if ( ! $timestamp ) { WP_CLI::error( sprintf( "'%s' is not a valid datetime.", $next_run ) ); } if ( ! empty( $recurrence ) ) { $schedules = wp_get_schedules(); if ( ! isset( $schedules[$recurrence] ) ) { WP_CLI::error( sprintf( "'%s' is not a valid schedule name for recurrence.", $recurrence ) ); } $event = wp_schedule_event( $timestamp, $recurrence, $hook, $assoc_args ); } else { $event = wp_schedule_single_event( $timestamp, $hook, $assoc_args ); } if ( false !== $event ) { WP_CLI::success( sprintf( "Scheduled event with hook '%s' for %s GMT.", $hook, date( self::$time_format, $timestamp ) ) ); } else { WP_CLI::error( 'Event not scheduled' ); } } /** * Run the next scheduled cron event for the given hook. * * ## OPTIONS * * [...] * : One or more hooks to run. * * [--all] * : Run all hooks. */ public function run( $args, $assoc_args ) { if ( empty( $args ) && ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { WP_CLI::error( 'Please specify one or more cron events, or use --all.' ); } $events = self::get_cron_events(); if ( is_wp_error( $events ) ) { WP_CLI::error( $events ); } if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { $hooks = wp_list_pluck( $events, 'hook' ); foreach( $args as $hook ) { if ( ! in_array( $hook, $hooks ) ) { WP_CLI::error( sprintf( "Invalid cron event '%s'", $hook ) ); } } } $executed = 0; foreach ( $events as $event ) { if ( in_array( $event->hook, $args ) || \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { $result = self::run_event( $event ); if ( $result ) { $executed++; WP_CLI::log( sprintf( "Executed the cron event '%s'.", $event->hook ) ); } else { WP_CLI::warning( sprintf( "Failed to the execute the cron event '%s'.", $event->hook ) ); } } } $message = 'Executed a total of %d cron event(s).'; WP_CLI::success( sprintf( $message, $executed ) ); } /** * Executes an event immediately. * * @param stdClass $event The event * @return bool Whether the event was successfully executed or not. */ protected static function run_event( stdClass $event ) { if ( ! defined( 'DOING_CRON' ) ) { define( 'DOING_CRON', true ); } if ( $event->schedule != false ) { $new_args = array( $event->time, $event->schedule, $event->hook, $event->args ); call_user_func_array( 'wp_reschedule_event', $new_args ); } wp_unschedule_event( $event->time, $event->hook, $event->args ); do_action_ref_array( $event->hook, $event->args ); return true; } /** * Delete the next scheduled cron event for the given hook. * * ## OPTIONS * * * : The hook name */ public function delete( $args, $assoc_args ) { $hook = $args[0]; $events = self::get_cron_events(); if ( is_wp_error( $events ) ) { WP_CLI::error( $events ); } $deleted = 0; foreach ( $events as $event ) { if ( $event->hook == $hook ) { $result = self::delete_event( $event ); if ( $result ) { $deleted++; } else { WP_CLI::warning( sprintf( "Failed to the delete the cron event '%s'", $hook ) ); } } } if ( $deleted ) { $message = ( 1 == $deleted ) ? "Deleted the cron event '%2\$s'" : "Deleted %1\$d instances of the cron event '%2\$s'"; WP_CLI::success( sprintf( $message, $deleted, $hook ) ); } else { WP_CLI::error( sprintf( "Invalid cron event '%s'", $hook ) ); } } /** * Deletes a cron event. * * @param stdClass $event The event * @return bool Whether the event was successfully deleted or not. */ protected static function delete_event( stdClass $event ) { $crons = _get_cron_array(); if ( ! isset( $crons[$event->time][$event->hook][$event->sig] ) ) { return false; } wp_unschedule_event( $event->time, $event->hook, $event->args ); return true; } /** * Callback function to format a cron event. * * @param stdClass $event The event. * @return stdClass The formatted event object. */ protected static function format_event( stdClass $event ) { $event->next_run = get_date_from_gmt( date( 'Y-m-d H:i:s', $event->time ), self::$time_format ); $event->next_run_gmt = date( self::$time_format, $event->time ); $event->next_run_relative = self::interval( $event->time - time() ); $event->recurrence = ( $event->schedule ) ? self::interval( $event->interval ) : 'Non-repeating'; return $event; } /** * Fetch an array of scheduled cron events. * * @return array|WP_Error An array of event objects, or a WP_Error object if there are no events scheduled. */ protected static function get_cron_events() { $crons = _get_cron_array(); $events = array(); if ( empty( $crons ) ) { return new WP_Error( 'no_events', 'You currently have no scheduled cron events.' ); } foreach ( $crons as $time => $hooks ) { foreach ( $hooks as $hook => $hook_events ) { foreach ( $hook_events as $sig => $data ) { $events["$hook-$sig"] = (object) array( 'hook' => $hook, 'time' => $time, 'sig' => $sig, 'args' => $data['args'], 'schedule' => $data['schedule'], 'interval' => \WP_CLI\Utils\get_flag_value( $data, 'interval' ), ); } } } $events = array_map( 'Cron_Event_Command::format_event', $events ); return $events; } /** * Convert a time interval into human-readable format. * * Similar to WordPress' built-in `human_time_diff()` but returns two time period chunks instead of just one. * * @param int $since An interval of time in seconds * @return string The interval in human readable format */ private static function interval( $since ) { if ( $since <= 0 ) { return 'now'; } $since = absint( $since ); // array of time period chunks $chunks = array( array( 60 * 60 * 24 * 365 , \_n_noop( '%s year', '%s years' ) ), array( 60 * 60 * 24 * 30 , \_n_noop( '%s month', '%s months' ) ), array( 60 * 60 * 24 * 7, \_n_noop( '%s week', '%s weeks' ) ), array( 60 * 60 * 24 , \_n_noop( '%s day', '%s days' ) ), array( 60 * 60 , \_n_noop( '%s hour', '%s hours' ) ), array( 60 , \_n_noop( '%s minute', '%s minutes' ) ), array( 1 , \_n_noop( '%s second', '%s seconds' ) ), ); // we only want to output two chunks of time here, eg: // x years, xx months // x days, xx hours // so there's only two bits of calculation below: // step one: the first chunk for ( $i = 0, $j = count( $chunks ); $i < $j; $i++ ) { $seconds = $chunks[$i][0]; $name = $chunks[$i][1]; // finding the biggest chunk (if the chunk fits, break) if ( ( $count = floor( $since / $seconds ) ) != 0 ){ break; } } // set output var $output = sprintf( \_n( $name[0], $name[1], $count ), $count ); // step two: the second chunk if ( $i + 1 < $j ) { $seconds2 = $chunks[$i + 1][0]; $name2 = $chunks[$i + 1][1]; if ( ( $count2 = floor( ( $since - ( $seconds * $count ) ) / $seconds2 ) ) != 0 ) { // add to output var $output .= ' ' . sprintf( \_n( $name2[0], $name2[1], $count2 ), $count2 ); } } return $output; } private function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->fields, 'event' ); } } /** * Manage WP-Cron schedules. */ class Cron_Schedule_Command extends WP_CLI_Command { private $fields = array( 'name', 'display', 'interval', ); /** * List available cron schedules. * * ## OPTIONS * * [--fields=] * : Limit the output to specific object fields. * * [--format=] * : Accepted values: table, json, csv, ids. Default: table. * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each cron schedule: * * * name * * display * * interval * * There are no additional fields. * * ## EXAMPLES * * wp cron schedule list * * wp cron schedule list --fields=name --format=ids * * @subcommand list */ public function list_( $args, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); $schedules = self::get_schedules(); if ( 'ids' == $formatter->format ) { echo implode( ' ', wp_list_pluck( $schedules, 'name' ) ); } else { $formatter->display_items( $schedules ); } } /** * Callback function to format a cron schedule. * * @param array $schedule The schedule. * @param string $name The schedule name. * @return array The formatted schedule. */ protected static function format_schedule( array $schedule, $name ) { $schedule['name'] = $name; return $schedule; } /** * Return a list of the cron schedules sorted according to interval. * * @return array The array of cron schedules. Each schedule is itself an array. */ protected static function get_schedules() { $schedules = wp_get_schedules(); if ( !empty( $schedules ) ) { uasort( $schedules, 'Cron_Schedule_Command::sort' ); $schedules = array_map( 'Cron_Schedule_Command::format_schedule', $schedules, array_keys( $schedules ) ); } return $schedules; } /** * Callback function to sort the cron schedule array by interval. * */ protected static function sort( array $a, array $b ) { return $a['interval'] - $b['interval']; } private function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->fields, 'schedule' ); } } /** * Manage WP-Cron events and schedules. */ class Cron_Command extends WP_CLI_Command { /** * Test the WP Cron spawning system and report back its status. */ public function test() { if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) { WP_CLI::error( 'The DISABLE_WP_CRON constant is set to true. WP-Cron spawning is disabled.' ); } if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) { WP_CLI::warning( 'The ALTERNATE_WP_CRON constant is set to true. WP-Cron spawning is not asynchronous.' ); } $spawn = self::get_cron_spawn(); if ( is_wp_error( $spawn ) ) { WP_CLI::error( sprintf( 'WP-Cron spawn failed with error: %s', $spawn->get_error_message() ) ); } $code = wp_remote_retrieve_response_code( $spawn ); $message = wp_remote_retrieve_response_message( $spawn ); if ( 200 === $code ) { WP_CLI::success( 'WP-Cron spawning is working as expected.' ); } else { WP_CLI::warning( sprintf( 'WP-Cron spawn succeeded but returned HTTP status code: %1$s %2$s', $code, $message ) ); } } /** * Spawn a request to `wp-cron.php` and return the response. * * This function is designed to mimic the functionality in `spawn_cron()` with the addition of returning * the result of the `wp_remote_post()` request. * * @return WP_Error|array The response or WP_Error on failure. */ protected static function get_cron_spawn() { $sslverify = \WP_CLI\Utils\wp_version_compare( 4.0, '<' ); $doing_wp_cron = sprintf( '%.22F', microtime( true ) ); $cron_request = apply_filters( 'cron_request', array( 'url' => site_url( 'wp-cron.php?doing_wp_cron=' . $doing_wp_cron ), 'key' => $doing_wp_cron, 'args' => array( 'timeout' => 3, 'blocking' => true, 'sslverify' => apply_filters( 'https_local_ssl_verify', $sslverify ) ) ) ); # Enforce a blocking request in case something that's hooked onto the 'cron_request' filter sets it to false $cron_request['args']['blocking'] = true; $result = wp_remote_post( $cron_request['url'], $cron_request['args'] ); return $result; } } WP_CLI::add_command( 'cron', 'Cron_Command' ); WP_CLI::add_command( 'cron event', 'Cron_Event_Command' ); WP_CLI::add_command( 'cron schedule', 'Cron_Schedule_Command' ); true, ) ); WP_CLI::success( "Database optimized." ); } /** * Repair the database. */ function repair() { self::run( Utils\esc_cmd( 'mysqlcheck --no-defaults %s', DB_NAME ), array( 'repair' => true, ) ); WP_CLI::success( "Database repaired." ); } /** * Open a mysql console using the WordPress credentials. * * @alias connect */ function cli() { self::run( 'mysql --no-defaults --no-auto-rehash', array( 'database' => DB_NAME ) ); } /** * Execute a query against the database. * * ## OPTIONS * * [] * : A SQL query. If not passed, will try to read from STDIN. * * ## EXAMPLES * * # execute a query stored in a file * wp db query < debug.sql * * # check all tables in the database * wp db query "CHECK TABLE $(wp db tables | paste -s -d',');" */ function query( $args ) { $assoc_args = array( 'database' => DB_NAME ); // The query might come from STDIN if ( !empty( $args ) ) { $assoc_args['execute'] = $args[0]; } self::run( 'mysql --no-defaults --no-auto-rehash', $assoc_args ); } /** * Exports the database to a file or to STDOUT. * * ## OPTIONS * * [] * : The name of the SQL file to export. If '-', then outputs to STDOUT. If omitted, it will be '{dbname}.sql'. * * [--=] * : Extra arguments to pass to mysqldump * * [--tables=] * : The comma separated list of specific tables to export. Excluding this parameter will export all tables in the database. * * ## EXAMPLES * * wp db export --add-drop-table * wp db export --tables=wp_options,wp_users * * # Export all tables matching a wildcard * wp db export --tables=$(wp db tables 'wp_user*' --format=csv) * * # Export all tables matching prefix * wp db export --tables=$(wp db tables --all-tables-with-prefix --format=csv) * * @alias dump */ function export( $args, $assoc_args ) { $result_file = $this->get_file_name( $args ); $stdout = ( '-' === $result_file ); if ( ! $stdout ) { $assoc_args['result-file'] = $result_file; } $command = 'mysqldump --no-defaults %s'; $command_esc_args = array( DB_NAME ); if ( isset( $assoc_args['tables'] ) ) { $tables = explode( ',', trim( $assoc_args['tables'], ',' ) ); unset( $assoc_args['tables'] ); $command .= ' --tables'; foreach ( $tables as $table ) { $command .= ' %s'; $command_esc_args[] = trim( $table ); } } $escaped_command = call_user_func_array( '\WP_CLI\Utils\esc_cmd', array_merge( array( $command ), $command_esc_args ) ); self::run( $escaped_command, $assoc_args ); if ( ! $stdout ) { WP_CLI::success( sprintf( 'Exported to %s', $result_file ) ); } } /** * Import database from a file or from STDIN. * * ## OPTIONS * * [] * : The name of the SQL file to import. If '-', then reads from STDIN. If omitted, it will look for '{dbname}.sql'. */ function import( $args, $assoc_args ) { $result_file = $this->get_file_name( $args ); if ( '-' === $result_file ) { $descriptors = array( STDIN, STDOUT, STDERR, ); } else { if ( ! file_exists( $result_file ) ) { WP_CLI::error( sprintf( 'Import file missing: %s', $result_file ) ); } $descriptors = array( array( 'file', $result_file, 'r' ), STDOUT, STDERR, ); } self::run( 'mysql --no-defaults --no-auto-rehash', array( 'database' => DB_NAME ), $descriptors ); WP_CLI::success( sprintf( 'Imported from %s', $result_file ) ); } /** * List the database tables. * * Defaults to all tables registered to $wpdb. * * ## OPTIONS * * [...] * : List tables based on wildcard search, e.g. 'wp_*_options' or 'wp_post?'. * * [--scope=] * : Can be all, global, ms_global, blog, or old tables. Defaults to all. * * [--network] * : List all the tables in a multisite install. Overrides --scope=. * * [--all-tables-with-prefix] * : List all tables that match the table prefix even if not registered on $wpdb. Overrides --network. * * [--all-tables] * : List all tables in the database, regardless of the prefix, and even if not registered on $wpdb. Overrides --all-tables-with-prefix. * * [--format=] * : Accepted values: list, csv. Default: list * * ## EXAMPLES * * # Export only tables for a single site * wp db export --tables=$(wp db tables --url=sub.example.com --format=csv) * * # Export all tables matching prefix * wp db export --tables=$(wp db tables --all-tables-with-prefix --format=csv) */ function tables( $args, $assoc_args ) { if ( empty( $args ) && empty( $assoc_args ) ) { $assoc_args['scope'] = 'all'; } $tables = WP_CLI\Utils\wp_get_table_names( $args, $assoc_args ); if ( ! empty( $assoc_args['format'] ) && 'csv' === $assoc_args['format'] ) { WP_CLI::line( implode( ',', $tables ) ); } else { foreach ( $tables as $table ) { WP_CLI::line( $table ); } } } private function get_file_name( $args ) { if ( empty( $args ) ) return sprintf( '%s.sql', DB_NAME ); return $args[0]; } private static function get_create_query() { $create_query = sprintf( 'CREATE DATABASE `%s`', DB_NAME ); if ( defined( 'DB_CHARSET' ) && constant( 'DB_CHARSET' ) ) { $create_query .= sprintf( ' DEFAULT CHARSET `%s`', constant( 'DB_CHARSET' ) ); } if ( defined( 'DB_COLLATE' ) && constant( 'DB_COLLATE' ) ) { $create_query .= sprintf( ' DEFAULT COLLATE `%s`', constant( 'DB_COLLATE' ) ); } return $create_query; } private static function run_query( $query ) { self::run( 'mysql --no-defaults --no-auto-rehash', array( 'execute' => $query ) ); } private static function run( $cmd, $assoc_args = array(), $descriptors = null ) { $required = array( 'host' => DB_HOST, 'user' => DB_USER, 'pass' => DB_PASSWORD, ); if ( defined( 'DB_CHARSET' ) && constant( 'DB_CHARSET' ) ) { $required['default-character-set'] = constant( 'DB_CHARSET' ); } $final_args = array_merge( $assoc_args, $required ); Utils\run_mysql_command( $cmd, $final_args, $descriptors ); } } WP_CLI::add_command( 'db', 'DB_Command' ); * : The path to the PHP file to execute. * * [...] * : One or more arguments to pass to the file. They are placed in the $args variable. * * [--skip-wordpress] * : Load and execute file without loading WordPress. * * @when before_wp_load * * ## EXAMPLES * * wp eval-file my-code.php value1 value2 */ public function __invoke( $args, $assoc_args ) { $file = array_shift( $args ); if ( !file_exists( $file ) ) { WP_CLI::error( "'$file' does not exist." ); } if ( null === \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-wordpress' ) ) { WP_CLI::get_runner()->load_wordpress(); } self::_eval( $file, $args ); } private static function _eval( $file, $args ) { include( $file ); } } WP_CLI::add_command( 'eval-file', 'EvalFile_Command' ); * : The code to execute, as a string. * * [--skip-wordpress] * : Execute code without loading WordPress. * * @when before_wp_load * * ## EXAMPLES * * wp eval 'echo WP_CONTENT_DIR;' */ public function __invoke( $args, $assoc_args ) { if ( null === \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-wordpress' ) ) { WP_CLI::get_runner()->load_wordpress(); } eval( $args[0] ); } } WP_CLI::add_command( 'eval', 'Eval_Command' ); ] * : Full path to directory where WXR export files should be stored. Defaults * to current working directory. * * [--skip_comments] * : Don't export comments. * * [--max_file_size=] * : A single export file should have this many megabytes. * * ## FILTERS * * [--start_date=] * : Export only posts published after this date, in format YYYY-MM-DD. * * [--end_date=] * : Export only posts published before this date, in format YYYY-MM-DD. * * [--post_type=] * : Export only posts with this post_type. Separate multiple post types with a * comma. Defaults to all. * * [--post_type__not_in=] * : Export all post types except those identified. Seperate multiple post types * with a comma. Defaults to none. * * [--post__in=] * : Export all posts specified as a comma- or space-separated list of IDs. * * [--start_id=] * : Export only posts with IDs greater than or equal to this post ID. * * [--author=] * : Export only posts by this author. Can be either user login or user ID. * * [--category=] * : Export only posts in this category. * * [--post_status=] * : Export only posts with this status. * * [--filename_format=] * : Use a custom format for export filenames. Defaults to '{site}.wordpress.{date}.{n}.xml'. * * ## EXAMPLES * * wp export --dir=/tmp/ --user=admin --post_type=post --start_date=2011-01-01 --end_date=2011-12-31 * * wp export --dir=/tmp/ --post__in=123,124,125 * * # Export a random subset of content * wp export --post__in=$(wp post list --post_type=post --orderby=rand --posts_per_page=8 --format=ids) */ public function __invoke( $_, $assoc_args ) { $defaults = array( 'dir' => NULL, 'start_date' => NULL, 'end_date' => NULL, 'post_type' => NULL, 'post_type__not_in' => NULL, 'author' => NULL, 'category' => NULL, 'post_status' => NULL, 'post__in' => NULL, 'start_id' => NULL, 'skip_comments' => NULL, 'max_file_size' => 15, 'filename_format' => '{site}.wordpress.{date}.{n}.xml', ); $assoc_args = wp_parse_args( $assoc_args, $defaults ); $this->validate_args( $assoc_args ); if ( !function_exists( 'wp_export' ) ) { self::load_export_api(); } WP_CLI::log( 'Starting export process...' ); add_action( 'wp_export_new_file', function( $file_path ) { WP_CLI::log( sprintf( "Writing to file %s", $file_path ) ); } ); try { wp_export( array( 'filters' => $this->export_args, 'writer' => 'WP_Export_Split_Files_Writer', 'writer_args' => array( 'max_file_size' => $this->max_file_size * MB_IN_BYTES, 'destination_directory' => $this->wxr_path, 'filename_template' => self::get_filename_template( $assoc_args['filename_format'] ), ) ) ); } catch ( Exception $e ) { WP_CLI::error( $e->getMessage() ); } WP_CLI::success( 'All done with export.' ); } private static function get_filename_template( $filename_format ) { $sitename = sanitize_key( get_bloginfo( 'name' ) ); if ( empty( $sitename ) ) { $sitename = 'site'; } return str_replace( array( '{site}', '{date}', '{n}' ), array( $sitename, date( 'Y-m-d' ), '%03d' ), $filename_format ); } private static function load_export_api() { if ( !defined( 'KB_IN_BYTES' ) ) { // Constants for expressing human-readable data sizes // in their respective number of bytes. define( 'KB_IN_BYTES', 1024 ); define( 'MB_IN_BYTES', 1024 * KB_IN_BYTES ); define( 'GB_IN_BYTES', 1024 * MB_IN_BYTES ); define( 'TB_IN_BYTES', 1024 * GB_IN_BYTES ); define( 'PB_IN_BYTES', 1024 * TB_IN_BYTES ); define( 'EB_IN_BYTES', 1024 * PB_IN_BYTES ); define( 'ZB_IN_BYTES', 1024 * EB_IN_BYTES ); define( 'YB_IN_BYTES', 1024 * ZB_IN_BYTES ); } require WP_CLI_ROOT . '/php/export/functions.export.php'; } private function validate_args( $args ) { $has_errors = false; foreach ( $args as $key => $value ) { if ( is_callable( array( $this, 'check_' . $key ) ) ) { $result = call_user_func( array( $this, 'check_' . $key ), $value ); if ( false === $result ) $has_errors = true; } } if ( $has_errors ) { exit(1); } } private function check_dir( $path ) { if ( empty( $path ) ) { $path = getcwd(); } elseif ( !is_dir( $path ) ) { WP_CLI::error( sprintf( "The directory %s does not exist", $path ) ); return false; } $this->wxr_path = trailingslashit( $path ); return true; } private function check_start_date( $date ) { if ( is_null( $date ) ) return true; $time = strtotime( $date ); if ( !empty( $date ) && !$time ) { WP_CLI::warning( sprintf( "The start_date %s is invalid", $date ) ); return false; } $this->export_args['start_date'] = date( 'Y-m-d', $time ); return true; } private function check_end_date( $date ) { if ( is_null( $date ) ) return true; $time = strtotime( $date ); if ( !empty( $date ) && !$time ) { WP_CLI::warning( sprintf( "The end_date %s is invalid", $date ) ); return false; } $this->export_args['end_date'] = date( 'Y-m-d', $time ); return true; } private function check_post_type( $post_type ) { if ( is_null( $post_type ) || 'any' === $post_type ) return true; $post_type = array_unique( array_filter( explode( ',', $post_type ) ) ); $post_types = get_post_types(); foreach ( $post_type as $type ) { if ( ! in_array( $type, $post_types ) ) { WP_CLI::warning( sprintf( 'The post type %s does not exist. Choose "any" or any of these existing post types instead: %s', $type, implode( ", ", $post_types ) ) ); return false; } } $this->export_args['post_type'] = $post_type; return true; } private function check_post_type__not_in( $post_type ) { if ( is_null( $post_type ) ) { return true; } $post_type = array_unique( array_filter( explode( ',', $post_type ) ) ); $post_types = get_post_types(); $keep_post_types = array(); foreach ( $post_type as $type ) { if ( ! in_array( $type, $post_types ) ) { WP_CLI::warning( sprintf( 'The post type %s does not exist. Use any of these existing post types instead: %s', $type, implode( ", ", $post_types ) ) ); return false; } } $this->export_args['post_type'] = array_diff( $post_types, $post_type ); return true; } private function check_post__in( $post__in ) { if ( is_null( $post__in ) ) return true; $separator = false !== stripos( $post__in, ' ' ) ? ' ' : ','; $post__in = array_unique( array_map( 'intval', explode( $separator, $post__in ) ) ); if ( empty( $post__in ) ) { WP_CLI::warning( "post__in should be comma-separated post IDs" ); return false; } // New exporter uses a different argument $this->export_args['post_ids'] = $post__in; return true; } private function check_start_id( $start_id ) { if ( is_null( $start_id ) ) { return true; } $start_id = intval( $start_id ); // Post IDs must be greater than 0 if ( 0 >= $start_id ) { WP_CLI::warning( sprintf( __( 'Invalid start ID: %d' ), $start_id ) ); return false; } $this->export_args['start_id'] = $start_id; return true; } private function check_author( $author ) { if ( is_null( $author ) ) return true; $authors = get_users_of_blog(); if ( empty( $authors ) || is_wp_error( $authors ) ) { WP_CLI::warning( sprintf( "Could not find any authors in this blog" ) ); return false; } $hit = false; foreach( $authors as $user ) { if ( $hit ) break; if ( (int) $author == $user->ID || $author == $user->user_login ) $hit = $user->ID; } if ( false === $hit ) { $authors_nice = array(); foreach( $authors as $_author ) $authors_nice[] = sprintf( '%s (%s)', $_author->user_login, $_author->display_name ); WP_CLI::warning( sprintf( 'Could not find a matching author for %s. The following authors exist: %s', $author, implode( ", ", $authors_nice ) ) ); return false; } $this->export_args['author'] = $hit; return true; } private function check_category( $category ) { if ( is_null( $category ) ) return true; $term = category_exists( $category ); if ( empty( $term ) || is_wp_error( $term ) ) { WP_CLI::warning( sprintf( 'Could not find a category matching %s', $category ) ); return false; } $this->export_args['category'] = $category; return true; } private function check_post_status( $status ) { if ( is_null( $status ) ) return true; $stati = get_post_statuses(); if ( empty( $stati ) || is_wp_error( $stati ) ) { WP_CLI::warning( 'Could not find any post stati' ); return false; } if ( !isset( $stati[$status] ) ) { WP_CLI::warning( sprintf( 'Could not find a post_status matching %s. Here is a list of available stati: %s', $status, implode( ", ", array_keys( $stati ) ) ) ); return false; } $this->export_args['status'] = $status; return true; } private function check_skip_comments( $skip ) { if ( is_null( $skip ) ) return true; if ( (int) $skip <> 0 && (int) $skip <> 1 ) { WP_CLI::warning( 'skip_comments needs to be 0 (no) or 1 (yes)' ); return false; } $this->export_args['skip_comments'] = $skip; return true; } private function check_max_file_size( $size ) { if ( !is_numeric( $size ) ) { WP_CLI::warning( sprintf( "max_file_size should be numeric", $size ) ); return false; } $this->max_file_size = $size; return true; } } WP_CLI::add_command( 'export', 'Export_Command' ); ...] * : Get help on a specific command. * * ## EXAMPLES * * # get help for `core` command * wp help core * * # get help for `core download` subcommand * wp help core download */ function __invoke( $args, $assoc_args ) { $command = self::find_subcommand( $args ); if ( $command ) { if ( WP_CLI::get_runner()->is_command_disabled( $command ) ) { $path = implode( ' ', array_slice( \WP_CLI\Dispatcher\get_path( $command ), 1 ) ); WP_CLI::error( sprintf( "The '%s' command has been disabled from the config file.", $path ) ); } self::show_help( $command ); exit; } // WordPress is already loaded, so there's no chance we'll find the command if ( function_exists( 'add_filter' ) ) { $command_string = implode( ' ', $args ); \WP_CLI::error( sprintf( "'%s' is not a registered wp command.", $command_string ) ); } } private static function find_subcommand( $args ) { $command = \WP_CLI::get_root_command(); while ( !empty( $args ) && $command && $command->can_have_subcommands() ) { $command = $command->find_subcommand( $args ); } return $command; } private static function show_help( $command ) { $out = self::get_initial_markdown( $command ); $longdesc = $command->get_longdesc(); if ( $longdesc ) { $out .= wordwrap( $longdesc, 90 ) . "\n"; } // definition lists $out = preg_replace_callback( '/([^\n]+)\n: (.+?)(\n\n|$)/s', array( __CLASS__, 'rewrap_param_desc' ), $out ); // Ensure all non-section headers are indented $out = preg_replace( '#^([^\s^\#])#m', "\t$1", $out ); // section headers $out = preg_replace( '/^## ([A-Z ]+)/m', WP_CLI::colorize( '%9\1%n' ), $out ); $out = str_replace( "\t", ' ', $out ); self::pass_through_pager( $out ); } private static function rewrap_param_desc( $matches ) { $param = $matches[1]; $desc = self::indent( "\t\t", wordwrap( $matches[2] ) ); return "\t$param\n$desc\n\n"; } private static function indent( $whitespace, $text ) { $lines = explode( "\n", $text ); foreach ( $lines as &$line ) { $line = $whitespace . $line; } return implode( $lines, "\n" ); } private static function pass_through_pager( $out ) { if ( false === ( $pager = getenv( 'PAGER' ) ) ) { $pager = Utils\is_windows() ? 'more' : 'less -r'; } // convert string to file handle $fd = fopen( "php://temp", "r+" ); fputs( $fd, $out ); rewind( $fd ); $descriptorspec = array( 0 => $fd, 1 => STDOUT, 2 => STDERR ); return proc_close( proc_open( $pager, $descriptorspec, $pipes ) ); } private static function get_initial_markdown( $command ) { $name = implode( ' ', Dispatcher\get_path( $command ) ); $binding = array( 'name' => $name, 'shortdesc' => $command->get_shortdesc(), ); $binding['synopsis'] = wordwrap( "$name " . $command->get_synopsis(), 79 ); if ( $command->can_have_subcommands() ) { $binding['has-subcommands']['subcommands'] = self::render_subcommands( $command ); } return Utils\mustache_render( 'man.mustache', $binding ); } private static function render_subcommands( $command ) { $subcommands = array(); foreach ( $command->get_subcommands() as $subcommand ) { if ( WP_CLI::get_runner()->is_command_disabled( $subcommand ) ) { continue; } $subcommands[ $subcommand->get_name() ] = $subcommand->get_shortdesc(); } $max_len = self::get_max_len( array_keys( $subcommands ) ); $lines = array(); foreach ( $subcommands as $name => $desc ) { $lines[] = str_pad( $name, $max_len ) . "\t\t\t" . $desc; } return $lines; } private static function get_max_len( $strings ) { $max_len = 0; foreach ( $strings as $str ) { $len = strlen( $str ); if ( $len > $max_len ) $max_len = $len; } return $max_len; } } WP_CLI::add_command( 'help', 'Help_Command' ); ... * : Path to one or more valid WXR files for importing. Directories are also accepted. * * --authors= * : How the author mapping should be handled. Options are 'create', 'mapping.csv', or 'skip'. The first will create any non-existent users from the WXR file. The second will read author mapping associations from a CSV, or create a CSV for editing if the file path doesn't exist. The CSV requires two columns, and a header row like "old_user_login,new_user_login". The last option will skip any author mapping. * * [--skip=] * : Skip importing specific data. Supported options are: 'attachment' and 'image_resize' (skip time-consuming thumbnail generation). */ public function __invoke( $args, $assoc_args ) { $defaults = array( 'authors' => null, 'skip' => array(), ); $assoc_args = wp_parse_args( $assoc_args, $defaults ); if ( ! is_array( $assoc_args['skip'] ) ) { $assoc_args['skip'] = explode( ',', $assoc_args['skip'] ); } $importer = $this->is_importer_available(); if ( is_wp_error( $importer ) ) { WP_CLI::error( $importer ); } $this->add_wxr_filters(); WP_CLI::log( 'Starting the import process...' ); $new_args = array(); foreach( $args as $arg ) { if ( is_dir( $arg ) ) { $files = glob( rtrim( $arg, '/' ) . '/*.{wxr,xml}', GLOB_BRACE ); $new_args = array_merge( $new_args, $files ); } else { $new_args[] = $arg; } } $args = $new_args; foreach ( $args as $file ) { if ( ! is_readable( $file ) ) { WP_CLI::warning( "Can't read $file file." ); } $ret = $this->import_wxr( $file, $assoc_args ); if ( is_wp_error( $ret ) ) { WP_CLI::error( $ret ); } else { WP_CLI::log(''); // WXR import ends with HTML, so make sure message is on next line WP_CLI::success( "Finished importing from $file file." ); } } } /** * Import a WXR file */ private function import_wxr( $file, $args ) { $wp_import = new WP_Import; $import_data = $wp_import->parse( $file ); if ( is_wp_error( $import_data ) ) return $import_data; // Prepare the data to be used in process_author_mapping(); $wp_import->get_authors_from_import( $import_data ); // We no longer need the original data, so unset to avoid using excess // memory. unset( $import_data ); $author_data = array(); foreach ( $wp_import->authors as $wxr_author ) { $author = new \stdClass; // Always in the WXR $author->user_login = $wxr_author['author_login']; // Should be in the WXR; no guarantees if ( isset ( $wxr_author['author_email'] ) ) { $author->user_email = $wxr_author['author_email']; } if ( isset( $wxr_author['author_display_name'] ) ) $author->display_name = $wxr_author['author_display_name']; if ( isset( $wxr_author['author_first_name'] ) ) $author->first_name = $wxr_author['author_first_name']; if ( isset( $wxr_author['author_last_name'] ) ) $author->last_name = $wxr_author['author_last_name']; $author_data[] = $author; } // Build the author mapping $author_mapping = $this->process_author_mapping( $args['authors'], $author_data ); if ( is_wp_error( $author_mapping ) ) return $author_mapping; $author_in = wp_list_pluck( $author_mapping, 'old_user_login' ); $author_out = wp_list_pluck( $author_mapping, 'new_user_login' ); unset( $author_mapping, $author_data ); // $user_select needs to be an array of user IDs $user_select = array(); $invalid_user_select = array(); foreach ( $author_out as $author_login ) { $user = get_user_by( 'login', $author_login ); if ( $user ) $user_select[] = $user->ID; else $invalid_user_select[] = $author_login; } if ( ! empty( $invalid_user_select ) ) return new WP_Error( 'invalid-author-mapping', sprintf( "These user_logins are invalid: %s", implode( ',', $invalid_user_select ) ) ); unset( $author_out ); // Drive the import $wp_import->fetch_attachments = !in_array( 'attachment', $args['skip'] ); $_GET = array( 'import' => 'wordpress', 'step' => 2 ); $_POST = array( 'imported_authors' => $author_in, 'user_map' => $user_select, 'fetch_attachments' => $wp_import->fetch_attachments, ); if ( in_array( 'image_resize', $args['skip'] ) ) { add_filter( 'intermediate_image_sizes_advanced', array( $this, 'filter_set_image_sizes' ) ); } $wp_import->import( $file ); return true; } public function filter_set_image_sizes( $sizes ) { // Return null here to prevent the core image resizing logic from running. return null; } /** * Useful verbosity filters for the WXR importer */ private function add_wxr_filters() { add_filter( 'wp_import_posts', function( $posts ) { global $wpcli_import_counts; $wpcli_import_counts['current_post'] = 0; $wpcli_import_counts['total_posts'] = count( $posts ); return $posts; }, 10 ); add_filter( 'wp_import_post_comments', function( $comments, $post_id, $post ) { global $wpcli_import_counts; $wpcli_import_counts['current_comment'] = 0; $wpcli_import_counts['total_comments'] = count( $comments ); return $comments; }, 10, 3 ); add_filter( 'wp_import_post_data_raw', function( $post ) { global $wpcli_import_counts; $wpcli_import_counts['current_post']++; WP_CLI::log(''); WP_CLI::log(''); WP_CLI::log( sprintf( 'Processing post #%d ("%s") (post_type: %s)', $post['post_id'], $post['post_title'], $post['post_type'] ) ); WP_CLI::log( sprintf( '-- %s of %s', number_format( $wpcli_import_counts['current_post'] ), number_format( $wpcli_import_counts['total_posts'] ) ) ); WP_CLI::log( '-- ' . date( 'r' ) ); return $post; } ); add_action( 'wp_import_insert_post', function( $post_id, $original_post_ID, $post, $postdata ) { global $wpcli_import_counts; if ( is_wp_error( $post_id ) ) { WP_CLI::warning( "-- Error importing post: " . $post_id->get_error_code() ); } else { WP_CLI::log( "-- Imported post as post_id #{$post_id}" ); } if ( $wpcli_import_counts['current_post'] % 500 === 0 ) { WP_CLI\Utils\wp_clear_object_cache(); WP_CLI::log( "-- Cleared object cache." ); } }, 10, 4 ); add_action( 'wp_import_insert_term', function( $t, $import_term, $post_id, $post ) { WP_CLI::log( "-- Created term \"{$import_term['name']}\"" ); }, 10, 4 ); add_action( 'wp_import_set_post_terms', function( $tt_ids, $term_ids, $taxonomy, $post_id, $post ) { WP_CLI::log( "-- Added terms (" . implode( ',', $term_ids ) .") for taxonomy \"{$taxonomy}\"" ); }, 10, 5 ); add_action( 'wp_import_insert_comment', function( $comment_id, $comment, $comment_post_ID, $post ) { global $wpcli_import_counts; $wpcli_import_counts['current_comment']++; WP_CLI::log( sprintf( '-- Added comment #%d (%s of %s)', $comment_id, number_format( $wpcli_import_counts['current_comment'] ), number_format( $wpcli_import_counts['total_comments'] ) ) ); }, 10, 4 ); add_action( 'import_post_meta', function( $post_id, $key, $value ) { WP_CLI::log( "-- Added post_meta $key" ); }, 10, 3 ); } /** * Is the requested importer available? */ private function is_importer_available() { require_once ABSPATH . 'wp-admin/includes/plugin.php'; if ( class_exists( 'WP_Import' ) ) { return true; } $plugins = get_plugins(); $wordpress_importer = 'wordpress-importer/wordpress-importer.php'; if ( array_key_exists( $wordpress_importer, $plugins ) ) $error_msg = "WordPress Importer needs to be activated. Try 'wp plugin activate wordpress-importer'."; else $error_msg = "WordPress Importer needs to be installed. Try 'wp plugin install wordpress-importer --activate'."; return new WP_Error( 'importer-missing', $error_msg ); } /** * Process how the authors should be mapped * * @param string $authors_arg The `--author` argument originally passed to command * @param array $author_data An array of WP_User-esque author objects * @return array|WP_Error $author_mapping Author mapping array if successful, WP_Error if something bad happened */ private function process_author_mapping( $authors_arg, $author_data ) { // Provided an author mapping file (method checks validity) if ( file_exists( $authors_arg ) ) return $this->read_author_mapping_file( $authors_arg ); // Provided a file reference, but the file doesn't yet exist if ( false !== stripos( $authors_arg, '.csv' ) ) return $this->create_author_mapping_file( $authors_arg, $author_data ); switch( $authors_arg ) { // Create authors if they don't yet exist; maybe match on email or user_login case 'create': return $this->create_authors_for_mapping( $author_data ); break; // Skip any sort of author mapping case 'skip': return array(); break; default: return new WP_Error( 'invalid-argument', "'authors' argument is invalid." ); } } /** * Read an author mapping file */ private function read_author_mapping_file( $file ) { $author_mapping = array(); foreach ( new \WP_CLI\Iterators\CSV( $file ) as $i => $author ) { if ( ! array_key_exists( 'old_user_login', $author ) || ! array_key_exists( 'new_user_login', $author ) ) return new WP_Error( 'invalid-author-mapping', "Author mapping file isn't properly formatted." ); $author_mapping[] = $author; } return $author_mapping; } /** * Create an author mapping file, based on provided author data * * @return WP_Error The file was just now created, so some action needs to be taken */ private function create_author_mapping_file( $file, $author_data ) { if ( touch( $file ) ) { $author_mapping = array(); foreach ( $author_data as $author ) { $author_mapping[] = array( 'old_user_login' => $author->user_login, 'new_user_login' => $this->suggest_user( $author->user_login, $author->user_email ), ); } $file_resource = fopen( $file, 'w' ); \WP_CLI\utils\write_csv( $file_resource, $author_mapping, array( 'old_user_login', 'new_user_login' ) ); return new WP_Error( 'author-mapping-error', sprintf( "Please update author mapping file before continuing: %s", $file ) ); } else { return new WP_Error( 'author-mapping-error', "Couldn't create author mapping file." ); } } /** * Create users if they don't exist, and build an author mapping file */ private function create_authors_for_mapping( $author_data ) { $author_mapping = array(); foreach ( $author_data as $author ) { if ( isset( $author->user_email ) ) { if ( $user = get_user_by( 'email', $author->user_email ) ) { $author_mapping[] = array( 'old_user_login' => $author->user_login, 'new_user_login' => $user->user_login, ); continue; } } if ( $user = get_user_by( 'login', $author->user_login ) ) { $author_mapping[] = array( 'old_user_login' => $author->user_login, 'new_user_login' => $user->user_login, ); continue; } $user = array( 'user_login' => '', 'user_email' => '', 'user_pass' => wp_generate_password(), ); $user = array_merge( $user, (array)$author ); $user_id = wp_insert_user( $user ); if ( is_wp_error( $user_id ) ) return $user_id; $user = get_user_by( 'id', $user_id ); $author_mapping[] = array( 'old_user_login' => $author->user_login, 'new_user_login' => $user->user_login, ); } return $author_mapping; } /** * Suggest a blog user based on the levenshtein distance */ private function suggest_user( $author_user_login, $author_user_email = '' ) { if ( ! isset( $this->blog_users ) ) $this->blog_users = get_users(); $shortest = -1; $shortestavg = array(); $threshold = floor( ( strlen( $author_user_login ) / 100 ) * 10 ); // 10 % of the strlen are valid $closest = ''; foreach ( $this->blog_users as $user ) { // Before we resort to an algorithm, let's try for an exact match if ( $author_user_email && $user->user_email == $author_user_email ) return $user->user_login; $levs = array(); $levs[] = levenshtein( $author_user_login, $user->display_name ); $levs[] = levenshtein( $author_user_login, $user->user_login ); $levs[] = levenshtein( $author_user_login, $user->user_email ); $email_parts = explode( "@", $user->user_email ); $email_login = array_shift( $email_parts ); $levs[] = levenshtein( $author_user_login, $email_login ); rsort( $levs ); $lev = array_pop( $levs ); if ( 0 == $lev ) { $closest = $user->user_login; $shortest = 0; break; } if ( ( $lev <= $shortest || $shortest < 0 ) && $lev <= $threshold ) { $closest = $user->user_login; $shortest = $lev; } $shortestavg[] = $lev; } // in case all usernames have a common pattern if ( $shortest > ( array_sum( $shortestavg ) / count( $shortestavg ) ) ) return ''; return $closest; } } WP_CLI::add_command( 'import', 'Import_Command' ); ...] * : One or more IDs of the attachments to regenerate. * * [--skip-delete] * : Skip deletion of the original thumbnails. If your thumbnails are linked from sources outside your control, it's likely best to leave them around. Defaults to false. * * [--only-missing] * : Only generate thumbnails for images missing image sizes. * * [--yes] * : Answer yes to the confirmation message. * * ## EXAMPLES * * # re-generate all thumbnails, without confirmation * wp media regenerate --yes * * # re-generate all thumbnails that have IDs between 1000 and 2000 * seq 1000 2000 | xargs wp media regenerate */ function regenerate( $args, $assoc_args = array() ) { if ( empty( $args ) ) { WP_CLI::confirm( 'Do you really want to regenerate all images?', $assoc_args ); } $skip_delete = \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-delete' ); $only_missing = \WP_CLI\Utils\get_flag_value( $assoc_args, 'only-missing' ); if ( $only_missing ) { $skip_delete = true; } $query_args = array( 'post_type' => 'attachment', 'post__in' => $args, 'post_mime_type' => 'image', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids' ); $images = new WP_Query( $query_args ); $count = $images->post_count; if ( !$count ) { WP_CLI::warning( 'No images found.' ); return; } WP_CLI::log( sprintf( 'Found %1$d %2$s to regenerate.', $count, _n( 'image', 'images', $count ) ) ); $errored = false; foreach ( $images->posts as $id ) { if ( ! $this->_process_regeneration( $id, $skip_delete, $only_missing ) ) { $errored = true; } } if ( $errored ) { WP_CLI::log( _n( 'An error occurred with image regeneration.', 'An error occurred regenerating one or more images.', $count ) ); } else { WP_CLI::success( sprintf( 'Finished regenerating %1$s.', _n('the image', 'all images', $count) ) ); } } /** * Create attachments from local files or from URLs. * * ## OPTIONS * * ... * : Path to file or files to be imported. Supports the glob(3) capabilities of the current shell. * If file is recognized as a URL (for example, with a scheme of http or ftp), the file will be * downloaded to a temp file before being sideloaded. * * [--post_id=] * : ID of the post to attach the imported files to * * [--title=] * : Attachment title (post title field) * * [--caption=<caption>] * : Caption for attachent (post excerpt field) * * [--alt=<alt_text>] * : Alt text for image (saved as post meta) * * [--desc=<description>] * : "Description" field (post content) of attachment post * * [--featured_image] * : If set, set the imported image as the Featured Image of the post its attached to. * * [--porcelain] * : Output just the new attachment id. * * ## EXAMPLES * * # Import all jpgs in the current user's "Pictures" directory, not attached to any post * wp media import ~/Pictures/**\/*.jpg * * # Import a local image and set it to be the post thumbnail for a post * wp media import ~/Downloads/image.png --post_id=123 --title="A downloaded picture" --featured_image * * # Import an image from the web * wp media import http://s.wordpress.org/style/images/wp-header-logo.png --title='The WordPress logo' --alt="Semantic personal publishing" */ function import( $args, $assoc_args = array() ) { $assoc_args = wp_parse_args( $assoc_args, array( 'title' => '', 'caption' => '', 'alt' => '', 'desc' => '', ) ); if ( isset( $assoc_args['post_id'] ) ) { if ( !get_post( $assoc_args['post_id'] ) ) { WP_CLI::warning( "Invalid --post_id" ); $assoc_args['post_id'] = false; } } else { $assoc_args['post_id'] = false; } foreach ( $args as $file ) { $is_file_remote = parse_url( $file, PHP_URL_HOST ); $orig_filename = $file; if ( empty( $is_file_remote ) ) { if ( !file_exists( $file ) ) { WP_CLI::warning( "Unable to import file $file. Reason: File doesn't exist." ); break; } $tempfile = $this->_make_copy( $file ); } else { $tempfile = download_url( $file ); } $file_array = array( 'tmp_name' => $tempfile, 'name' => basename( $file ) ); $post_array= array( 'post_title' => $assoc_args['title'], 'post_excerpt' => $assoc_args['caption'], 'post_content' => $assoc_args['desc'] ); // Deletes the temporary file. $success = media_handle_sideload( $file_array, $assoc_args['post_id'], $assoc_args['title'], $post_array ); if ( is_wp_error( $success ) ) { WP_CLI::warning( sprintf( 'Unable to import file %s. Reason: %s', $orig_filename, implode( ', ', $success->get_error_messages() ) ) ); continue; } // Set alt text if ( $assoc_args['alt'] ) { update_post_meta( $success, '_wp_attachment_image_alt', $assoc_args['alt'] ); } // Set as featured image, if --post_id and --featured_image are set if ( $assoc_args['post_id'] && \WP_CLI\Utils\get_flag_value( $assoc_args, 'featured_image' ) ) { update_post_meta( $assoc_args['post_id'], '_thumbnail_id', $success ); } $attachment_success_text = ''; if ( $assoc_args['post_id'] ) { $attachment_success_text = " and attached to post {$assoc_args['post_id']}"; if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'featured_image' ) ) $attachment_success_text .= ' as featured image'; } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) { WP_CLI::line( $success ); } else { WP_CLI::success( sprintf( 'Imported file %s as attachment ID %d%s.', $orig_filename, $success, $attachment_success_text ) ); } } } // wp_tempnam() inexplicably forces a .tmp extension, which spoils MIME type detection private function _make_copy( $path ) { $dir = get_temp_dir(); $filename = basename( $path ); if ( empty( $filename ) ) $filename = time(); $filename = $dir . wp_unique_filename( $dir, $filename ); if ( !copy( $path, $filename ) ) WP_CLI::error( "Could not create temporary file for $path" ); return $filename; } private function _process_regeneration( $id, $skip_delete = false, $only_missing = false ) { $fullsizepath = get_attached_file( $id ); $att_desc = sprintf( '"%1$s" (ID %2$d).', get_the_title( $id ), $id ); if ( false === $fullsizepath || !file_exists( $fullsizepath ) ) { WP_CLI::warning( "Can't find $att_desc" ); return false; } if ( ! $skip_delete ) { $this->remove_old_images( $id ); } if ( ! $only_missing || $this->needs_regeneration( $id ) ) { $metadata = wp_generate_attachment_metadata( $id, $fullsizepath ); if ( is_wp_error( $metadata ) ) { WP_CLI::warning( $metadata->get_error_message() ); return false; } if ( empty( $metadata ) ) { WP_CLI::warning( "Couldn't regenerate thumbnails for $att_desc." ); return false; } wp_update_attachment_metadata( $id, $metadata ); WP_CLI::log( "Regenerated thumbnails for $att_desc" ); return true; } else { WP_CLI::log( "No thumbnail regeneration needed for $att_desc" ); return true; } } private function remove_old_images( $att_id ) { $wud = wp_upload_dir(); $metadata = wp_get_attachment_metadata( $att_id ); if ( empty( $metadata['file'] ) ) { return; } $dir_path = $wud['basedir'] . '/' . dirname( $metadata['file'] ) . '/'; $original_path = $dir_path . basename( $metadata['file'] ); if ( empty( $metadata['sizes'] ) ) { return; } foreach ( $metadata['sizes'] as $size_info ) { $intermediate_path = $dir_path . $size_info['file']; if ( $intermediate_path == $original_path ) continue; if ( file_exists( $intermediate_path ) ) unlink( $intermediate_path ); } } private function needs_regeneration( $att_id ) { $wud = wp_upload_dir(); $metadata = wp_get_attachment_metadata($att_id); if ( empty($metadata['file'] ) ) { return false; } $dir_path = $wud['basedir'] . '/' . dirname( $metadata['file'] ) . '/'; $original_path = $dir_path . basename( $metadata['file'] ); if ( empty( $metadata['sizes'] ) ) { return false; } foreach( $metadata['sizes'] as $size_info ) { $intermediate_path = $dir_path . $size_info['file']; if ( $intermediate_path == $original_path ) continue; if ( ! file_exists( $intermediate_path ) ) { return true; } } return false; } } WP_CLI::add_command( 'media', 'Media_Command', array( 'before_invoke' => function () { if ( !wp_image_editor_supports() ) { WP_CLI::error( 'No support for generating images found. ' . 'Please install the Imagick or GD PHP extensions.' ); } } ) ); <?php /** * List, create, assign, and delete menus * * ## EXAMPLES * * # Create a new menu * wp menu create "My Menu" * * # List existing menus * wp menu list * * # Create a new menu link item * wp menu item add-custom sidebar-menu Apple http://apple.com --porcelain * * # Assign the 'primary-menu' menu to the 'primary' location * wp menu location assign primary-menu primary */ class Menu_Command extends WP_CLI_Command { protected $obj_type = 'nav_menu'; protected $obj_fields = array( 'term_id', 'name', 'slug', 'locations', 'count', ); /** * Create a new menu * * <menu-name> * : A descriptive name for the menu * * [--porcelain] * : Output just the new menu id. * * ## EXAMPLES * * wp menu create "My Menu" */ public function create( $args, $assoc_args ) { $menu_id = wp_create_nav_menu( $args[0] ); if ( is_wp_error( $menu_id ) ) { WP_CLI::error( $menu_id->get_error_message() ); } else { if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) { WP_CLI::line( $menu_id ); } else { WP_CLI::success( "Created menu $menu_id." ); } } } /** * Delete one or more menus * * <menu>... * : The name, slug, or term ID for the menu(s) * * ## EXAMPLES * * wp menu delete "My Menu" */ public function delete( $args, $_ ) { foreach( $args as $arg ) { $ret = wp_delete_nav_menu( $arg ); if ( ! $ret || is_wp_error( $ret ) ) { WP_CLI::warning( "Error deleting menu." ); } } WP_CLI::success( "Menu(s) deleted." ); } /** * Get a list of menus. * * ## OPTIONS * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count, ids. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each menu: * * * term_id * * name * * slug * * count * * These fields are optionally available: * * * term_group * * term_taxonomy_id * * taxonomy * * description * * parent * * locations * * ## EXAMPLES * * wp menu list * * @subcommand list */ public function list_( $_, $assoc_args ) { $menus = wp_get_nav_menus(); $menu_locations = get_nav_menu_locations(); // < 3.6 $menu_locations could be false if ( ! $menu_locations ) { $menu_locations = array(); } foreach( $menus as &$menu ) { $menu->locations = array(); foreach( $menu_locations as $location => $term_id ) { if ( $term_id == $menu->term_id ) { $menu->locations[] = $location; } } // Normalize the data for some output formats if ( ! isset( $assoc_args['format'] ) || in_array( $assoc_args['format'], array( 'csv', 'table' ) ) ) { $menu->locations = implode( ',', $menu->locations ); } } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_items( $menus ); } protected function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->obj_fields, $this->obj_type ); } } /** * List, add, and delete items associated with a menu * * ## EXAMPLES * * # Add an existing post to an existing menu * wp menu item add-post sidebar-menu 33 --title="Custom Test Post" * * # Create a new menu link item * wp menu item add-custom sidebar-menu Apple http://apple.com --porcelain */ class Menu_Item_Command extends WP_CLI_Command { protected $obj_fields = array( 'db_id', 'type', 'title', 'link', 'position', ); /** * Get a list of items associated with a menu * * ## OPTIONS * * <menu> * : The name, slug, or term ID for the menu * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count, ids. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each menu item: * * * db_id * * type * * title * * link * * position * * These fields are optionally available: * * * menu_item_parent * * object_id * * object * * type * * type_label * * target * * attr_title * * description * * classes * * xfn * * ## EXAMPLES * * wp menu item list <menu> * * @subcommand list */ public function list_( $args, $assoc_args ) { $items = wp_get_nav_menu_items( $args[0] ); if ( false === $items || is_wp_error( $items ) ) { WP_CLI::error( "Invalid menu" ); } // Correct position inconsistency and // protected `url` param in WP-CLI $items = array_map( function( $item ) use ( $assoc_args ) { $item->position = $item->menu_order; $item->link = $item->url; return $item; }, $items ); if ( ! empty( $assoc_args['format'] ) && 'ids' == $assoc_args['format'] ) { $items = array_map( function( $item ) { return $item->db_id; }, $items ); } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_items( $items ); } /** * Add a post as a menu item * * <menu> * : The name, slug, or term ID for the menu * * <post-id> * : Post ID to add to the menu * * [--title=<title>] * : Set a custom title for the menu item * * [--link=<link>] * : Set a custom url for the menu item * * [--description=<description>] * : Set a custom description for the menu item * * [--attr-title=<attr-title>] * : Set a custom title attribute for the menu item * * [--target=<target>] * : Set a custom link target for the menu item * * [--classes=<classes>] * : Set a custom link classes for the menu item * * [--position=<position>] * : Specify the position of this menu item. * * [--parent-id=<parent-id>] * : Make this menu item a child of another menu item * * [--porcelain] * : Output just the new menu item id. * * ## EXAMPLES * * wp menu item add-post sidebar-menu 33 --title="Custom Test Post" * * @subcommand add-post */ public function add_post( $args, $assoc_args ) { $assoc_args['object-id'] = $args[1]; unset( $args[1] ); $post = get_post( $assoc_args['object-id'] ); if ( ! $post ) { WP_CLI::error( "Invalid post." ); } $assoc_args['object'] = $post->post_type; $this->add_or_update_item( 'add', 'post_type', $args, $assoc_args ); } /** * Add a taxonomy term as a menu item * * <menu> * : The name, slug, or term ID for the menu * * <taxonomy> * : Taxonomy of the term to be added * * <term-id> * : Term ID of the term to be added * * [--title=<title>] * : Set a custom title for the menu item * * [--link=<link>] * : Set a custom url for the menu item * * [--description=<description>] * : Set a custom description for the menu item * * [--attr-title=<attr-title>] * : Set a custom title attribute for the menu item * * [--target=<target>] * : Set a custom link target for the menu item * * [--classes=<classes>] * : Set a custom link classes for the menu item * * [--position=<position>] * : Specify the position of this menu item. * * [--parent-id=<parent-id>] * : Make this menu item a child of another menu item * * [--porcelain] * : Output just the new menu item id. * * ## EXAMPLES * * wp menu item add-term sidebar-menu post_tag 24 * * @subcommand add-term */ public function add_term( $args, $assoc_args ) { $assoc_args['object'] = $args[1]; unset( $args[1] ); $assoc_args['object-id'] = $args[2]; unset( $args[2] ); if ( ! get_term_by( 'id', $assoc_args['object-id'], $assoc_args['object'] ) ) { WP_CLI::error( "Invalid term." ); } $this->add_or_update_item( 'add', 'taxonomy', $args, $assoc_args ); } /** * Add a custom menu item * * <menu> * : The name, slug, or term ID for the menu * * <title> * : Title for the link * * <link> * : Target URL for the link * * [--description=<description>] * : Set a custom description for the menu item * * [--attr-title=<attr-title>] * : Set a custom title attribute for the menu item * * [--target=<target>] * : Set a custom link target for the menu item * * [--classes=<classes>] * : Set a custom link classes for the menu item * * [--position=<position>] * : Specify the position of this menu item. * * [--parent-id=<parent-id>] * : Make this menu item a child of another menu item * * [--porcelain] * : Output just the new menu item id. * * ## EXAMPLES * * wp menu item add-custom sidebar-menu Apple http://apple.com --porcelain * * @subcommand add-custom */ public function add_custom( $args, $assoc_args ) { $assoc_args['title'] = $args[1]; unset( $args[1] ); $assoc_args['link'] = $args[2]; unset( $args[2] ); $this->add_or_update_item( 'add', 'custom', $args, $assoc_args ); } /** * Update a menu item * * <db-id> * : Database ID for the menu item. * * [--title=<title>] * : Set a custom title for the menu item * * [--link=<link>] * : Set a custom url for the menu item * * [--description=<description>] * : Set a custom description for the menu item * * [--attr-title=<attr-title>] * : Set a custom title attribute for the menu item * * [--target=<target>] * : Set a custom link target for the menu item * * [--classes=<classes>] * : Set a custom link classes for the menu item * * [--position=<position>] * : Specify the position of this menu item. * * [--parent-id=<parent-id>] * : Make this menu item a child of another menu item * * ## EXAMPLES * * wp menu item update 45 --title=WordPress --link='http://wordpress.org' --target=_blank --position=2 * * @subcommand update */ public function update( $args, $assoc_args ) { // Shuffle the position of these $args[1] = $args[0]; $terms = get_the_terms( $args[1], 'nav_menu' ); if ( $terms && ! is_wp_error( $terms ) ) { $args[0] = (int)$terms[0]->term_id; } else { $args[0] = 0; } $type = get_post_meta( $args[1], '_menu_item_type', true ); $this->add_or_update_item( 'update', $type, $args, $assoc_args ); } /** * Delete one or more items from a menu * * <db-id>... * : Database ID for the menu item(s). * * ## EXAMPLES * * wp menu item delete 45 * * @subcommand delete */ public function delete( $args, $_ ) { global $wpdb; foreach( $args as $arg ) { $parent_menu_id = (int) get_post_meta( $arg, '_menu_item_menu_item_parent', true ); $ret = wp_delete_post( $arg, true ); if ( ! $ret ) { WP_CLI::warning( "Couldn't delete menu item." ); } else if ( $parent_menu_id ) { $children = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_menu_item_menu_item_parent' AND meta_value=%s", (int) $arg ) ); if ( $children ) { $children_query = $wpdb->prepare( "UPDATE $wpdb->postmeta SET meta_value = %d WHERE meta_key = '_menu_item_menu_item_parent' AND meta_value=%s", $parent_menu_id, (int) $arg ); $wpdb->query( $children_query ); foreach( $children as $child ) { clean_post_cache( $child ); } } } } WP_CLI::success( "Menu item(s) deleted." ); } /** * Worker method to create new items or update existing ones */ private function add_or_update_item( $method, $type, $args, $assoc_args ) { $menu = $args[0]; $menu_item_db_id = \WP_CLI\Utils\get_flag_value( $args, 1, 0 ); $menu = wp_get_nav_menu_object( $menu ); if ( ! $menu || is_wp_error( $menu ) ) { WP_CLI::error( "Invalid menu." ); } // `url` is protected in WP-CLI, so we use `link` instead $assoc_args['url'] = \WP_CLI\Utils\get_flag_value( $assoc_args, 'link' ); // Need to persist the menu item data. See https://core.trac.wordpress.org/ticket/28138 if ( 'update' == $method ) { $menu_item_obj = get_post( $menu_item_db_id ); $menu_item_obj = wp_setup_nav_menu_item( $menu_item_obj ); // Correct the menu position if this was the first item. See https://core.trac.wordpress.org/ticket/28140 $position = ( 0 === $menu_item_obj->menu_order ) ? 1 : $menu_item_obj->menu_order; $default_args = array( 'position' => $position, 'title' => $menu_item_obj->title, 'url' => $menu_item_obj->url, 'description' => $menu_item_obj->description, 'object' => $menu_item_obj->object, 'object-id' => $menu_item_obj->object_id, 'parent-id' => $menu_item_obj->menu_item_parent, 'attr-title' => $menu_item_obj->attr_title, 'target' => $menu_item_obj->target, 'classes' => implode( ' ', $menu_item_obj->classes ), // stored in the database as array 'xfn' => $menu_item_obj->xfn, 'status' => $menu_item_obj->post_status, ); } else { $default_args = array( 'position' => 0, 'title' => '', 'url' => '', 'description' => '', 'object' => '', 'object-id' => 0, 'parent-id' => 0, 'attr-title' => '', 'target' => '', 'classes' => '', 'xfn' => '', // Core oddly defaults to 'draft' for create, // and 'publish' for update // Easiest to always work with publish 'status' => 'publish', ); } $menu_item_args = array(); foreach( $default_args as $key => $default_value ) { // wp_update_nav_menu_item() has a weird argument prefix $new_key = 'menu-item-' . $key; $menu_item_args[ $new_key ] = \WP_CLI\Utils\get_flag_value( $assoc_args, $key, $default_value ); } $menu_item_args['menu-item-type'] = $type; $ret = wp_update_nav_menu_item( $menu->term_id, $menu_item_db_id, $menu_item_args ); if ( is_wp_error( $ret ) ) { WP_CLI::error( $ret->get_error_message() ); } else if ( ! $ret ) { if ( 'add' == $method ) { WP_CLI::error( "Couldn't add menu item." ); } else if ( 'update' == $method ) { WP_CLI::error( "Couldn't update menu item." ); } } else { /** * Set the menu * * wp_update_nav_menu_item() *should* take care of this, but * depends on wp_insert_post()'s "tax_input" argument, which * is ignored if the user can't edit the taxonomy * * @see https://core.trac.wordpress.org/ticket/27113 */ if ( ! is_object_in_term( $ret, 'nav_menu', (int) $menu->term_id ) ) { wp_set_object_terms( $ret, array( (int)$menu->term_id ), 'nav_menu' ); } if ( 'add' == $method && ! empty( $assoc_args['porcelain'] ) ) { WP_CLI::line( $ret ); } else { if ( 'add' == $method ) { WP_CLI::success( "Menu item added." ); } else if ( 'update' == $method ) { WP_CLI::success( "Menu item updated." ); } } } } protected function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->obj_fields ); } } /** * Manage a menu's assignment to locations. * * ## EXAMPLES * * # List available menu locations * wp menu location list * * # Assign the 'primary-menu' menu to the 'primary' location * wp menu location assign primary-menu primary * * # Remove the 'primary-menu' menu from the 'primary' location * wp menu location remove primary-menu primary */ class Menu_Location_Command extends WP_CLI_Command { /** * List locations for the current theme. * * [--format=<format>] * : Accepted values: table, csv, json, count, ids. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each location: * * * name * * description * * ## EXAMPLES * * wp menu location list * * @subcommand list */ public function list_( $_, $assoc_args ) { $locations = get_registered_nav_menus(); $location_objs = array(); foreach( $locations as $location => $description ) { $location_obj = new \stdClass; $location_obj->location = $location; $location_obj->description = $description; $location_objs[] = $location_obj; } $formatter = new \WP_CLI\Formatter( $assoc_args, array( 'location', 'description' ) ); $formatter->display_items( $location_objs ); } /** * Assign a location to a menu * * <menu> * : The name, slug, or term ID for the menu * * <location> * : Location's slug * * ## EXAMPLES * * wp menu location assign primary-menu primary * * @subcommand assign */ public function assign( $args, $_ ) { list( $menu, $location ) = $args; $menu = wp_get_nav_menu_object( $menu ); if ( ! $menu || is_wp_error( $menu ) ) { WP_CLI::error( "Invalid menu." ); } $locations = get_registered_nav_menus(); if ( ! array_key_exists( $location, $locations ) ) { WP_CLI::error( "Invalid location." ); } $locations = get_nav_menu_locations(); $locations[ $location ] = $menu->term_id; set_theme_mod( 'nav_menu_locations', $locations ); WP_CLI::success( "Assigned location to menu." ); } /** * Remove a location from a menu * * <menu> * : The name, slug, or term ID for the menu * * <location> * : Location's slug * * ## EXAMPLES * * wp menu location remove primary-menu primary * * @subcommand remove */ public function remove( $args, $_ ) { list( $menu, $location ) = $args; $menu = wp_get_nav_menu_object( $menu ); if ( ! $menu || is_wp_error( $menu ) ) { WP_CLI::error( "Invalid menu." ); } $locations = get_nav_menu_locations(); if ( \WP_CLI\Utils\get_flag_value( $locations, $location ) != $menu->term_id ) { WP_CLI::error( "Menu isn't assigned to location." ); } $locations[ $location ] = 0; set_theme_mod( 'nav_menu_locations', $locations ); WP_CLI::success( "Removed location from menu." ); } } WP_CLI::add_command( 'menu', 'Menu_Command' ); WP_CLI::add_command( 'menu item', 'Menu_Item_Command' ); WP_CLI::add_command( 'menu location', 'Menu_Location_Command' ); <?php /** * Manage network custom fields. * * ## OPTIONS * * <id> * : The network id (usually 1). * * --format=json * : Encode/decode values as JSON. * * ## EXAMPLES * * # get a list of super-admins * wp network meta get 1 site_admins */ class Network_Meta_Command extends \WP_CLI\CommandWithMeta { protected $meta_type = 'site'; } WP_CLI::add_command( 'network meta', 'Network_Meta_Command', array( 'before_invoke' => function () { if ( !is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } } ) ); <?php /** * Manage options. * * ## OPTIONS * * [--format=json] * : Encode/decode values as JSON. * * ## EXAMPLES * * wp option get siteurl * * wp option add my_option foobar * * wp option update my_option '{"foo": "bar"}' --format=json * * wp option delete my_option */ class Option_Command extends WP_CLI_Command { /** * Get an option. * * <key> * : Key for the option * * [--format=<format>] * : Get value as var_export() or JSON. Default: var_export() */ public function get( $args, $assoc_args ) { list( $key ) = $args; $value = get_option( $key ); if ( false === $value ) die(1); WP_CLI::print_value( $value, $assoc_args ); } /** * Add an option. * * ## OPTIONS * * <key> * : The name of the option to add. * * [<value>] * : The value of the option to add. If ommited, the value is read from STDIN. * * [--format=<format>] * : The serialization format for the value. Default is plaintext. * * [--autoload=<autoload>] * : Should this option be automatically loaded. Accepted values: yes, no. Default: yes * * ## EXAMPLES * * # Create an option by reading a JSON file * wp option add my_option --format=json < config.json */ public function add( $args, $assoc_args ) { $key = $args[0]; $value = WP_CLI::get_value_from_arg_or_stdin( $args, 1 ); $value = WP_CLI::read_value( $value, $assoc_args ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'autoload' ) === 'no' ) { $autoload = 'no'; } else { $autoload = 'yes'; } if ( !add_option( $key, $value, '', $autoload ) ) { WP_CLI::error( "Could not add option '$key'. Does it already exist?" ); } else { WP_CLI::success( "Added '$key' option." ); } } /** * List options. * * [--search=<pattern>] * : Use wildcards ( * and ? ) to match option name. * * [--autoload=<value>] * : Match only autoload options when value is on, and only not-autoload option when off. * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : The serialization format for the value. total_bytes displays the total size of matching options in bytes. Accepted values: table, json, csv, count, total_bytes. Default: table * * ## EXAMPLES * * # Get the total size of all autoload options * wp option list --autoload=on --format=total_bytes * * # Find biggest transients * wp option list --search="*_transient_*" --fields=option_name,size_bytes | sort -n -k 2 | tail * * # List all options begining with "i2f_" * wp option list --search "i2f_*" * * ## AVAILABLE FIELDS * * This field will be displayed by default for each matching option: * * * option_name * * option_value * * These fields are optionally available: * * * autoload * * size_bytes * * @subcommand list */ public function list_( $args, $assoc_args ) { global $wpdb; $pattern = '%'; $fields = array( 'option_name', 'option_value' ); $size_query = ",LENGTH(option_value) AS `size_bytes`"; $autoload_query = ''; if ( isset( $assoc_args['search'] ) ) { $pattern = self::esc_like( $assoc_args['search'] ); // substitute wildcards $pattern = str_replace( '*', '%', $pattern ); $pattern = str_replace( '?', '_', $pattern ); } if ( isset( $assoc_args['fields'] ) ) { $fields = explode( ',', $assoc_args['fields'] ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' ) === 'total_bytes' ) { $fields = array( 'size_bytes' ); $size_query = ",SUM(LENGTH(option_value)) AS `size_bytes`"; } if ( isset( $assoc_args['autoload'] ) ) { if ( 'on' === $assoc_args['autoload'] ) { $autoload_query = " AND autoload='yes'"; } elseif ( 'off' === $assoc_args['autoload'] ) { $autoload_query = " AND autoload='no'"; } else { WP_CLI::error( "Value of '--autoload' should be on or off." ); } } $results = $wpdb->get_results( $wpdb->prepare( "SELECT `option_name`,`option_value`,`autoload`" . $size_query . " FROM `$wpdb->options` WHERE `option_name` LIKE %s" . $autoload_query, $pattern ) ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' ) === 'total_bytes' ) { WP_CLI::line( $results[0]->size_bytes ); } else { $formatter = new \WP_CLI\Formatter( $assoc_args, $fields ); $formatter->display_items( $results ); } } /** * Update an option. * * ## OPTIONS * * <key> * : The name of the option to add. * * [<value>] * : The new value. If ommited, the value is read from STDIN. * * [--autoload=<autoload>] * : Requires WP 4.2. Should this option be automatically loaded. Accepted values: yes, no. Default: yes * * [--format=<format>] * : The serialization format for the value. Default is plaintext. * * ## EXAMPLES * * # Update an option by reading from a file * wp option update my_option < value.txt * * # Update one option on multiple sites using xargs * wp site list --field=url | xargs -n1 -I {} sh -c 'wp --url={} option update <key> <value>' * * @alias set */ public function update( $args, $assoc_args ) { $key = $args[0]; $value = WP_CLI::get_value_from_arg_or_stdin( $args, 1 ); $value = WP_CLI::read_value( $value, $assoc_args ); $autoload = \WP_CLI\Utils\get_flag_value( $assoc_args, 'autoload' ); if ( ! in_array( $autoload, array( 'yes', 'no' ) ) ) { $autoload = null; } $value = sanitize_option( $key, $value ); $old_value = sanitize_option( $key, get_option( $key ) ); if ( $value === $old_value && is_null( $autoload ) ) { WP_CLI::success( "Value passed for '$key' option is unchanged." ); } else { if ( update_option( $key, $value, $autoload ) ) { WP_CLI::success( "Updated '$key' option." ); } else { WP_CLI::error( "Could not update option '$key'." ); } } } /** * Delete an option. * * <key> * : Key for the option. */ public function delete( $args ) { list( $key ) = $args; if ( !delete_option( $key ) ) { WP_CLI::error( "Could not delete '$key' option. Does it exist?" ); } else { WP_CLI::success( "Deleted '$key' option." ); } } private static function esc_like( $old ) { global $wpdb; // Remove notices in 4.0 and support backwards compatibility if( method_exists( $wpdb, 'esc_like' ) ) { // 4.0 $old = $wpdb->esc_like( $old ); } else { // 3.9 or less $old = like_escape( esc_sql( $old ) ); } return $old; } } WP_CLI::add_command( 'option', 'Option_Command' ); <?php use \WP_CLI\Utils; /** * Manage plugins. * * @package wp-cli */ class Plugin_Command extends \WP_CLI\CommandWithUpgrade { protected $item_type = 'plugin'; protected $upgrade_refresh = 'wp_update_plugins'; protected $upgrade_transient = 'update_plugins'; protected $obj_fields = array( 'name', 'status', 'update', 'version' ); function __construct() { require_once ABSPATH.'wp-admin/includes/plugin.php'; require_once ABSPATH.'wp-admin/includes/plugin-install.php'; parent::__construct(); $this->fetcher = new \WP_CLI\Fetchers\Plugin; } protected function get_upgrader_class( $force ) { return $force ? '\\WP_CLI\\DestructivePluginUpgrader' : 'Plugin_Upgrader'; } /** * See the status of one or all plugins. * * ## OPTIONS * * [<plugin>] * : A particular plugin to show the status for. */ function status( $args ) { parent::status( $args ); } /** * Search the wordpress.org plugin repository. * * ## OPTIONS * * <search> * : The string to search for. * * [--per-page=<per-page>] * : Optional number of results to display. Defaults to 10. * * [--field=<field>] * : Prints the value of a single field for each plugin. * * [--fields=<fields>] * : Ask for specific fields from the API. Defaults to name,slug,author_profile,rating. Acceptable values: * * **name**: Plugin Name * **slug**: Plugin Slug * **version**: Current Version Number * **author**: Plugin Author * **author_profile**: Plugin Author Profile * **contributors**: Plugin Contributors * **requires**: Plugin Minimum Requirements * **tested**: Plugin Tested Up To * **compatibility**: Plugin Compatible With * **rating**: Plugin Rating * **num_ratings**: Number of Plugin Ratings * **homepage**: Plugin Author's Homepage * **description**: Plugin's Description * **short_description**: Plugin's Short Description * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## EXAMPLES * * wp plugin search dsgnwrks --per-page=20 --format=json * * wp plugin search dsgnwrks --fields=name,version,slug,rating,num_ratings */ public function search( $args, $assoc_args ) { parent::_search( $args, $assoc_args ); } protected function status_single( $args ) { $plugin = $this->fetcher->get_check( $args[0] ); $file = $plugin->file; $details = $this->get_details( $file ); $status = $this->format_status( $this->get_status( $file ), 'long' ); $version = $details['Version']; if ( $this->has_update( $file ) ) $version .= ' (%gUpdate available%n)'; echo WP_CLI::colorize( \WP_CLI\Utils\mustache_render( 'plugin-status.mustache', array( 'slug' => Utils\get_plugin_name( $file ), 'status' => $status, 'version' => $version, 'name' => $details['Name'], 'author' => $details['Author'], 'description' => $details['Description'] ) ) ); } protected function get_all_items() { $items = $this->get_item_list(); foreach ( get_mu_plugins() as $file => $mu_plugin ) { $mu_version = ''; if ( ! empty( $mu_plugin['Version'] ) ) { $mu_version = $mu_plugin['Version']; } $items[ $file ] = array( 'name' => Utils\get_plugin_name( $file ), 'status' => 'must-use', 'update' => false, 'update_version' => NULL, 'update_package' => NULL, 'version' => $mu_version, 'update_id' => '', 'title' => '', 'description' => '', ); } return $items; } /** * Activate a plugin. * * ## OPTIONS * * [<plugin>...] * : One or more plugins to activate. * * [--all] * : If set, all plugins will be activated. * * [--network] * : If set, the plugin will be activated for the entire multisite network. */ function activate( $args, $assoc_args = array() ) { $network_wide = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { $args = array_map( function( $file ){ return Utils\get_plugin_name( $file ); }, array_keys( get_plugins() ) ); } foreach ( $this->fetcher->get_many( $args ) as $plugin ) { $status = $this->get_status( $plugin->file ); // Network-active is the highest level of activation status if ( 'active-network' === $status ) { WP_CLI::warning( "Plugin '{$plugin->name}' is already network active." ); continue; } // Don't reactivate active plugins, but do let them become network-active if ( ! $network_wide && 'active' === $status ) { WP_CLI::warning( "Plugin '{$plugin->name}' is already active." ); continue; } // Plugins need to be deactivated before being network activated if ( $network_wide && 'active' === $status ) { deactivate_plugins( $plugin->file, false, false ); } activate_plugin( $plugin->file, '', $network_wide ); $this->active_output( $plugin->name, $plugin->file, $network_wide, "activate" ); } } /** * Deactivate a plugin. * * ## OPTIONS * * [<plugin>...] * : One or more plugins to deactivate. * * [--uninstall] * : Uninstall the plugin after deactivation. * * [--all] * : If set, all plugins will be deactivated. * * [--network] * : If set, the plugin will be deactivated for the entire multisite network. */ function deactivate( $args, $assoc_args = array() ) { $network_wide = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ); $disable_all = \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ); if ( $disable_all ) { $args = array_map( function( $file ){ return Utils\get_plugin_name( $file ); }, array_keys( get_plugins() ) ); } foreach ( $this->fetcher->get_many( $args ) as $plugin ) { $status = $this->get_status( $plugin->file ); // Network active plugins must be explicitly deactivated if ( ! $network_wide && 'active-network' === $status ) { WP_CLI::warning( "Plugin '{$plugin->name}' is network active and must be deactivated with --network flag." ); continue; } if ( ! in_array( $status, array( 'active', 'active-network' ) ) ) { WP_CLI::warning( "Plugin '{$plugin->name}' isn't active." ); continue; } deactivate_plugins( $plugin->file, false, $network_wide ); $this->active_output( $plugin->name, $plugin->file, $network_wide, "deactivate" ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'uninstall' ) ) { WP_CLI::log( "Uninstalling '{$plugin->name}'..." ); $this->uninstall( array( $plugin->name ) ); } } } /** * Toggle a plugin's activation state. * * ## OPTIONS * * <plugin>... * : One or more plugins to toggle. * * [--network] * : If set, the plugin will be toggled for the entire multisite network. */ function toggle( $args, $assoc_args = array() ) { $network_wide = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ); foreach ( $this->fetcher->get_many( $args ) as $plugin ) { if ( $this->check_active( $plugin->file, $network_wide ) ) { $this->deactivate( array( $plugin->name ), $assoc_args ); } else { $this->activate( array( $plugin->name ), $assoc_args ); } } } /** * Get the path to a plugin or to the plugin directory. * * ## OPTIONS * * [<plugin>] * : The plugin to get the path to. If not set, will return the path to the * plugins directory. * * [--dir] * : If set, get the path to the closest parent directory, instead of the * plugin file. * * ## EXAMPLES * * cd $(wp plugin path) */ function path( $args, $assoc_args ) { $path = untrailingslashit( WP_PLUGIN_DIR ); if ( !empty( $args ) ) { $plugin = $this->fetcher->get_check( $args[0] ); $path .= '/' . $plugin->file; if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'dir' ) ) $path = dirname( $path ); } WP_CLI::line( $path ); } protected function install_from_repo( $slug, $assoc_args ) { $api = plugins_api( 'plugin_information', array( 'slug' => $slug ) ); if ( is_wp_error( $api ) ) { return $api; } if ( isset( $assoc_args['version'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); } $status = install_plugin_install_status( $api ); if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ) && 'install' != $status['status'] ) { // We know this will fail, so avoid a needless download of the package. return new WP_Error( 'already_installed', 'Plugin already installed.' ); } WP_CLI::log( sprintf( 'Installing %s (%s)', html_entity_decode( $api->name, ENT_QUOTES ), $api->version ) ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'version' ) != 'dev' ) { WP_CLI::get_http_cache_manager()->whitelist_package( $api->download_link, $this->item_type, $api->slug, $api->version ); } $result = $this->get_upgrader( $assoc_args )->install( $api->download_link ); return $result; } /** * Update one or more plugins. * * ## OPTIONS * * [<plugin>...] * : One or more plugins to update. * * [--all] * : If set, all plugins that have updates will be updated. * * [--format=<format>] * : Output summary as table or summary. Defaults to table. * * [--version=<version>] * : If set, the plugin will be updated to the specified version. * * [--dry-run] * : Preview which plugins would be updated. * * ## EXAMPLES * * wp plugin update bbpress --version=dev * * wp plugin update --all * * @alias upgrade */ function update( $args, $assoc_args ) { if ( isset( $assoc_args['version'] ) ) { foreach ( $this->fetcher->get_many( $args ) as $plugin ) { $assoc_args['force'] = 1; $this->install( array( $plugin->name ), $assoc_args ); } } else { parent::update_many( $args, $assoc_args ); } } protected function get_item_list() { $items = $duplicate_names = array(); foreach ( get_plugins() as $file => $details ) { $update_info = $this->get_update_info( $file ); $name = Utils\get_plugin_name( $file ); if ( ! isset( $duplicate_names[ $name ] ) ) { $duplicate_names[ $name ] = array(); } $duplicate_names[ $name ][] = $file; $items[ $file ] = array( 'name' => $name, 'status' => $this->get_status( $file ), 'update' => (bool) $update_info, 'update_version' => $update_info['new_version'], 'update_package' => $update_info['package'], 'version' => $details['Version'], 'update_id' => $file, 'title' => $details['Name'], 'description' => $details['Description'], ); } foreach( $duplicate_names as $name => $files ) { if ( count( $files ) <= 1 ) { continue; } foreach( $files as $file ) { $items[ $file ]['name'] = str_replace( '.' . pathinfo( $file, PATHINFO_EXTENSION ), '', $file ); } } return $items; } protected function filter_item_list( $items, $args ) { $basenames = wp_list_pluck( $this->fetcher->get_many( $args ), 'file' ); return \WP_CLI\Utils\pick_fields( $items, $basenames ); } /** * Install a plugin. * * ## OPTIONS * * <plugin|zip|url>... * : A plugin slug, the path to a local zip file, or URL to a remote zip file. * * [--version=<version>] * : If set, get that particular version from wordpress.org, instead of the * stable version. * * [--force] * : If set, the command will overwrite any installed version of the plugin, without prompting * for confirmation. * * [--activate] * : If set, the plugin will be activated immediately after install. * * [--activate-network] * : If set, the plugin will be network activated immediately after install * * ## EXAMPLES * * # Install the latest version from wordpress.org and activate * wp plugin install bbpress --activate * * # Install the development version from wordpress.org * wp plugin install bbpress --version=dev * * # Install from a local zip file * wp plugin install ../my-plugin.zip * * # Install from a remote zip file * wp plugin install http://s3.amazonaws.com/bucketname/my-plugin.zip?AWSAccessKeyId=123&Expires=456&Signature=abcdef */ function install( $args, $assoc_args ) { if ( ! is_dir( WP_PLUGIN_DIR ) ) { wp_mkdir_p( WP_PLUGIN_DIR ); } parent::install( $args, $assoc_args ); } /** * Get a plugin. * * ## OPTIONS * * <plugin> * : The plugin to get. * * [--field=<field>] * : Instead of returning the whole plugin, returns the value of a single field. * * [--fields=<fields>] * : Limit the output to specific fields. Defaults to all fields. * * [--format=<format>] * : Output list as table, json, CSV. Defaults to table. * * ## EXAMPLES * * wp plugin get bbpress --format=json */ public function get( $args, $assoc_args ) { $plugin = $this->fetcher->get_check( $args[0] ); $file = $plugin->file; $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file, false, false ); $plugin_obj = (object)array( 'name' => Utils\get_plugin_name( $file ), 'title' => $plugin_data['Name'], 'author' => $plugin_data['Author'], 'version' => $plugin_data['Version'], 'description' => wordwrap( $plugin_data['Description'] ), 'status' => $this->get_status( $file ), ); if ( empty( $assoc_args['fields'] ) ) { $plugin_array = get_object_vars( $plugin_obj ); $assoc_args['fields'] = array_keys( $plugin_array ); } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $plugin_obj ); } /** * Uninstall a plugin. * * ## OPTIONS * * <plugin>... * : One or more plugins to uninstall. * * [--deactivate] * : Deactivate the plugin before uninstalling. Default behavior is to warn and skip if the plugin is active. * * [--skip-delete] * : If set, the plugin files will not be deleted. Only the uninstall procedure * will be run. * * ## EXAMPLES * * wp plugin uninstall hello */ function uninstall( $args, $assoc_args = array() ) { foreach ( $this->fetcher->get_many( $args ) as $plugin ) { if ( is_plugin_active( $plugin->file ) && ! WP_CLI\Utils\get_flag_value( $assoc_args, 'deactivate' ) ) { WP_CLI::warning( "The '{$plugin->name}' plugin is active." ); continue; } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'deactivate' ) ) { WP_CLI::log( "Deactivating '{$plugin->name}'..." ); $this->deactivate( array( $plugin->name ) ); } uninstall_plugin( $plugin->file ); if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-delete' ) && $this->_delete( $plugin ) ) { WP_CLI::success( "Uninstalled and deleted '$plugin->name' plugin." ); } else { WP_CLI::success( "Ran uninstall procedure for '$plugin->name' plugin without deleting." ); } } } /** * Check if the plugin is installed. * * ## OPTIONS * * <plugin> * : The plugin to check. * * ## EXAMPLES * * wp plugin is-installed hello * * @subcommand is-installed */ function is_installed( $args, $assoc_args = array() ) { if ( $this->fetcher->get( $args[0] ) ) { exit( 0 ); } else { exit( 1 ); } } /** * Delete plugin files without deactivating or uninstalling. * * ## OPTIONS * * <plugin>... * : One or more plugins to delete. * * ## EXAMPLES * * wp plugin delete hello * * # Delete inactive plugins * wp plugin delete $(wp plugin list --status=inactive --field=name) */ function delete( $args, $assoc_args = array() ) { foreach ( $this->fetcher->get_many( $args ) as $plugin ) { if ( $this->_delete( $plugin ) ) { WP_CLI::success( "Deleted '{$plugin->name}' plugin." ); } } } /** * Get a list of plugins. * * ## OPTIONS * * [--<field>=<value>] * : Filter results based on the value of a field. * * [--field=<field>] * : Prints the value of a single field for each plugin. * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each plugin: * * * name * * status * * update * * version * * These fields are optionally available: * * * update_version * * update_package * * update_id * * title * * description * * ## EXAMPLES * * wp plugin list --status=active --format=json * * # List plugins on each site in a network * wp site list --field=url | xargs -n 1 -I % wp plugin list --url=% * * @subcommand list */ public function list_( $_, $assoc_args ) { parent::_list( $_, $assoc_args ); } /* PRIVATES */ private function check_active( $file, $network_wide ) { $required = $network_wide ? 'active-network' : 'active'; return $required == $this->get_status( $file ); } private function active_output( $name, $file, $network_wide, $action ) { $network_wide = $network_wide || ( is_multisite() && is_network_only_plugin( $file ) ); $check = $this->check_active( $file, $network_wide ); if ( ( $action == "activate" ) ? $check : ! $check ) { if ( $network_wide ) WP_CLI::success( "Plugin '{$name}' network {$action}d." ); else WP_CLI::success( "Plugin '{$name}' {$action}d." ); } else { WP_CLI::warning( "Could not {$action} the '{$name}' plugin." ); } } protected function get_status( $file ) { if ( is_plugin_active_for_network( $file ) ) return 'active-network'; if ( is_plugin_active( $file ) ) return 'active'; return 'inactive'; } /** * Get the details of a plugin. * * @param object * @return array */ private function get_details( $file ) { $plugin_folder = get_plugins( '/' . plugin_basename( dirname( $file ) ) ); $plugin_file = basename( $file ); return $plugin_folder[$plugin_file]; } private function _delete( $plugin ) { $plugin_dir = dirname( $plugin->file ); if ( '.' == $plugin_dir ) $plugin_dir = $plugin->file; $path = path_join( WP_PLUGIN_DIR, $plugin_dir ); if ( \WP_CLI\Utils\is_windows() ) { // Handles plugins that are not in own folders // e.g. Hello Dolly -> plugins/hello.php if ( is_file( $path ) ) { $command = 'del /f /q '; } else { $command = 'rd /s /q '; } $path = str_replace( "/", "\\", $path ); } else { $command = 'rm -rf '; } return ! WP_CLI::launch( $command . $path ); } } WP_CLI::add_command( 'plugin', 'Plugin_Command' ); <?php /** * Manage post types. * * @package wp-cli */ class Post_Type_Command extends WP_CLI_Command { private $fields = array( 'name', 'label', 'description', 'hierarchical', 'public', 'capability_type', ); /** * List post types. * * ## OPTIONS * * [--<field>=<value>] * : Filter by one or more fields (see get_post_types() first parameter for a list of available fields). * * [--field=<field>] * : Prints the value of a single field for each post type. * * [--fields=<fields>] * : Limit the output to specific post type fields. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each term: * * * name * * label * * description * * hierarchical * * public * * capability_type * * There are no optionally available fields. * * ## EXAMPLES * * wp post-type list --format=csv * * wp post-type list --object-type=post --fields=name,public * * @subcommand list */ public function list_( $args, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); $types = get_post_types( $assoc_args, 'objects' ); $formatter->display_items( $types ); } /** * Get a post type * * ## OPTIONS * * <post-type> * : Post type slug * * [--field=<field>] * : Instead of returning the whole taxonomy, returns the value of a single field. * * [--fields=<fields>] * : Limit the output to specific fields. Defaults to all fields. * * [--format=<format>] * : Accepted values: table, json, csv. Default: table * * ## EXAMPLES * * wp post-type get page --format=json */ public function get( $args, $assoc_args ) { $post_type = get_post_type_object( $args[0] ); if ( ! $post_type ) { WP_CLI::error( "Post type {$args[0]} doesn't exist." ); } if ( empty( $assoc_args['fields'] ) ) { $default_fields = array_merge( $this->fields, array( 'labels', 'cap' ) ); $assoc_args['fields'] = $default_fields; } $data = array( 'name' => $post_type->name, 'label' => $post_type->label, 'description' => $post_type->description, 'hierarchical' => $post_type->hierarchical, 'public' => $post_type->public, 'capability_type' => $post_type->capability_type, 'labels' => $post_type->labels, 'cap' => $post_type->cap, ); $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $data ); } private function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->fields, 'post-type' ); } } WP_CLI::add_command( 'post-type', 'Post_Type_Command' ); <?php /** * Manage posts. * * @package wp-cli */ class Post_Command extends \WP_CLI\CommandWithDBObject { protected $obj_type = 'post'; protected $obj_fields = array( 'ID', 'post_title', 'post_name', 'post_date', 'post_status' ); public function __construct() { $this->fetcher = new \WP_CLI\Fetchers\Post; } /** * Create a post. * * ## OPTIONS * * [<file>] * : Read post content from <file>. If this value is present, the * `--post_content` argument will be ignored. * * Passing `-` as the filename will cause post content to * be read from STDIN. * * [--<field>=<value>] * : Associative args for the new post. See wp_insert_post(). * * [--edit] * : Immediately open system's editor to write or edit post content. * * If content is read from a file, from STDIN, or from the `--post_content` * argument, that text will be loaded into the editor. * * [--porcelain] * : Output just the new post id. * * ## EXAMPLES * * wp post create --post_type=page --post_title='A future post' --post_status=future --post_date='2020-12-01 07:00:00' * * wp post create ./post-content.txt --post_category=201,345 --post_title='Post from file' */ public function create( $args, $assoc_args ) { if ( ! empty( $args[0] ) ) { $assoc_args['post_content'] = $this->read_from_file_or_stdin( $args[0] ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'edit' ) ) { $input = \WP_CLI\Utils\get_flag_value( $assoc_args, 'post_content', '' ); if ( $output = $this->_edit( $input, 'WP-CLI: New Post' ) ) $assoc_args['post_content'] = $output; else $assoc_args['post_content'] = $input; } if ( isset( $assoc_args['post_category'] ) ) { $assoc_args['post_category'] = explode( ',', $assoc_args['post_category'] ); } parent::_create( $args, $assoc_args, function ( $params ) { return wp_insert_post( $params, true ); } ); } /** * Update one or more posts. * * ## OPTIONS * * <id>... * : One or more IDs of posts to update. * * [<file>] * : Read post content from <file>. If this value is present, the * `--post_content` argument will be ignored. * * Passing `-` as the filename will cause post content to * be read from STDIN. * * --<field>=<value> * : One or more fields to update. See wp_update_post(). * * [--defer-term-counting] * : Recalculate term count in batch, for a performance boost. * * ## EXAMPLES * * wp post update 123 --post_name=something --post_status=draft */ public function update( $args, $assoc_args ) { foreach( $args as $key => $arg ) { if ( is_numeric( $arg ) ) { continue; } $assoc_args['post_content'] = $this->read_from_file_or_stdin( $arg ); unset( $args[ $key ] ); break; } if ( isset( $assoc_args['post_category'] ) ) { $assoc_args['post_category'] = explode( ',', $assoc_args['post_category'] ); } parent::_update( $args, $assoc_args, function ( $params ) { return wp_update_post( $params, true ); } ); } /** * Launch system editor to edit post content. * * ## OPTIONS * * <id> * : The ID of the post to edit. * * ## EXAMPLES * * wp post edit 123 */ public function edit( $args, $_ ) { $post = $this->fetcher->get_check( $args[0] ); $r = $this->_edit( $post->post_content, "WP-CLI post {$post->ID}" ); if ( $r === false ) \WP_CLI::warning( 'No change made to post content.', 'Aborted' ); else $this->update( $args, array( 'post_content' => $r ) ); } protected function _edit( $content, $title ) { $content = apply_filters( 'the_editor_content', $content ); $output = \WP_CLI\Utils\launch_editor_for_input( $content, $title ); return ( is_string( $output ) ) ? apply_filters( 'content_save_pre', $output ) : $output; } /** * Get a post's content by ID. * * ## OPTIONS * * <id> * : The ID of the post to get. * * [--field=<field>] * : Instead of returning the whole post, returns the value of a single field. * * [--fields=<fields>] * : Limit the output to specific fields. Defaults to all fields. * * [--format=<format>] * : Accepted values: table, json, csv. Default: table * * ## EXAMPLES * * # save the post content to a file * wp post get 12 --field=content > file.txt */ public function get( $args, $assoc_args ) { $post = $this->fetcher->get_check( $args[0] ); $post_arr = get_object_vars( $post ); unset( $post_arr['filter'] ); if ( empty( $assoc_args['fields'] ) ) { $assoc_args['fields'] = array_keys( $post_arr ); } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $post_arr ); } /** * Delete a post by ID. * * ## OPTIONS * * <id>... * : One or more IDs of posts to delete. * * [--force] * : Skip the trash bin. * * [--defer-term-counting] * : Recalculate term count in batch, for a performance boost. * * ## EXAMPLES * * wp post delete 123 --force * * wp post delete $(wp post list --post_type='page' --format=ids) * * # delete all posts in the trash * wp post delete $(wp post list --post_status=trash --format=ids) */ public function delete( $args, $assoc_args ) { $defaults = array( 'force' => false ); $assoc_args = array_merge( $defaults, $assoc_args ); parent::_delete( $args, $assoc_args, function ( $post_id, $assoc_args ) { $status = get_post_status( $post_id ); $r = wp_delete_post( $post_id, $assoc_args['force'] ); if ( $r ) { $action = $assoc_args['force'] || 'trash' === $status ? 'Deleted' : 'Trashed'; return array( 'success', "$action post $post_id." ); } else { return array( 'error', "Failed deleting post $post_id." ); } } ); } /** * Get a list of posts. * * ## OPTIONS * * [--<field>=<value>] * : One or more args to pass to WP_Query. * * [--field=<field>] * : Prints the value of a single field for each post. * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count, ids. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each post: * * * ID * * post_title * * post_name * * post_date * * post_status * * These fields are optionally available: * * * post_author * * post_date_gmt * * post_content * * post_excerpt * * comment_status * * ping_status * * post_password * * to_ping * * pinged * * post_modified * * post_modified_gmt * * post_content_filtered * * post_parent * * guid * * menu_order * * post_type * * post_mime_type * * comment_count * * filter * * url * * ## EXAMPLES * * wp post list --field=ID * * wp post list --post_type=post --posts_per_page=5 --format=json * * wp post list --post_type=page --fields=post_title,post_status * * wp post list --post_type=page,post --format=ids * * wp post list --post__in=1,3 * * @subcommand list */ public function list_( $_, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); $defaults = array( 'posts_per_page' => -1, 'post_status' => 'any', ); $query_args = array_merge( $defaults, $assoc_args ); foreach ( $query_args as $key => $query_arg ) { if ( false !== strpos( $key, '__' ) || ( 'post_type' == $key && 'any' != $query_arg ) ) { $query_args[$key] = explode( ',', $query_arg ); } } if ( 'ids' == $formatter->format ) { $query_args['fields'] = 'ids'; $query = new WP_Query( $query_args ); echo implode( ' ', $query->posts ); } else { $query = new WP_Query( $query_args ); $posts = array_map( function( $post ) { $post->url = get_permalink( $post->ID ); return $post; }, $query->posts ); $formatter->display_items( $posts ); } } /** * Generate some posts. * * ## OPTIONS * * [--count=<number>] * : How many posts to generate. Default: 100 * * [--post_type=<type>] * : The type of the generated posts. Default: 'post' * * [--post_status=<status>] * : The status of the generated posts. Default: 'publish' * * [--post_author=<login>] * : The author of the generated posts. Default: none * * [--post_date=<yyyy-mm-dd>] * : The date of the generated posts. Default: current date * * [--post_content] * : If set, the command reads the post_content from STDIN. * * [--max_depth=<number>] * : For hierarchical post types, generate child posts down to a certain depth. Default: 1 * * ## EXAMPLES * * wp post generate --count=10 --post_type=page --post_date=1999-01-04 * curl http://loripsum.net/api/5 | wp post generate --post_content --count=10 */ public function generate( $args, $assoc_args ) { global $wpdb; $defaults = array( 'count' => 100, 'max_depth' => 1, 'post_type' => 'post', 'post_status' => 'publish', 'post_author' => false, 'post_date' => current_time( 'mysql' ), 'post_content' => '', ); extract( array_merge( $defaults, $assoc_args ), EXTR_SKIP ); // @codingStandardsIgnoreStart if ( !post_type_exists( $post_type ) ) { WP_CLI::error( sprintf( "'%s' is not a registered post type.", $post_type ) ); } if ( $post_author ) { $user_fetcher = new \WP_CLI\Fetchers\User; $post_author = $user_fetcher->get_check( $post_author )->ID; } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'post_content' ) ) { $post_content = file_get_contents( 'php://stdin' ); } // Get the total number of posts $total = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = %s", $post_type ) ); $label = get_post_type_object( $post_type )->labels->singular_name; $hierarchical = get_post_type_object( $post_type )->hierarchical; $limit = $count + $total; $notify = \WP_CLI\Utils\make_progress_bar( 'Generating posts', $count ); $previous_post_id = 0; $current_depth = 1; $current_parent = 0; for ( $i = $total; $i < $limit; $i++ ) { if ( $hierarchical ) { if( $this->maybe_make_child() && $current_depth < $max_depth ) { $current_parent = $previous_post_id; $current_depth++; } else if( $this->maybe_reset_depth() ) { $current_depth = 1; $current_parent = 0; } } $args = array( 'post_type' => $post_type, 'post_title' => "$label $i", 'post_status' => $post_status, 'post_author' => $post_author, 'post_parent' => $current_parent, 'post_name' => "post-$i", 'post_date' => $post_date, 'post_content' => $post_content, ); $post_id = wp_insert_post( $args, true ); if ( is_wp_error( $post_id ) ) { WP_CLI::warning( $post_id ); } else { $previous_post_id = $post_id; } $notify->tick(); } $notify->finish(); // @codingStandardsIgnoreEnd } private function maybe_make_child() { // 50% chance of making child post return ( mt_rand(1, 2) == 1 ); } private function maybe_reset_depth() { // 10% chance of reseting to root depth return ( mt_rand(1, 10) == 7 ); } /** * Read post content from file or STDIN * * @param string $arg Supplied argument * @return string */ private function read_from_file_or_stdin( $arg ) { if ( $arg !== '-' ) { $readfile = $arg; if ( ! file_exists( $readfile ) || ! is_file( $readfile ) ) { \WP_CLI::error( "Unable to read content from $readfile." ); } } else { $readfile = 'php://stdin'; } return file_get_contents( $readfile ); } } /** * Manage post custom fields. * * ## OPTIONS * * [--format=json] * : Encode/decode values as JSON. * * ## EXAMPLES * * wp post meta set 123 _wp_page_template about.php */ class Post_Meta_Command extends \WP_CLI\CommandWithMeta { protected $meta_type = 'post'; /** * Check that the post ID exists * * @param int */ protected function check_object_id( $object_id ) { $fetcher = new \WP_CLI\Fetchers\Post; $post = $fetcher->get_check( $object_id ); return $post->ID; } } /** * Manage post terms. * * * ## EXAMPLES * * wp post term set 123 test category */ class Post_Term_Command extends \WP_CLI\CommandWithTerms { protected $obj_type = 'post'; public function __construct() { $this->fetcher = new \WP_CLI\Fetchers\Post; } protected function get_object_type() { $post = $this->fetcher->get_check( $this->get_obj_id() ); return $post->post_type; } } WP_CLI::add_command( 'post', 'Post_Command' ); WP_CLI::add_command( 'post meta', 'Post_Meta_Command' ); WP_CLI::add_command( 'post term', 'Post_Term_Command' ); <?php /** * Manage rewrite rules. * * @package wp-cli */ class Rewrite_Command extends WP_CLI_Command { /** * Flush rewrite rules. * * ## DESCRIPTION * * Resets WordPress' rewrite rules based on registered post types, etc. * * To regenerate a .htaccess file with WP-CLI, you'll need to add the mod_rewrite module * to your wp-cli.yml or config.yml. For example: * * `apache_modules: * - mod_rewrite` * * ## OPTIONS * * [--hard] * : Perform a hard flush - update `.htaccess` rules as well as rewrite rules in database. Works only on single site installs. */ public function flush( $args, $assoc_args ) { // make sure we detect mod_rewrite if configured in apache_modules in config self::apache_modules(); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'hard' ) && ! in_array( 'mod_rewrite', (array) WP_CLI::get_config( 'apache_modules' ) ) ) { WP_CLI::warning( "Regenerating a .htaccess file requires special configuration. See usage docs." ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'hard' ) && is_multisite() ) { WP_CLI::warning( "WordPress can't generate .htaccess file for a multisite install." ); } flush_rewrite_rules( \WP_CLI\Utils\get_flag_value( $assoc_args, 'hard' ) ); if ( ! get_option( 'rewrite_rules' ) ) { WP_CLI::warning( "Rewrite rules are empty, possibly because of a missing permalink_structure option. Use 'wp rewrite list' to verify, or 'wp rewrite structure' to update permalink_structure." ); } else { WP_CLI::success( 'Rewrite rules flushed.' ); } } /** * Update the permalink structure. * * ## DESCRIPTION * * Updates the post permalink structure. * * To regenerate a .htaccess file with WP-CLI, you'll need to add the mod_rewrite module * to your wp-cli.yml or config.yml. For example: * * `apache_modules: * - mod_rewrite` * * ## OPTIONS * * <permastruct> * : The new permalink structure to apply. * * [--category-base=<base>] * : Set the base for category permalinks, i.e. '/category/'. * * [--tag-base=<base>] * : Set the base for tag permalinks, i.e. '/tag/'. * * [--hard] * : Perform a hard flush - update `.htaccess` rules as well as rewrite rules in database. * * ## EXAMPLES * * wp rewrite structure '/%year%/%monthnum%/%postname%' */ public function structure( $args, $assoc_args ) { global $wp_rewrite; // copypasta from /wp-admin/options-permalink.php $prefix = $blog_prefix = ''; if ( is_multisite() && !is_subdomain_install() && is_main_site() ) $blog_prefix = '/blog'; $permalink_structure = ( $args[0] == 'default' ) ? '' : $args[0]; if ( ! empty( $permalink_structure ) ) { $permalink_structure = preg_replace( '#/+#', '/', '/' . str_replace( '#', '', $permalink_structure ) ); if ( $prefix && $blog_prefix ) $permalink_structure = $prefix . preg_replace( '#^/?index\.php#', '', $permalink_structure ); else $permalink_structure = $blog_prefix . $permalink_structure; } $wp_rewrite->set_permalink_structure( $permalink_structure ); // Update category or tag bases if ( isset( $assoc_args['category-base'] ) ) { $category_base = $assoc_args['category-base']; if ( ! empty( $category_base ) ) $category_base = $blog_prefix . preg_replace('#/+#', '/', '/' . str_replace( '#', '', $category_base ) ); $wp_rewrite->set_category_base( $category_base ); } if ( isset( $assoc_args['tag-base'] ) ) { $tag_base = $assoc_args['tag-base']; if ( ! empty( $tag_base ) ) $tag_base = $blog_prefix . preg_replace('#/+#', '/', '/' . str_replace( '#', '', $tag_base ) ); $wp_rewrite->set_tag_base( $tag_base ); } // make sure we detect mod_rewrite if configured in apache_modules in config self::apache_modules(); // Launch a new process to flush rewrites because core expects flush // to happen after rewrites are set $new_assoc_args = array(); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'hard' ) ) { $new_assoc_args['hard'] = true; if ( ! in_array( 'mod_rewrite', (array) WP_CLI::get_config( 'apache_modules' ) ) ) { WP_CLI::warning( "Regenerating a .htaccess file requires special configuration. See usage docs." ); } } $process_run = WP_CLI::launch_self( 'rewrite flush', array(), $new_assoc_args, true, true, array( 'apache_modules', WP_CLI::get_config( 'apache_modules' ) ) ); if ( ! empty( $process_run->stderr ) ) { // Strip "Warning: " WP_CLI::warning( substr( $process_run->stderr, 9 ) ); } WP_CLI::success( "Rewrite structure set." ); } /** * Print current rewrite rules. * * ## OPTIONS * * [--match=<url>] * : Show rewrite rules matching a particular URL. * * [--source=<source>] * : Show rewrite rules from a particular source. * * [--fields=<fields>] * : Limit the output to specific fields. Defaults to match,query,source. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## EXAMPLES * * wp rewrite list --format=csv * * @subcommand list */ public function list_( $args, $assoc_args ) { global $wp_rewrite; $rules = get_option( 'rewrite_rules' ); if ( ! $rules ) { $rules = array(); WP_CLI::warning( 'No rewrite rules.' ); } $defaults = array( 'source' => '', 'match' => '', 'format' => 'table', 'fields' => 'match,query,source', ); $assoc_args = array_merge( $defaults, $assoc_args ); $rewrite_rules_by_source = array(); $rewrite_rules_by_source['post'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->permalink_structure, EP_PERMALINK ); $rewrite_rules_by_source['date'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_date_permastruct(), EP_DATE ); $rewrite_rules_by_source['root'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->root . '/', EP_ROOT ); $rewrite_rules_by_source['comments'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->root . $wp_rewrite->comments_base, EP_COMMENTS, true, true, true, false ); $rewrite_rules_by_source['search'] = $wp_rewrite->generate_rewrite_rules( $wp_rewrite->get_search_permastruct(), EP_SEARCH ); $rewrite_rules_by_source['author'] = $wp_rewrite->generate_rewrite_rules($wp_rewrite->get_author_permastruct(), EP_AUTHORS ); $rewrite_rules_by_source['page'] = $wp_rewrite->page_rewrite_rules(); // Extra permastructs including tags, categories, etc. foreach ( $wp_rewrite->extra_permastructs as $permastructname => $permastruct ) { if ( is_array( $permastruct ) ) { $rewrite_rules_by_source[$permastructname] = $wp_rewrite->generate_rewrite_rules( $permastruct['struct'], $permastruct['ep_mask'], $permastruct['paged'], $permastruct['feed'], $permastruct['forcomments'], $permastruct['walk_dirs'], $permastruct['endpoints'] ); } else { $rewrite_rules_by_source[$permastructname] = $wp_rewrite->generate_rewrite_rules( $permastruct, EP_NONE ); } } // Apply the filters used in core just in case foreach( $rewrite_rules_by_source as $source => $source_rules ) { $rewrite_rules_by_source[$source] = apply_filters( $source . '_rewrite_rules', $source_rules ); if ( 'post_tag' == $source ) $rewrite_rules_by_source[$source] = apply_filters( 'tag_rewrite_rules', $source_rules ); } $rule_list = array(); foreach ( $rules as $match => $query ) { if ( ! empty( $assoc_args['match'] ) && ! preg_match( "!^$match!", trim( $assoc_args['match'], '/' ) ) ) continue; $source = 'other'; foreach( $rewrite_rules_by_source as $rules_source => $source_rules ) { if ( array_key_exists( $match, $source_rules ) ) { $source = $rules_source; } } if ( ! empty( $assoc_args['source'] ) && $source != $assoc_args['source'] ) continue; $rule_list[] = compact( 'match', 'query', 'source' ); } WP_CLI\Utils\format_items( $assoc_args['format'], $rule_list, explode( ',', $assoc_args['fields'] ) ); } /** * Expose apache modules if present in config * * Implementation Notes: This function exposes a global function * apache_get_modules and also sets the $is_apache global variable. * * This is so that flush_rewrite_rules will actually write out the * .htaccess file for apache wordpress installations. There is a check * to see: * * 1. if the $is_apache variable is set. * 2. if the mod_rewrite module is returned from the apache_get_modules * function. * * To get this to work with wp-cli you'll need to add the mod_rewrite module * to your config.yml. For example * * apache_modules: * - mod_rewrite * * If this isn't done then the .htaccess rewrite rules won't be flushed out * to disk. */ private static function apache_modules() { $mods = WP_CLI::get_config('apache_modules'); if ( !empty( $mods ) && !function_exists( 'apache_get_modules' ) ) { global $is_apache; $is_apache = true; // needed for get_home_path() and .htaccess location $_SERVER['SCRIPT_FILENAME'] = ABSPATH; function apache_get_modules() { return WP_CLI::get_config( 'apache_modules' ); } } } } WP_CLI:: add_command( 'rewrite', 'Rewrite_Command' ); <?php /** * Manage user roles. * * @package wp-cli */ class Role_Command extends WP_CLI_Command { private $fields = array( 'name', 'role' ); /** * List all roles. * * ## OPTIONS * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each role: * * * name * * role * * There are no optional fields. * * ## EXAMPLES * * wp role list --fields=role --format=csv * * @subcommand list */ public function list_( $args, $assoc_args ) { global $wp_roles; $output_roles = array(); foreach ( $wp_roles->roles as $key => $role ) { $output_role = new stdClass; $output_role->name = $role['name']; $output_role->role = $key; $output_roles[] = $output_role; } $formatter = new \WP_CLI\Formatter( $assoc_args, $this->fields ); $formatter->display_items( $output_roles ); } /** * Check if a role exists. * * ##DESCRIPTION * * Will exit with status 0 if the role exists, 1 if it does not. * * ## OPTIONS * * <role-key> * : The internal name of the role. * * ## EXAMPLES * * wp role exists editor */ public function exists( $args ) { global $wp_roles; if ( ! in_array($args[0], array_keys( $wp_roles->roles ) ) ) { WP_CLI::error( "Role with ID $args[0] does not exist." ); } WP_CLI::success( "Role with ID $args[0] exists." ); } /** * Create a new role. * * ## OPTIONS * * <role-key> * : The internal name of the role. * * <role-name> * : The publicly visible name of the role. * * [--clone=<role>] * : Clone capabilities from an existing role. * * ## EXAMPLES * * wp role create approver Approver * * wp role create productadmin "Product Administrator" */ public function create( $args, $assoc_args ) { global $wp_roles; self::persistence_check(); $role_key = array_shift( $args ); $role_name = array_shift( $args ); if ( empty( $role_key ) || empty( $role_name ) ) { WP_CLI::error( "Can't create role, insufficient information provided."); } $capabilities = false; if ( ! empty( $assoc_args['clone'] ) ) { $role_obj = $wp_roles->get_role( $assoc_args['clone'] ); if ( ! $role_obj ) { WP_CLI::error( "'{$assoc_args['clone']}' role not found." ); } $capabilities = array_keys( $role_obj->capabilities ); } if ( add_role( $role_key, $role_name ) ) { if ( ! empty( $capabilities ) ) { $role_obj = $wp_roles->get_role( $role_key ); foreach( $capabilities as $cap ) { $role_obj->add_cap( $cap ); } WP_CLI::success( sprintf( "Role with key %s created. Cloned capabilities from %s.", $role_key, $assoc_args['clone'] ) ); } else { WP_CLI::success( sprintf( "Role with key %s created.", $role_key ) ); } } else { WP_CLI::error( "Role couldn't be created." ); } } /** * Delete an existing role. * * ## OPTIONS * * <role-key> * : The internal name of the role. * * ## EXAMPLES * * wp role delete approver * * wp role delete productadmin */ public function delete( $args ) { global $wp_roles; self::persistence_check(); $role_key = array_shift( $args ); if ( empty( $role_key ) || ! isset( $wp_roles->roles[$role_key] ) ) WP_CLI::error( "Role key not provided, or is invalid." ); remove_role( $role_key ); // Note: remove_role() doesn't indicate success or otherwise, so we have to // check ourselves if ( ! isset( $wp_roles->roles[$role_key] ) ) WP_CLI::success( sprintf( "Role with key %s deleted.", $role_key ) ); else WP_CLI::error( sprintf( "Role with key %s could not be deleted.", $role_key ) ); } /** * Reset any default role to default capabilities. * * ## OPTIONS * * [<role-key>...] * : The internal name of one or more roles to reset. * * [--all] * : If set, all default roles will be reset. * * ## EXAMPLES * * wp role reset administrator author contributor * * wp role reset --all */ public function reset( $args, $assoc_args ) { self::persistence_check(); if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) WP_CLI::error( "Role key not provided, or is invalid." ); if ( ! function_exists( 'populate_roles' ) ) { require_once( ABSPATH.'wp-admin/includes/schema.php' ); } // get our default roles $default_roles = $preserve = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { foreach( $default_roles as $role ) { remove_role( $role ); } populate_roles(); WP_CLI::success( 'All default roles reset.' ); return; } foreach( $args as $k => $role_key ) { $key = array_search( $role_key, $default_roles ); if ( false !== $key ) { unset( $preserve[ $key ] ); $before[ $role_key ] = get_role( $role_key ); remove_role( $role_key ); } else { unset( $args[ $k ] ); } } $num_to_reset = count( $args ); // no roles were unset, bail if ( count( $default_roles ) == count( $preserve ) ) { WP_CLI::error( 'Must specify a default role to reset.' ); } // for the roles we're not resetting foreach( $preserve as $k => $role ) { /* save roles * if get_role is null * save role name for re-removal */ $roleobj = get_role( $role ); $preserve[$k] = is_null( $roleobj ) ? $role : $roleobj; remove_role( $role ); } // put back all default roles and capabilities populate_roles(); // restore the preserved roles foreach( $preserve as $k => $roleobj ) { // re-remove after populating if ( is_a( $roleobj, 'WP_Role' ) ) { remove_role( $roleobj->name ); add_role( $roleobj->name, ucwords( $roleobj->name ), $roleobj->capabilities ); } else { // when not an object, that means the role didn't exist before remove_role( $roleobj ); } } $num_reset = 0; foreach( $args as $role_key ) { $after[ $role_key ] = get_role( $role_key ); if ( $after[ $role_key ] != $before[ $role_key ] ) { ++$num_reset; } } WP_CLI::success( "Reset $num_reset/$num_to_reset roles" ); } private static function persistence_check() { global $wp_roles; if ( !$wp_roles->use_db ) WP_CLI::error( "Role definitions are not persistent." ); } } WP_CLI::add_command( 'role', 'Role_Command' ); <?php use WP_CLI\Utils; use WP_CLI\Process; /** * Generate code for post types, taxonomies, etc. * * @package wp-cli */ class Scaffold_Command extends WP_CLI_Command { /** * Generate PHP code for registering a custom post type. * * ## OPTIONS * * <slug> * : The internal name of the post type. * * [--label=<label>] * : The text used to translate the update messages * * [--textdomain=<textdomain>] * : The textdomain to use for the labels. * * [--dashicon=<dashicon>] * : The dashicon to use in the menu. * * [--theme] * : Create a file in the active theme directory, instead of sending to * STDOUT. Specify a theme with `--theme=<theme>` to have the file placed in that theme. * * [--plugin=<plugin>] * : Create a file in the given plugin's directory, instead of sending to STDOUT. * * [--raw] * : Just generate the `register_post_type()` call and nothing else. * * [--force] * : Overwrite files that already exist. * * @subcommand post-type * * @alias cpt */ function post_type( $args, $assoc_args ) { if ( strlen( $args[0] ) > 20 ) { WP_CLI::error( "Post type slugs cannot exceed 20 characters in length." ); } $defaults = array( 'textdomain' => '', 'dashicon' => 'admin-post', ); $this->_scaffold( $args[0], $assoc_args, $defaults, '/post-types/', array( 'post_type.mustache', 'post_type_extended.mustache' ) ); } /** * Generate PHP code for registering a custom taxonomy. * * ## OPTIONS * * <slug> * : The internal name of the taxonomy. * * [--post_types=<post-types>] * : Post types to register for use with the taxonomy. * * [--label=<label>] * : The text used to translate the update messages. * * [--textdomain=<textdomain>] * : The textdomain to use for the labels. * * [--theme] * : Create a file in the active theme directory, instead of sending to * STDOUT. Specify a theme with `--theme=<theme>` to have the file placed in that theme. * * [--plugin=<plugin>] * : Create a file in the given plugin's directory, instead of sending to STDOUT. * * [--raw] * : Just generate the `register_taxonomy()` call and nothing else. * * [--force] * : Overwrite files that already exist. * * ## EXAMPLES * * wp scaffold taxonomy venue --post_types=event,presentation * * @subcommand taxonomy * * @alias tax */ function taxonomy( $args, $assoc_args ) { $defaults = array( 'textdomain' => '', 'post_types' => "'post'" ); if ( isset( $assoc_args['post_types'] ) ) { $assoc_args['post_types'] = $this->quote_comma_list_elements( $assoc_args['post_types'] ); } $this->_scaffold( $args[0], $assoc_args, $defaults, '/taxonomies/', array( 'taxonomy.mustache', 'taxonomy_extended.mustache' ) ); } private function _scaffold( $slug, $assoc_args, $defaults, $subdir, $templates ) { $wp_filesystem = $this->init_wp_filesystem(); $control_args = $this->extract_args( $assoc_args, array( 'label' => preg_replace( '/_|-/', ' ', strtolower( $slug ) ), 'theme' => false, 'plugin' => false, 'raw' => false, ) ); $vars = $this->extract_args( $assoc_args, $defaults ); $vars['slug'] = $slug; $vars['textdomain'] = $this->get_textdomain( $vars['textdomain'], $control_args ); $vars['label'] = $control_args['label']; $vars['label_ucfirst'] = ucfirst( $vars['label'] ); $vars['label_plural'] = $this->pluralize( $vars['label'] ); $vars['label_plural_ucfirst'] = ucfirst( $vars['label_plural'] ); // We use the machine name for function declarations $machine_name = preg_replace( '/-/', '_', $slug ); $machine_name_plural = $this->pluralize( $slug ); list( $raw_template, $extended_template ) = $templates; $raw_output = Utils\mustache_render( $raw_template, $vars ); if ( ! $control_args['raw'] ) { $vars = array_merge( $vars, array( 'machine_name' => $machine_name, 'output' => $raw_output ) ); $final_output = Utils\mustache_render( $extended_template, $vars ); } else { $final_output = $raw_output; } if ( $path = $this->get_output_path( $control_args, $subdir ) ) { $filename = $path . $slug . '.php'; $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ); $files_written = $this->create_files( array( $filename => $final_output ), $force ); $this->log_whether_files_written( $files_written, $skip_message = "Skipped creating $filename", $success_message = "Created $filename" ); } else { // STDOUT echo $final_output; } } /** * Generate starter code for a theme. * * ## OPTIONS * * <slug> * : The slug for the new theme, used for prefixing functions. * * [--activate] * : Activate the newly downloaded theme. * * [--enable-network] * : Enable the newly downloaded theme for the entire network. * * [--theme_name=<title>] * : What to put in the 'Theme Name:' header in style.css * * [--author=<full-name>] * : What to put in the 'Author:' header in style.css * * [--author_uri=<uri>] * : What to put in the 'Author URI:' header in style.css * * [--sassify] * : Include stylesheets as SASS * * [--force] * : Overwrite files that already exist. * */ function _s( $args, $assoc_args ) { $theme_slug = $args[0]; $theme_path = WP_CONTENT_DIR . "/themes"; $url = "http://underscores.me"; $timeout = 30; $data = wp_parse_args( $assoc_args, array( 'theme_name' => ucfirst( $theme_slug ), 'author' => "Me", 'author_uri' => "", ) ); $_s_theme_path = "$theme_path/$data[theme_name]"; $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ); $should_write_file = $this->prompt_if_files_will_be_overwritten( $_s_theme_path, $force ); if ( ! $should_write_file ) { WP_CLI::log( 'No files created' ); die; } $theme_description = "Custom theme: " . $data['theme_name'] . ", developed by " . $data['author']; $body = array(); $body['underscoresme_name'] = $data['theme_name']; $body['underscoresme_slug'] = $theme_slug; $body['underscoresme_author'] = $data['author']; $body['underscoresme_author_uri'] = $data['author_uri']; $body['underscoresme_description'] = $theme_description; $body['underscoresme_generate_submit'] = "Generate"; $body['underscoresme_generate'] = "1"; if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'sassify' ) ) { $body['underscoresme_sass'] = 1; } $tmpfname = wp_tempnam( $url ); $response = wp_remote_post( $url, array( 'timeout' => $timeout, 'body' => $body, 'stream' => true, 'filename' => $tmpfname ) ); if ( is_wp_error( $response ) ) { WP_CLI::error( $response ); } $response_code = wp_remote_retrieve_response_code( $response ); if ( 200 != $response_code ) { WP_CLI::error( "Couldn't create theme (received $response_code response)." ); } $this->maybe_create_themes_dir(); $this->init_wp_filesystem(); $unzip_result = unzip_file( $tmpfname, $theme_path ); unlink( $tmpfname ); if ( true === $unzip_result ) { WP_CLI::success( "Created theme '{$data['theme_name']}'." ); } else { WP_CLI::error( "Could not decompress your theme files ('{$tmpfname}') at '{$theme_path}': {$unzip_result->get_error_message()}" ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'activate' ) ) { WP_CLI::run_command( array( 'theme', 'activate', $theme_slug ) ); } else if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'enable-network' ) ) { WP_CLI::run_command( array( 'theme', 'enable', $theme_slug ), array( 'network' => true ) ); } } /** * Generate empty child theme. * * ## OPTIONS * * <slug> * : The slug for the new child theme. * * --parent_theme=<slug> * : What to put in the 'Template:' header in style.css * * [--theme_name=<title>] * : What to put in the 'Theme Name:' header in style.css * * [--author=<full-name>] * : What to put in the 'Author:' header in style.css * * [--author_uri=<uri>] * : What to put in the 'Author URI:' header in style.css * * [--theme_uri=<uri>] * : What to put in the 'Theme URI:' header in style.css * * [--activate] * : Activate the newly created child theme. * * [--enable-network] * : Enable the newly created child theme for the entire network. * * [--force] * : Overwrite files that already exist. * * @subcommand child-theme */ function child_theme( $args, $assoc_args ) { $theme_slug = $args[0]; $data = wp_parse_args( $assoc_args, array( 'theme_name' => ucfirst( $theme_slug ), 'author' => "Me", 'author_uri' => "", 'theme_uri' => "" ) ); $data['slug'] = $theme_slug; $data['parent_theme_function_safe'] = str_replace( '-', '_', $data['parent_theme'] ); $data['description'] = ucfirst( $data['parent_theme'] ) . " child theme."; $theme_dir = WP_CONTENT_DIR . "/themes" . "/$theme_slug"; $theme_style_path = "$theme_dir/style.css"; $theme_functions_path = "$theme_dir/functions.php"; $this->maybe_create_themes_dir(); $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ); $files_written = $this->create_files( array( $theme_style_path => Utils\mustache_render( 'child_theme.mustache', $data ), $theme_functions_path => Utils\mustache_render( 'child_theme_functions.mustache', $data ) ), $force ); $this->log_whether_files_written( $files_written, $skip_message = 'All theme files were skipped.', $success_message = "Created $theme_dir." ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'activate' ) ) { WP_CLI::run_command( array( 'theme', 'activate', $theme_slug ) ); } else if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'enable-network' ) ) { WP_CLI::run_command( array( 'theme', 'enable', $theme_slug ), array( 'network' => true ) ); } } private function get_output_path( $assoc_args, $subdir ) { if ( $assoc_args['theme'] ) { $theme = $assoc_args['theme']; if ( is_string( $theme ) ) { $path = get_theme_root( $theme ) . '/' . $theme; } else { $path = get_stylesheet_directory(); } } elseif ( $assoc_args['plugin'] ) { $plugin = $assoc_args['plugin']; $path = WP_PLUGIN_DIR . '/' . $plugin; if ( ! is_dir( $path ) ) { WP_CLI::error( "Can't find '$plugin' plugin." ); } } else { return false; } $path .= $subdir; return $path; } /** * Generate files needed for writing Behat tests for your command. * * ## DESCRIPTION * * These are the files that are generated: * * * `.travis.yml` is the configuration file for Travis CI * * `bin/install-package-tests.sh` will configure environment to run tests. Script expects WP_CLI_BIN_DIR and WP_CLI_CONFIG_PATH environment variables. * * `features/load-wp-cli.feature` is a basic test to confirm WP-CLI can load. * * `features/bootstrap`, `features/steps`, `features/extra` are Behat configuration files. * * `utils/generate-package-require-from-composer.php` generates a test config.yml file from your package's composer.json * * ## ENVIRONMENT * * The `features/bootstrap/FeatureContext.php` file expects the WP_CLI_BIN_DIR and WP_CLI_CONFIG_PATH environment variables. * * WP-CLI Behat framework uses Behat ~2.5. * * ## OPTIONS * * <dir> * : The package directory to generate tests for. * * [--force] * : Overwrite files that already exist. * * ## EXAMPLE * * wp scaffold package-tests /path/to/command/dir/ * * @when before_wp_load * @subcommand package-tests */ public function package_tests( $args, $assoc_args ) { list( $package_dir ) = $args; if ( is_file( $package_dir ) ) { $package_dir = dirname( $package_dir ); } else if ( is_dir( $package_dir ) ) { $package_dir = rtrim( $package_dir, '/' ); } if ( ! is_dir( $package_dir ) || ! file_exists( $package_dir . '/composer.json' ) ) { WP_CLI::error( "Invalid package directory. composer.json file must be present." ); } $package_dir .= '/'; $bin_dir = $package_dir . 'bin/'; $utils_dir = $package_dir . 'utils/'; $features_dir = $package_dir . 'features/'; $bootstrap_dir = $features_dir . 'bootstrap/'; $steps_dir = $features_dir . 'steps/'; $extra_dir = $features_dir . 'extra/'; foreach ( array( $features_dir, $bootstrap_dir, $steps_dir, $extra_dir, $utils_dir, $bin_dir ) as $dir ) { if ( ! is_dir( $dir ) ) { Process::create( Utils\esc_cmd( 'mkdir %s', $dir ) )->run(); } } $to_copy = array( 'templates/.travis.package.yml' => $package_dir, 'templates/load-wp-cli.feature' => $features_dir, 'templates/install-package-tests.sh' => $bin_dir, 'features/bootstrap/FeatureContext.php' => $bootstrap_dir, 'features/bootstrap/support.php' => $bootstrap_dir, 'php/WP_CLI/Process.php' => $bootstrap_dir, 'php/utils.php' => $bootstrap_dir, 'ci/behat-tags.php' => $utils_dir, 'utils/get-package-require-from-composer.php' => $utils_dir, 'features/steps/given.php' => $steps_dir, 'features/steps/when.php' => $steps_dir, 'features/steps/then.php' => $steps_dir, 'features/extra/no-mail.php' => $extra_dir, ); $files_written = array(); foreach ( $to_copy as $file => $dir ) { // file_get_contents() works with Phar-archived files $contents = file_get_contents( WP_CLI_ROOT . "/{$file}" ); $file_path = $dir . basename( $file ); $file_path = str_replace( array( '.travis.package.yml' ), array( '.travis.yml' ), $file_path ); $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ); $should_write_file = $this->prompt_if_files_will_be_overwritten( $file_path, $force ); if ( ! $should_write_file ) { continue; } $files_written[] = $file_path; $result = Process::create( Utils\esc_cmd( 'touch %s', $file_path ) )->run(); file_put_contents( $file_path, $contents ); if ( 'templates/install-package-tests.sh' === $file ) { Process::create( Utils\esc_cmd( 'chmod +x %s', $file_path ) )->run(); } } $this->log_whether_files_written( $files_written, $skip_message = 'All package tests were skipped.', $success_message = 'Created test files.' ); } /** * Generate starter code for a plugin. * * ## OPTIONS * * <slug> * : The internal name of the plugin. * * [--dir=<dirname>] * : Put the new plugin in some arbitrary directory path. Plugin directory will be path plus supplied slug. * * [--plugin_name=<title>] * : What to put in the 'Plugin Name:' header * * [--plugin_description=<description>] * : What to put in the 'Description:' header. * * [--plugin_author=<author>] * : What to put in the 'Author:' header. * * [--plugin_author_uri=<url>] * : What to put in the 'Author URI:' header. * * [--plugin_uri=<url>] * : What to put in the 'Plugin URI:' header. * * [--skip-tests] * : Don't generate files for unit testing. * * [--activate] * : Activate the newly generated plugin. * * [--activate-network] * : Network activate the newly generated plugin. * * [--force] * : Overwrite files that already exist. * */ function plugin( $args, $assoc_args ) { $plugin_slug = $args[0]; $data = wp_parse_args( $assoc_args, array( 'plugin_slug' => $plugin_slug, 'plugin_name' => ucfirst( $plugin_slug ), 'plugin_description' => 'PLUGIN DESCRIPTION HERE', 'plugin_author' => 'YOUR NAME HERE', 'plugin_author_uri' => 'YOUR SITE HERE', 'plugin_uri' => 'PLUGIN SITE HERE', ) ); $data['textdomain'] = $plugin_slug; if ( ! empty( $assoc_args['dir'] ) ) { if ( ! is_dir( $assoc_args['dir'] ) ) { WP_CLI::error( "Cannot create plugin in directory that doesn't exist." ); } $plugin_dir = $assoc_args['dir'] . "/$plugin_slug"; } else { $plugin_dir = WP_PLUGIN_DIR . "/$plugin_slug"; $this->maybe_create_plugins_dir(); } $plugin_path = "$plugin_dir/$plugin_slug.php"; $plugin_readme_path = "$plugin_dir/readme.txt"; $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ); $files_written = $this->create_files( array( $plugin_path => Utils\mustache_render( 'plugin.mustache', $data ), $plugin_readme_path => Utils\mustache_render( 'plugin-readme.mustache', $data ), "$plugin_dir/package.json" => Utils\mustache_render( 'plugin-packages.mustache', $data ), "$plugin_dir/Gruntfile.js" => Utils\mustache_render( 'plugin-gruntfile.mustache', $data ), "$plugin_dir/.gitignore" => Utils\mustache_render( 'plugin-gitignore.mustache', $data ), "$plugin_dir/.editorconfig" => file_get_contents( WP_CLI_ROOT . "/templates/.editorconfig" ), ), $force ); $this->log_whether_files_written( $files_written, $skip_message = 'All plugin files were skipped.', $success_message = 'Created plugin files.' ); if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-tests' ) ) { WP_CLI::run_command( array( 'scaffold', 'plugin-tests', $plugin_slug ), array( 'dir' => $plugin_dir, 'force' => $force ) ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'activate' ) ) { WP_CLI::run_command( array( 'plugin', 'activate', $plugin_slug ) ); } else if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'activate-network' ) ) { WP_CLI::run_command( array( 'plugin', 'activate', $plugin_slug), array( 'network' => true ) ); } } /** * Generate files needed for running PHPUnit tests. * * ## DESCRIPTION * * These are the files that are generated: * * * `phpunit.xml.dist` is the configuration file for PHPUnit * * `.travis.yml` is the configuration file for Travis CI * * `tests/bootstrap.php` is the file that makes the current plugin active when running the test suite * * `tests/test-sample.php` is a sample file containing the actual tests * * ## ENVIRONMENT * * The `tests/bootstrap.php` file looks for the WP_TESTS_DIR environment * variable. * * ## OPTIONS * * [<plugin>] * : The name of the plugin to generate test files for. * * [--dir=<dirname>] * : Generate test files for a non-standard plugin path. If no plugin slug is specified, the directory name is used. * * [--force] * : Overwrite files that already exist. * * ## EXAMPLE * * wp scaffold plugin-tests hello * * @subcommand plugin-tests */ function plugin_tests( $args, $assoc_args ) { $wp_filesystem = $this->init_wp_filesystem(); if ( ! empty( $args[0] ) ) { $plugin_slug = $args[0]; $plugin_dir = WP_PLUGIN_DIR . "/$plugin_slug"; if ( empty( $assoc_args['dir'] ) && ! is_dir( $plugin_dir ) ) { WP_CLI::error( 'Invalid plugin slug specified.' ); } } if ( ! empty( $assoc_args['dir'] ) ) { $plugin_dir = $assoc_args['dir']; if ( ! is_dir( $plugin_dir ) ) { WP_CLI::error( 'Invalid plugin directory specified.' ); } if ( empty( $plugin_slug ) ) { $plugin_slug = basename( $plugin_dir ); } } if ( empty( $plugin_slug ) || empty( $plugin_dir ) ) { WP_CLI::error( 'Invalid plugin specified.' ); } $tests_dir = "$plugin_dir/tests"; $bin_dir = "$plugin_dir/bin"; $wp_filesystem->mkdir( $tests_dir ); $wp_filesystem->mkdir( $bin_dir ); $wp_versions_to_test = array('latest'); // Parse plugin readme.txt if ( file_exists( $plugin_dir . '/readme.txt' ) ) { $readme_content = file_get_contents( $plugin_dir . '/readme.txt' ); preg_match( '/Requires at least\:(.*)\n/m', $readme_content, $matches ); if ( isset( $matches[1] ) && $matches[1] ) { $wp_versions_to_test[] = trim( $matches[1] ); } preg_match( '/Tested up to\:(.*)\n/m', $readme_content, $matches ); if ( isset( $matches[1] ) && $matches[1] ) { $wp_versions_to_test[] = trim( $matches[1] ); } } $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ); $files_written = $this->create_files( array( "$tests_dir/bootstrap.php" => Utils\mustache_render( 'bootstrap.mustache', compact( 'plugin_slug' ) ), "$plugin_dir/.travis.yml" => Utils\mustache_render( '.travis.mustache', compact( 'wp_versions_to_test' ) ) ), $force ); $to_copy = array( 'install-wp-tests.sh' => $bin_dir, 'phpunit.xml.dist' => $plugin_dir, 'test-sample.php' => $tests_dir, ); foreach ( $to_copy as $file => $dir ) { $file_name = "$dir/$file"; $force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ); $should_write_file = $this->prompt_if_files_will_be_overwritten( $file_name, $force ); if ( ! $should_write_file ) continue; $files_written[] = $file_name; $wp_filesystem->copy( WP_CLI_ROOT . "/templates/$file", $file_name, true ); if ( 'install-wp-tests.sh' === $file ) { if ( ! $wp_filesystem->chmod( "$dir/$file", 0755 ) ) { WP_CLI::warning( "Couldn't mark install-wp-tests.sh as executable." ); } } } $this->log_whether_files_written( $files_written, $skip_message = 'All test files were skipped.', $success_message = 'Created test files.' ); } private function create_files( $files_and_contents, $force ) { $wp_filesystem = $this->init_wp_filesystem(); $wrote_files = array(); foreach ( $files_and_contents as $filename => $contents ) { $should_write_file = $this->prompt_if_files_will_be_overwritten( $filename, $force ); if ( ! $should_write_file ) { continue; } $wp_filesystem->mkdir( dirname( $filename ) ); if ( ! $wp_filesystem->put_contents( $filename, $contents ) ) { WP_CLI::error( "Error creating file: $filename" ); } elseif ( $should_write_file ) { $wrote_files[] = $filename; } } return $wrote_files; } private function prompt_if_files_will_be_overwritten( $filename, $force ) { $should_write_file = true; if ( ! file_exists( $filename ) ) { return true; } WP_CLI::warning( 'File already exists' ); WP_CLI::log( $filename ); if ( ! $force ) { do { $answer = cli\prompt( 'Skip this file, or replace it with scaffolding?', $default = false, $marker = '[s/r]: ' ); } while ( ! in_array( $answer, array( 's', 'r' ) ) ); $should_write_file = 'r' === $answer; } $outcome = $should_write_file ? 'Replacing' : 'Skipping'; WP_CLI::log( $outcome . PHP_EOL ); return $should_write_file; } private function log_whether_files_written( $files_written, $skip_message, $success_message ) { if ( empty( $files_written ) ) { WP_CLI::log( $skip_message ); } else { WP_CLI::success( $success_message ); } } /** * If you're writing your files to your theme directory your textdomain also needs to be the same as your theme. * Same goes for when plugin is being used. */ private function get_textdomain( $textdomain, $args ) { if ( strlen( $textdomain ) ) { return $textdomain; } if ( $args['theme'] ) { return strtolower( wp_get_theme()->template ); } if ( $args['plugin'] && true !== $args['plugin'] ) { return $args['plugin']; } return 'YOUR-TEXTDOMAIN'; } private function pluralize( $word ) { $plural = array( '/(quiz)$/i' => '\1zes', '/^(ox)$/i' => '\1en', '/([m|l])ouse$/i' => '\1ice', '/(matr|vert|ind)ix|ex$/i' => '\1ices', '/(x|ch|ss|sh)$/i' => '\1es', '/([^aeiouy]|qu)ies$/i' => '\1y', '/([^aeiouy]|qu)y$/i' => '\1ies', '/(hive)$/i' => '\1s', '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', '/sis$/i' => 'ses', '/([ti])um$/i' => '\1a', '/(buffal|tomat)o$/i' => '\1oes', '/(bu)s$/i' => '1ses', '/(alias|status)/i' => '\1es', '/(octop|vir)us$/i' => '1i', '/(ax|test)is$/i' => '\1es', '/s$/i' => 's', '/$/' => 's' ); $uncountable = array( 'equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep' ); $irregular = array( 'person' => 'people', 'man' => 'men', 'woman' => 'women', 'child' => 'children', 'sex' => 'sexes', 'move' => 'moves' ); $lowercased_word = strtolower( $word ); foreach ( $uncountable as $_uncountable ) { if ( substr( $lowercased_word, ( - 1 * strlen( $_uncountable ) ) ) == $_uncountable ) { return $word; } } foreach ( $irregular as $_plural => $_singular ) { if ( preg_match( '/(' . $_plural . ')$/i', $word, $arr ) ) { return preg_replace( '/(' . $_plural . ')$/i', substr( $arr[0], 0, 1 ) . substr( $_singular, 1 ), $word ); } } foreach ( $plural as $rule => $replacement ) { if ( preg_match( $rule, $word ) ) { return preg_replace( $rule, $replacement, $word ); } } return false; } protected function extract_args( $assoc_args, $defaults ) { $out = array(); foreach ( $defaults as $key => $value ) { $out[ $key ] = \WP_CLI\Utils\get_flag_value( $assoc_args, $key, $value ); } return $out; } protected function quote_comma_list_elements( $comma_list ) { return "'" . implode( "', '", explode( ',', $comma_list ) ) . "'"; } /** * Create the themes directory if it doesn't already exist */ protected function maybe_create_themes_dir() { $themes_dir = WP_CONTENT_DIR . '/themes'; if ( ! is_dir( $themes_dir ) ) { wp_mkdir_p( $themes_dir ); } } /** * Create the plugins directory if it doesn't already exist */ protected function maybe_create_plugins_dir() { if ( ! is_dir( WP_PLUGIN_DIR ) ) { wp_mkdir_p( WP_PLUGIN_DIR ); } } /** * Initialize WP Filesystem */ private function init_wp_filesystem() { global $wp_filesystem; WP_Filesystem(); return $wp_filesystem; } } WP_CLI::add_command( 'scaffold', 'Scaffold_Command' ); <?php /** * Search and replace strings in the database. * * @package wp-cli */ class Search_Replace_Command extends WP_CLI_Command { private $dry_run; private $export_handle = false; private $recurse_objects; private $regex; private $skip_columns; /** * Search/replace strings in the database. * * ## DESCRIPTION * * This command will searches through all rows in a selection of tables * and replaces appearances of the first string with the second string. * * By default, the command uses tables registered to the $wpdb object. On * multisite, this will just be the tables for the current site unless * --network is specified. * * Search/replace intelligently handles PHP serialized data, and does not * change primary key values. * * ## OPTIONS * * <old> * : A string to search for within the database. * * <new> * : Replace instances of the first string with this new string. * * [<table>...] * : List of database tables to restrict the replacement to. Wildcards are * supported, e.g. 'wp_*_options' or 'wp_post*'. * * [--dry-run] * : Run the entire search/replace operation and show report, but don't save * changes to the database. * * [--network] * : Search/replace through all the tables registered to $wpdb in a * multisite install. * * [--all-tables-with-prefix] * : Enable replacement on any tables that match the table prefix even if * not registered on $wpdb. * * [--all-tables] * : Enable replacement on ALL tables in the database, regardless of the * prefix, and even if not registered on $wpdb. Overrides --network * and --all-tables-with-prefix. * * [--export[=<file>]] * : Write transformed data as SQL file instead of saving replacements to * the database. If <file> is not supplied, will output to STDOUT. * * [--skip-columns=<columns>] * : Do not perform the replacement on specific columns. Use commas to * specify multiple columns. 'guid' is skipped by default. * * [--precise] * : Force the use of PHP (instead of SQL) which is more thorough, * but slower. * * [--recurse-objects] * : Enable recursing into objects to replace strings. Defaults to true; * pass --no-recurse-objects to disable. * * [--verbose] * : Prints rows to the console as they're updated. * * [--regex] * : Runs the search using a regular expression. Warning: search-replace * will take about 15-20x longer when using --regex. * * ## EXAMPLES * * wp search-replace 'http://example.dev' 'http://example.com' --skip-columns=guid * * wp search-replace 'foo' 'bar' wp_posts wp_postmeta wp_terms --dry-run * * # Turn your production database into a local database * wp search-replace --url=example.com example.com example.dev wp_\*_options * * # Search/replace to a SQL file without transforming the database * wp search-replace foo bar --export=database.sql */ public function __invoke( $args, $assoc_args ) { global $wpdb; $old = array_shift( $args ); $new = array_shift( $args ); $total = 0; $report = array(); $this->dry_run = \WP_CLI\Utils\get_flag_value( $assoc_args, 'dry-run' ); $php_only = \WP_CLI\Utils\get_flag_value( $assoc_args, 'precise' ); $this->recurse_objects = \WP_CLI\Utils\get_flag_value( $assoc_args, 'recurse-objects', true ); $this->verbose = \WP_CLI\Utils\get_flag_value( $assoc_args, 'verbose' ); $this->regex = \WP_CLI\Utils\get_flag_value( $assoc_args, 'regex' ); $this->skip_columns = explode( ',', \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-columns' ) ); if ( $old === $new && ! $this->regex ) { WP_CLI::warning( "Replacement value '{$old}' is identical to search value '{$new}'. Skipping operation." ); exit; } if ( null !== ( $export = \WP_CLI\Utils\get_flag_value( $assoc_args, 'export' ) ) ) { if ( $this->dry_run ) { WP_CLI::error( 'You cannot supply --dry-run and --export at the same time.' ); } if ( true === $export ) { $this->export_handle = STDOUT; $this->verbose = false; } else { $this->export_handle = fopen( $assoc_args['export'], 'w' ); if ( false === $this->export_handle ) { WP_CLI::error( sprintf( 'Unable to open "%s" for writing.', $assoc_args['export'] ) ); } } $php_only = true; } // never mess with hashed passwords $this->skip_columns[] = 'user_pass'; // Get table names based on leftover $args or supplied $assoc_args $tables = \WP_CLI\Utils\wp_get_table_names( $args, $assoc_args ); foreach ( $tables as $table ) { if ( $this->export_handle ) { fwrite( $this->export_handle, "\nDROP TABLE IF EXISTS `$table`;\n" ); $row = $wpdb->get_row( "SHOW CREATE TABLE `$table`", ARRAY_N ); fwrite( $this->export_handle, $row[1] . ";\n" ); list( $table_report, $total_rows ) = $this->php_export_table( $table, $old, $new ); $report = array_merge( $report, $table_report ); $total += $total_rows; // Don't perform replacements on the actual database continue; } list( $primary_keys, $columns, $all_columns ) = self::get_columns( $table ); // since we'll be updating one row at a time, // we need a primary key to identify the row if ( empty( $primary_keys ) ) { $report[] = array( $table, '', 'skipped' ); continue; } foreach ( $columns as $col ) { if ( in_array( $col, $this->skip_columns ) ) { continue; } if ( $this->verbose ) { $this->start_time = microtime( true ); WP_CLI::log( sprintf( 'Checking: %s.%s', $table, $col ) ); } if ( ! $php_only && ! $this->regex ) { $serialRow = $wpdb->get_row( "SELECT * FROM `$table` WHERE `$col` REGEXP '^[aiO]:[1-9]' LIMIT 1" ); } if ( $php_only || $this->regex || NULL !== $serialRow ) { $type = 'PHP'; $count = $this->php_handle_col( $col, $primary_keys, $table, $old, $new ); } else { $type = 'SQL'; $count = $this->sql_handle_col( $col, $table, $old, $new ); } $report[] = array( $table, $col, $count, $type ); $total += $count; } } if ( $this->export_handle && STDOUT !== $this->export_handle ) { fclose( $this->export_handle ); } // Only informational output after this point if ( WP_CLI::get_config( 'quiet' ) || STDOUT === $this->export_handle ) { return; } $table = new \cli\Table(); $table->setHeaders( array( 'Table', 'Column', 'Replacements', 'Type' ) ); $table->setRows( $report ); $table->display(); if ( ! $this->dry_run ) { if ( ! empty( $assoc_args['export'] ) ) { $success_message = "Made {$total} replacements and exported to {$assoc_args['export']}."; } else { $success_message = "Made $total replacements."; if ( $total && 'Default' !== WP_CLI\Utils\wp_get_cache_type() ) { $success_message .= ' Please remember to flush your persistent object cache with `wp cache flush`.'; } } WP_CLI::success( $success_message ); } } private function php_export_table( $table, $old, $new ) { list( $primary_keys, $columns, $all_columns ) = self::get_columns( $table ); $chunk_size = getenv( 'BEHAT_RUN' ) ? 10 : 1000; $args = array( 'table' => $table, 'fields' => $all_columns, 'chunk_size' => $chunk_size ); $replacer = new \WP_CLI\SearchReplacer( $old, $new, $this->recurse_objects, $this->regex ); $col_counts = array_fill_keys( $all_columns, 0 ); if ( $this->verbose ) { $this->start_time = microtime( true ); WP_CLI::log( sprintf( 'Checking: %s', $table ) ); } foreach ( new \WP_CLI\Iterators\Table( $args ) as $i => $row ) { $row_fields = array(); foreach( $all_columns as $col ) { $value = $row->$col; if ( $value && ! in_array( $col, $primary_keys ) && ! in_array( $col, $this->skip_columns ) ) { $new_value = $replacer->run( $value ); if ( $new_value !== $value ) { $col_counts[ $col ]++; $value = $new_value; } } $row_fields[ $col ] = $value; } $this->write_sql_row_fields( $table, $row_fields ); } $table_report = array(); $total_rows = $total_cols = 0; foreach ( $col_counts as $col => $col_count ) { $table_report[] = array( $table, $col, $col_count, 'PHP' ); if ( $col_count ) { $total_cols++; $total_rows += $col_count; } } if ( $this->verbose ) { $time = round( microtime( true ) - $this->start_time, 3 ); WP_CLI::log( sprintf( '%d columns and %d total rows affected using PHP (in %ss)', $total_cols, $total_rows, $time ) ); } return array( $table_report, $total_rows ); } private function sql_handle_col( $col, $table, $old, $new ) { global $wpdb; if ( $this->dry_run ) { $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(`$col`) FROM `$table` WHERE `$col` LIKE %s;", '%' . self::esc_like( $old ) . '%' ) ); } else { $count = $wpdb->query( $wpdb->prepare( "UPDATE `$table` SET `$col` = REPLACE(`$col`, %s, %s);", $old, $new ) ); } if ( $this->verbose ) { $time = round( microtime( true ) - $this->start_time, 3 ); WP_CLI::log( sprintf( '%d rows affected using SQL (in %ss)', $count, $time ) ); } return $count; } private function php_handle_col( $col, $primary_keys, $table, $old, $new ) { global $wpdb; $count = 0; $replacer = new \WP_CLI\SearchReplacer( $old, $new, $this->recurse_objects, $this->regex ); $where = $this->regex ? '' : " WHERE `$col`" . $wpdb->prepare( ' LIKE %s', '%' . self::esc_like( $old ) . '%' ); $primary_keys_sql = esc_sql( implode( ',', $primary_keys ) ); $col_sql = esc_sql( $col ); $table_sql = esc_sql( $table ); $rows = $wpdb->get_results( "SELECT {$primary_keys_sql} FROM {$table_sql}{$where}" ); foreach ( $rows as $keys ) { $where_sql = ''; foreach( (array) $keys as $k => $v ) { $where_sql .= "{$k}={$v}"; } $col_value = $wpdb->get_var( "SELECT {$col_sql} FROM {$table_sql} WHERE {$where_sql}" ); if ( '' === $col_value ) continue; $value = $replacer->run( $col_value ); if ( $value === $col_value ) { continue; } if ( $this->dry_run ) { if ( $value != $col_value ) $count++; } else { $where = array(); foreach( (array) $keys as $k => $v ) { $where[ $k ] = $v; } $count += $wpdb->update( $table, array( $col => $value ), $where ); } } if ( $this->verbose ) { $time = round( microtime( true ) - $this->start_time, 3 ); WP_CLI::log( sprintf( '%d rows affected using PHP (in %ss)', $count, $time ) ); } return $count; } private function write_sql_row_fields( $table, $row_fields ) { global $wpdb; $sql = "INSERT INTO `$table` ("; $sql .= join( ', ', array_map( function ( $field ) { return "`$field`"; }, array_keys( $row_fields ) ) ); $sql .= ') VALUES ('; $sql .= join( ', ', array_fill( 0, count( $row_fields ), '%s' ) ); $sql .= ");\n"; $sql = $wpdb->prepare( $sql, array_values( $row_fields ) ); fwrite( $this->export_handle, $sql ); } private static function get_columns( $table ) { global $wpdb; $primary_keys = $text_columns = $all_columns = array(); foreach ( $wpdb->get_results( "DESCRIBE $table" ) as $col ) { if ( 'PRI' === $col->Key ) { $primary_keys[] = $col->Field; } if ( self::is_text_col( $col->Type ) ) { $text_columns[] = $col->Field; } $all_columns[] = $col->Field; } return array( $primary_keys, $text_columns, $all_columns ); } private static function is_text_col( $type ) { foreach ( array( 'text', 'varchar' ) as $token ) { if ( false !== strpos( $type, $token ) ) return true; } return false; } private static function esc_like( $old ) { global $wpdb; // Remove notices in 4.0 and support backwards compatibility if( method_exists( $wpdb, 'esc_like' ) ) { // 4.0 $old = $wpdb->esc_like( $old ); } else { // 3.9 or less $old = like_escape( esc_sql( $old ) ); } return $old; } } WP_CLI::add_command( 'search-replace', 'Search_Replace_Command' ); <?php class Server_Command extends WP_CLI_Command { /** * Launch PHP's built-in web server for this specific WordPress installation. * * <http://php.net/manual/en/features.commandline.webserver.php> * * ## OPTIONS * * [--host=<host>] * : The hostname to bind the server to. Default: localhost * * [--port=<port>] * : The port number to bind the server to. Default: 8080 * * [--docroot=<path>] * : The path to use as the document root. * * ## EXAMPLES * * # Make the instance available on any address (with port 8080) * wp server --host=0.0.0.0 * * # Run on port 80 (for multisite) * sudo wp server --host=localhost.localdomain --port=80 * * @when before_wp_load */ function __invoke( $_, $assoc_args ) { $min_version = '5.4'; if ( version_compare( PHP_VERSION, $min_version, '<' ) ) { WP_CLI::error( "The `wp server` command requires PHP $min_version or newer." ); } $defaults = array( 'host' => 'localhost', 'port' => 8080, 'docroot' => false ); $assoc_args = array_merge( $defaults, $assoc_args ); $docroot = $assoc_args['docroot']; if ( !$docroot ) { $config_path = WP_CLI::get_runner()->project_config_path; if ( !$config_path ) { $docroot = ABSPATH; } else { $docroot = dirname( $config_path ); } } $cmd = \WP_CLI\Utils\esc_cmd( PHP_BINARY . ' -S %s -t %s %s', $assoc_args['host'] . ':' . $assoc_args['port'], $docroot, \WP_CLI\Utils\extract_from_phar( WP_CLI_ROOT . '/php/router.php' ) ); $descriptors = array( STDIN, STDOUT, STDERR ); exit( proc_close( proc_open( $cmd, $descriptors, $pipes ) ) ); } } WP_CLI::add_command( 'server', 'Server_Command' ); <?php class Shell_Command extends \WP_CLI_Command { /** * Interactive PHP console. * * ## DESCRIPTION * * `wp shell` allows you to evaluate PHP statements and expressions interactively, from within a WordPress environment. This means that you have access to all the functions, classes and globals that you would have access to from inside a WordPress plugin, for example. * * ## OPTIONS * * [--basic] * : Start in fail-safe mode, even if Boris is available. */ public function __invoke( $_, $assoc_args ) { $implementations = array( '\\Psy\\Shell', '\\Boris\\Boris', '\\WP_CLI\\REPL', ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'basic' ) ) { $class = '\\WP_CLI\\REPL'; } else { foreach ( $implementations as $candidate ) { if ( class_exists( $candidate ) ) { $class = $candidate; break; } } } if ( '\\Psy\\Shell' == $class ) { \Psy\Shell::debug(); } else { $repl = new $class( 'wp> ' ); $repl->start(); } } } \WP_CLI::add_command( 'shell', 'Shell_Command' ); <?php /** * Manage sidebars. */ class Sidebar_Command extends WP_CLI_Command { private $fields = array( 'name', 'id', 'description' ); /** * List registered sidebars. * * ## OPTIONS * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each sidebar: * * * name * * id * * description * * These fields are optionally available: * * * class * * before_widget * * after_widget * * before_title * * after_title * * ## EXAMPLES * * wp sidebar list --fields=name,id --format=csv * * @subcommand list */ public function list_( $args, $assoc_args ) { global $wp_registered_sidebars; \WP_CLI\Utils\wp_register_unused_sidebar(); $formatter = new \WP_CLI\Formatter( $assoc_args, $this->fields ); $formatter->display_items( $wp_registered_sidebars ); } } WP_CLI::add_command( 'sidebar', 'Sidebar_Command' ); <?php /** * Perform site-wide operations. * * @package wp-cli */ class Site_Command extends \WP_CLI\CommandWithDBObject { protected $obj_type = 'site'; protected $obj_id_key = 'blog_id'; public function __construct() { $this->fetcher = new \WP_CLI\Fetchers\Site; } /** * Delete comments. */ private function _empty_comments() { global $wpdb; // Empty comments and comment cache $comment_ids = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments" ); foreach ( $comment_ids as $comment_id ) { wp_cache_delete( $comment_id, 'comment' ); wp_cache_delete( $comment_id, 'comment_meta' ); } $wpdb->query( "TRUNCATE $wpdb->comments" ); $wpdb->query( "TRUNCATE $wpdb->commentmeta" ); } /** * Delete all posts. */ private function _empty_posts() { global $wpdb; // Empty posts and post cache $posts_query = "SELECT ID FROM $wpdb->posts"; $posts = new WP_CLI\Iterators\Query( $posts_query, 10000 ); $taxonomies = get_taxonomies(); while ( $posts->valid() ) { $post_id = $posts->current()->ID; wp_cache_delete( $post_id, 'posts' ); wp_cache_delete( $post_id, 'post_meta' ); foreach ( $taxonomies as $taxonomy ) wp_cache_delete( $post_id, "{$taxonomy}_relationships" ); wp_cache_delete( $wpdb->blogid . '-' . $post_id, 'global-posts' ); $posts->next(); } $wpdb->query( "TRUNCATE $wpdb->posts" ); $wpdb->query( "TRUNCATE $wpdb->postmeta" ); } /** * Delete terms, taxonomies, and tax relationships. */ private function _empty_taxonomies() { global $wpdb; // Empty taxonomies and terms $terms = $wpdb->get_results( "SELECT term_id, taxonomy FROM $wpdb->term_taxonomy" ); $ids = array(); $taxonomies = array(); foreach ( (array) $terms as $term ) { $taxonomies[] = $term->taxonomy; $ids[] = $term->term_id; wp_cache_delete( $term->term_id, $term->taxonomy ); } $taxonomies = array_unique( $taxonomies ); $cleaned = array(); foreach ( $taxonomies as $taxonomy ) { if ( isset( $cleaned[$taxonomy] ) ) continue; $cleaned[$taxonomy] = true; wp_cache_delete( 'all_ids', $taxonomy ); wp_cache_delete( 'get', $taxonomy ); delete_option( "{$taxonomy}_children" ); } $wpdb->query( "TRUNCATE $wpdb->terms" ); $wpdb->query( "TRUNCATE $wpdb->term_taxonomy" ); $wpdb->query( "TRUNCATE $wpdb->term_relationships" ); } /** * Insert default terms. */ private function _insert_default_terms() { global $wpdb; // Default category $cat_name = __( 'Uncategorized' ); /* translators: Default category slug */ $cat_slug = sanitize_title( _x( 'Uncategorized', 'Default category slug' ) ); if ( global_terms_enabled() ) { $cat_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM {$wpdb->sitecategories} WHERE category_nicename = %s", $cat_slug ) ); if ( $cat_id == null ) { $wpdb->insert( $wpdb->sitecategories, array('cat_ID' => 0, 'cat_name' => $cat_name, 'category_nicename' => $cat_slug, 'last_updated' => current_time('mysql', true)) ); $cat_id = $wpdb->insert_id; } update_option('default_category', $cat_id); } else { $cat_id = 1; } $wpdb->insert( $wpdb->terms, array('term_id' => $cat_id, 'name' => $cat_name, 'slug' => $cat_slug, 'term_group' => 0) ); $wpdb->insert( $wpdb->term_taxonomy, array('term_id' => $cat_id, 'taxonomy' => 'category', 'description' => '', 'parent' => 0, 'count' => 1)); } /** * Empty a site of its content (posts, comments, and terms). * * ## OPTIONS * * [--uploads] * : Also delete *all* files in the site's in the uploads directory. * * [--yes] * : Proceed to empty the site without a confirmation prompt. * * @subcommand empty */ public function _empty( $args, $assoc_args ) { $upload_message = ''; if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'uploads' ) ) { $upload_message = ', and delete its uploads directory'; } WP_CLI::confirm( 'Are you sure you want to empty the site at ' . site_url() . ' of all posts, comments, and terms' . $upload_message . '?', $assoc_args ); $this->_empty_posts(); $this->_empty_comments(); $this->_empty_taxonomies(); $this->_insert_default_terms(); if ( ! empty( $upload_message ) ) { $upload_dir = wp_upload_dir(); $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $upload_dir['basedir'], RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $files as $fileinfo ) { $realpath = $fileinfo->getRealPath(); // Don't clobber subsites when operating on the main site if ( is_main_site() && false !== stripos( $realpath, '/sites/' ) ) { continue; } $todo = $fileinfo->isDir() ? 'rmdir' : 'unlink'; $todo( $realpath ); } rmdir( $upload_dir['basedir'] ); } WP_CLI::success( 'The site at ' . site_url() . ' was emptied.' ); } /** * Delete a site in a multisite install. * * ## OPTIONS * * [<site-id>] * : The id of the site to delete. If not provided, you must set the --slug parameter. * * [--slug=<slug>] * : Path of the blog to be deleted. Subdomain on subdomain installs, directory on subdirectory installs. * * [--yes] * : Answer yes to the confirmation message. * * [--keep-tables] * : Delete the blog from the list, but don't drop it's tables. */ function delete( $args, $assoc_args ) { if ( !is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } if ( isset( $assoc_args['slug'] ) ) { $blog = get_blog_details( trim( $assoc_args['slug'], '/' ) ); } else { if ( empty( $args ) ) { WP_CLI::error( "Need to specify a blog id." ); } $blog_id = $args[0]; $blog = get_blog_details( $blog_id ); } if ( !$blog ) { WP_CLI::error( "Site not found." ); } WP_CLI::confirm( "Are you sure you want to delete the $blog->siteurl site?", $assoc_args ); wpmu_delete_blog( $blog->blog_id, ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'keep-tables' ) ); WP_CLI::success( "The site at $blog->siteurl was deleted." ); } /** * Create a site in a multisite install. * * ## OPTIONS * * --slug=<slug> * : Path for the new site. Subdomain on subdomain installs, directory on subdirectory installs. * * [--title=<title>] * : Title of the new site. Default: prettified slug. * * [--email=<email>] * : Email for Admin user. User will be created if none exists. Assignement to Super Admin if not included. * * [--network_id=<network-id>] * : Network to associate new site with. Defaults to current network (typically 1). * * [--private] * : If set, the new site will be non-public (not indexed) * * [--porcelain] * : If set, only the site id will be output on success. */ public function create( $_, $assoc_args ) { if ( !is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } global $wpdb, $current_site; $base = $assoc_args['slug']; $title = \WP_CLI\Utils\get_flag_value( $assoc_args, 'title', ucfirst( $base ) ); $email = empty( $assoc_args['email'] ) ? '' : $assoc_args['email']; // Network if ( !empty( $assoc_args['network_id'] ) ) { $network = $this->_get_network( $assoc_args['network_id'] ); if ( $network === false ) { WP_CLI::error( sprintf( 'Network with id %d does not exist.', $assoc_args['network_id'] ) ); } } else { $network = $current_site; } $public = ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'private' ); // Sanitize if ( preg_match( '|^([a-zA-Z0-9-])+$|', $base ) ) { $base = strtolower( $base ); } // If not a subdomain install, make sure the domain isn't a reserved word if ( !is_subdomain_install() ) { $subdirectory_reserved_names = apply_filters( 'subdirectory_reserved_names', array( 'page', 'comments', 'blog', 'files', 'feed' ) ); if ( in_array( $base, $subdirectory_reserved_names ) ) { WP_CLI::error( 'The following words are reserved and cannot be used as blog names: ' . implode( ', ', $subdirectory_reserved_names ) ); } } // Check for valid email, if not, use the first Super Admin found // Probably a more efficient way to do this so we dont query for the // User twice if super admin $email = sanitize_email( $email ); if ( empty( $email ) || !is_email( $email ) ) { $super_admins = get_super_admins(); $email = ''; if ( !empty( $super_admins ) && is_array( $super_admins ) ) { // Just get the first one $super_login = $super_admins[0]; $super_user = get_user_by( 'login', $super_login ); if ( $super_user ) { $email = $super_user->user_email; } } } if ( is_subdomain_install() ) { $path = '/'; $url = $newdomain = $base.'.'.preg_replace( '|^www\.|', '', $network->domain ); } else { $newdomain = $network->domain; $path = '/' . trim( $base, '/' ) . '/'; $url = $network->domain . $path; } $user_id = email_exists( $email ); if ( !$user_id ) { // Create a new user with a random password $password = wp_generate_password( 12, false ); $user_id = wpmu_create_user( $base, $password, $email ); if ( false == $user_id ) { WP_CLI::error( "Can't create user." ); } else { wp_new_user_notification( $user_id, $password ); } } $wpdb->hide_errors(); $id = wpmu_create_blog( $newdomain, $path, $title, $user_id, array( 'public' => $public ), $network->id ); $wpdb->show_errors(); if ( !is_wp_error( $id ) ) { if ( !is_super_admin( $user_id ) && !get_user_option( 'primary_blog', $user_id ) ) { update_user_option( $user_id, 'primary_blog', $id, true ); } // Prevent mailing admins of new sites // @TODO argument to pass in? // $content_mail = sprintf(__( "New site created by WP Command Line Interface\n\nAddress: %2s\nName: %3s"), get_site_url($id), stripslashes($title)); // wp_mail(get_site_option('admin_email'), sprintf(__('[%s] New Site Created'), $current_site->site_name), $content_mail, 'From: "Site Admin" <'.get_site_option( 'admin_email').'>'); } else { WP_CLI::error( $id->get_error_message() ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) WP_CLI::line( $id ); else WP_CLI::success( "Site $id created: $url" ); } /** * Get network data for a given id. * * @param int $network_id * @return bool|array False if no network found with given id, array otherwise */ private function _get_network( $network_id ) { global $wpdb; // Load network data $networks = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->site WHERE id = %d", $network_id ) ); if ( !empty( $networks ) ) { // Only care about domain and path which are set here return $networks[0]; } return false; } /** * List all sites in a multisite install. * * ## OPTIONS * * [--network=<id>] * : The network to which the sites belong. * * [--<field>=<value>] * : Filter by one or more fields. * * [--field=<field>] * : Prints the value of a single field for each site. * * [--fields=<fields>] * : Comma-separated list of fields to show. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each site: * * * blog_id * * url * * last_updated * * registered * * These fields are optionally available: * * * site_id * * domain * * path * * public * * archived * * mature * * spam * * deleted * * lang_id * * ## EXAMPLES * * # Output a simple list of site URLs * wp site list --field=url * * @subcommand list */ public function list_( $_, $assoc_args ) { if ( !is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } global $wpdb; if ( isset( $assoc_args['fields'] ) ) { $assoc_args['fields'] = preg_split( '/,[ \t]*/', $assoc_args['fields'] ); } $defaults = array( 'format' => 'table', 'fields' => array( 'blog_id', 'url', 'last_updated', 'registered' ), ); $assoc_args = array_merge( $defaults, $assoc_args ); $where = array(); $append = ''; $site_cols = array( 'blog_id', 'url', 'last_updated', 'registered', 'site_id', 'domain', 'path', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ); foreach( $site_cols as $col ) { if ( isset( $assoc_args[ $col ] ) ) { $where[ $col ] = $assoc_args[ $col ]; } } if ( isset( $assoc_args['site__in'] ) ) { $where['blog_id'] = explode( ',', $assoc_args['site__in'] ); $append = "ORDER BY FIELD( blog_id, " . implode( ',', array_map( 'intval', $where['blog_id'] ) ) . " )"; } if ( isset( $assoc_args['network'] ) ) { $where['site_id'] = $assoc_args['network']; } $iterator_args = array( 'table' => $wpdb->blogs, 'where' => $where, 'append' => $append, ); $it = new \WP_CLI\Iterators\Table( $iterator_args ); $it = \WP_CLI\Utils\iterator_map( $it, function( $blog ) { $blog->url = trailingslashit( get_site_url( $blog->blog_id ) ); return $blog; } ); $formatter = new \WP_CLI\Formatter( $assoc_args, null, 'site' ); $formatter->display_items( $it ); } /** * Archive one or more sites * * ## OPTIONS * * <id>... * : One or more IDs of sites to archive. * * ## EXAMPLES * * wp site archive 123 */ public function archive( $args ) { $this->update_site_status( $args, 'archived', 1 ); } /** * Unarchive one or more sites * * ## OPTIONS * * <id>... * : One or more IDs of sites to unarchive. * * ## EXAMPLES * * wp site unarchive 123 */ public function unarchive( $args ) { $this->update_site_status( $args, 'archived', 0 ); } /** * Activate one or more sites * * ## OPTIONS * * <id>... * : One or more IDs of sites to activate. * * ## EXAMPLES * * wp site activate 123 */ public function activate( $args ) { $this->update_site_status( $args, 'deleted', 0 ); } /** * Deactivate one or more sites * * ## OPTIONS * * <id>... * : One or more IDs of sites to deactivate. * * ## EXAMPLES * * wp site deactivate 123 */ public function deactivate( $args ) { $this->update_site_status( $args, 'deleted', 1 ); } /** * Mark one or more sites as spam * * ## OPTIONS * * <id>... * : One or more IDs of sites to be marked as spam. * * ## EXAMPLES * * wp site spam 123 */ public function spam( $args ) { $this->update_site_status( $args, 'spam', 1 ); } /** * Remove one or more sites from spam * * ## OPTIONS * * <id>... * : One or more IDs of sites to remove from spam. * * ## EXAMPLES * * wp site unspam 123 * * @subcommand unspam */ public function unspam( $args ) { $this->update_site_status( $args, 'spam', 0 ); } private function update_site_status( $ids, $pref, $value ) { if ( $pref == 'archived' && $value == 1 ) { $action = 'archived'; } else if ( $pref == 'archived' && $value == 0) { $action = 'unarchived'; } else if ( $pref == 'deleted' && $value == 1 ) { $action = 'deactivated'; } else if ( $pref == 'deleted' && $value == 0 ) { $action = 'activated'; } else if ( $pref == 'spam' && $value == 1 ) { $action = 'marked as spam'; } else if ( $pref == 'spam' && $value == 0 ) { $action = 'removed from spam'; } foreach ( $ids as $site_id ) { $site = $this->fetcher->get_check( $site_id ); if ( is_main_site( $site->blog_id ) ) { WP_CLI::warning( "You are not allowed to change the main site." ); continue; } $old_value = get_blog_status( $site->blog_id, $pref ); if ( $value == $old_value ) { WP_CLI::warning( "Site {$site->blog_id} already {$action}." ); continue; } update_blog_status( $site->blog_id, $pref, $value ); WP_CLI::success( "Site {$site->blog_id} {$action}." ); } } } WP_CLI::add_command( 'site', 'Site_Command' ); <?php /** * List, add, and remove super admins from a network. */ class Super_Admin_Command extends WP_CLI_Command { public function __construct() { $this->fetcher = new \WP_CLI\Fetchers\User; } /** * Show a list of users with super-admin capabilities. * * @subcommand list */ public function _list( $_, $assoc_args ) { $super_admins = self::get_admins(); foreach ( $super_admins as $user_login ) { WP_CLI::line( $user_login ); } } /** * Grant super-admin privileges to one or more users. * * <user>... * : One or more user IDs, user emails, or user logins. */ public function add( $args, $_ ) { $users = $this->fetcher->get_many( $args ); $user_logins = wp_list_pluck( $users, 'user_login' ); $super_admins = self::get_admins(); $num_super_admins = count( $super_admins ); foreach ( $user_logins as $user_login ) { $user = get_user_by( 'login', $user_login ); if ( !$user ) { WP_CLI::warning( "Couldn't find {$user_login} user." ); continue; } if ( in_array( $user->user_login, $super_admins ) ) { WP_CLI::warning( "User {$user_login} already has super-admin capabilities." ); continue; } $super_admins[] = $user->user_login; } if ( $num_super_admins === count( $super_admins ) ) { WP_CLI::log( 'No changes.' ); } else { if ( update_site_option( 'site_admins' , $super_admins ) ) { WP_CLI::success( 'Granted super-admin capabilities.' ); } else { WP_CLI::error( 'Site options update failed!' ); } } } /** * Revoke super-admin privileges to one or more users. * * <user>... * : One or more user IDs, user emails, or user logins. */ public function remove( $args, $_ ) { $users = $this->fetcher->get_many( $args ); $user_logins = wp_list_pluck( $users, 'user_login' ); $super_admins = self::get_admins(); $super_admins = array_diff( $super_admins, $user_logins ); update_site_option( 'site_admins' , $super_admins ); WP_CLI::success( 'Revoked super-admin capabilities.' ); } private static function get_admins() { // We don't use get_super_admins() because we don't want to mess with the global return get_site_option( 'site_admins', array('admin') ); } } WP_CLI::add_command( 'super-admin', 'Super_Admin_Command', array( 'before_invoke' => function () { if ( !is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } } ) ); <?php /** * Manage taxonomies. * * @package wp-cli */ class Taxonomy_Command extends WP_CLI_Command { private $fields = array( 'name', 'label', 'description', 'object_type', 'show_tagcloud', 'hierarchical', 'public', ); public function __construct() { if ( \WP_CLI\Utils\wp_version_compare( 3.7, '<' ) ) { // remove description for wp <= 3.7 $this->fields = array_values( array_diff( $this->fields, array( 'description' ) ) ); } parent::__construct(); } /** * List taxonomies. * * ## OPTIONS * * [--<field>=<value>] * : Filter by one or more fields (see get_taxonomies() first parameter for a list of available fields). * * [--field=<field>] * : Prints the value of a single field for each taxonomy. * * [--fields=<fields>] * : Limit the output to specific taxonomy fields. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each term: * * * name * * label * * description * * public * * hierarchical * * There are no optionally available fields. * * ## EXAMPLES * * wp taxonomy list --format=csv * * wp taxonomy list --object-type=post --fields=name,public * * @subcommand list */ public function list_( $args, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); if ( isset( $assoc_args['object_type'] ) ) { $assoc_args['object_type'] = array( $assoc_args['object_type'] ); } $taxonomies = get_taxonomies( $assoc_args, 'objects' ); $taxonomies = array_map( function( $taxonomy ) { $taxonomy->object_type = implode( ', ', $taxonomy->object_type ); return $taxonomy; }, $taxonomies ); $formatter->display_items( $taxonomies ); } /** * Get a taxonomy * * ## OPTIONS * * <taxonomy> * : Taxonomy slug * * [--field=<field>] * : Instead of returning the whole taxonomy, returns the value of a single field. * * [--fields=<fields>] * : Limit the output to specific fields. Defaults to all fields. * * [--format=<format>] * : Accepted values: table, json, csv. Default: table * * ## EXAMPLES * * wp taxonomy get post_tag --format=json */ public function get( $args, $assoc_args ) { $taxonomy = get_taxonomy( $args[0] ); if ( ! $taxonomy ) { WP_CLI::error( "Taxonomy {$args[0]} doesn't exist." ); } if ( empty( $assoc_args['fields'] ) ) { $default_fields = array_merge( $this->fields, array( 'labels', 'cap' ) ); $assoc_args['fields'] = $default_fields; } $data = array( 'name' => $taxonomy->name, 'label' => $taxonomy->label, 'description' => $taxonomy->description, 'object_type' => $taxonomy->object_type, 'show_tagcloud' => $taxonomy->show_tagcloud, 'hierarchical' => $taxonomy->hierarchical, 'public' => $taxonomy->public, 'labels' => $taxonomy->labels, 'cap' => $taxonomy->cap, ); $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $data ); } private function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->fields, 'taxonomy' ); } } WP_CLI::add_command( 'taxonomy', 'Taxonomy_Command' ); <?php /** * Manage terms. * * @package wp-cli */ class Term_Command extends WP_CLI_Command { private $fields = array( 'term_id', 'term_taxonomy_id', 'name', 'slug', 'description', 'parent', 'count', ); /** * List terms in a taxonomy. * * ## OPTIONS * * <taxonomy>... * : List terms of one or more taxonomies * * [--<field>=<value>] * : Filter by one or more fields (see get_terms() $args parameter for a list of fields). * * [--field=<field>] * : Prints the value of a single field for each term. * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each term: * * * term_id * * term_taxonomy_id * * name * * slug * * description * * parent * * count * * There are no optionally available fields. * * ## EXAMPLES * * wp term list category --format=csv * * wp term list post_tag --fields=name,slug * * @subcommand list */ public function list_( $args, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); $defaults = array( 'hide_empty' => false, ); $assoc_args = array_merge( $defaults, $assoc_args ); if ( ! empty( $assoc_args['term_id'] ) ) { $term = get_term_by( 'id', $assoc_args['term_id'], $args[0] ); $terms = array( $term ); } else { $terms = get_terms( $args, $assoc_args ); } $terms = array_map( function( $term ){ $term->count = (int)$term->count; $term->parent = (int)$term->parent; return $term; }, $terms ); if ( 'ids' == $formatter->format ) { $terms = wp_list_pluck( $terms, 'term_id' ); echo implode( ' ', $terms ); } else { $formatter->display_items( $terms ); } } /** * Create a term. * * ## OPTIONS * * <taxonomy> * : Taxonomy for the new term. * * <term> * : A name for the new term. * * [--slug=<slug>] * : A unique slug for the new term. Defaults to sanitized version of name. * * [--description=<description>] * : A description for the new term. * * [--parent=<term-id>] * : A parent for the new term. * * [--porcelain] * : Output just the new term id. * * ## EXAMPLES * * wp term create category Apple --description="A type of fruit" */ public function create( $args, $assoc_args ) { list( $taxonomy, $term ) = $args; $defaults = array( 'slug' => sanitize_title( $term ), 'description' => '', 'parent' => 0, ); $assoc_args = wp_parse_args( $assoc_args, $defaults ); $porcelain = \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ); unset( $assoc_args['porcelain'] ); // Compatibility for < WP 4.0 if ( $assoc_args['parent'] > 0 && ! term_exists( (int) $assoc_args['parent'] ) ) { WP_CLI::error( 'Parent term does not exist.' ); } $ret = wp_insert_term( $term, $taxonomy, $assoc_args ); if ( is_wp_error( $ret ) ) { WP_CLI::error( $ret->get_error_message() ); } else { if ( $porcelain ) WP_CLI::line( $ret['term_id'] ); else WP_CLI::success( sprintf( "Created %s %d.", $taxonomy, $ret['term_id'] ) ); } } /** * Get a taxonomy term * * ## OPTIONS * * <taxonomy> * : Taxonomy of the term to get * * <term-id> * : ID of the term to get * * [--field=<field>] * : Instead of returning the whole term, returns the value of a single field. * * [--fields=<fields>] * : Limit the output to specific fields. Defaults to all fields. * * [--format=<format>] * : Accepted values: table, json, csv. Default: table * * ## EXAMPLES * * wp term get category 1 --format=json */ public function get( $args, $assoc_args ) { list( $taxonomy, $term_id ) = $args; $term = get_term_by( 'id', $term_id, $taxonomy ); if ( ! $term ) { WP_CLI::error( "Term doesn't exist." ); } if ( empty( $assoc_args['fields'] ) ) { $term_array = get_object_vars( $term ); $assoc_args['fields'] = array_keys( $term_array ); } $term->count = (int) $term->count; $term->parent = (int) $term->parent; $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $term ); } /** * Update a term. * * ## OPTIONS * * <taxonomy> * : Taxonomy of the term to update. * * <term-id> * : ID for the term to update. * * [--name=<name>] * : A new name for the term. * * [--slug=<slug>] * : A new slug for the term. * * [--description=<description>] * : A new description for the term. * * [--parent=<term-id>] * : A new parent for the term. * * ## EXAMPLES * * wp term update category 15 --name=Apple */ public function update( $args, $assoc_args ) { list( $taxonomy, $term_id ) = $args; $defaults = array( 'name' => null, 'slug' => null, 'description' => null, 'parent' => null, ); $assoc_args = wp_parse_args( $assoc_args, $defaults ); foreach( $assoc_args as $key => $value ) { if ( is_null( $value ) ) unset( $assoc_args[$key] ); } $ret = wp_update_term( $term_id, $taxonomy, $assoc_args ); if ( is_wp_error( $ret ) ) WP_CLI::error( $ret->get_error_message() ); else WP_CLI::success( "Term updated." ); } /** * Delete a term. * * ## OPTIONS * * <taxonomy> * : Taxonomy of the term to delete. * * <term-id>... * : One or more IDs of terms to delete. * * ## EXAMPLES * * # delete all post tags * wp term list post_tag --field=term_id | xargs wp term delete post_tag */ public function delete( $args ) { $taxonomy = array_shift( $args ); foreach ( $args as $term_id ) { $ret = wp_delete_term( $term_id, $taxonomy ); if ( is_wp_error( $ret ) ) { WP_CLI::warning( $ret ); } else if ( $ret ) { WP_CLI::success( sprintf( "Deleted %s %d.", $taxonomy, $term_id ) ); } else { WP_CLI::warning( sprintf( "%s %d doesn't exist.", $taxonomy, $term_id ) ); } } } /** * Generate some terms. * * ## OPTIONS * * <taxonomy> * : The taxonomy for the generated terms. * * [--count=<number>] * : How many terms to generate. Default: 100 * * [--max_depth=<number>] * : Generate child terms down to a certain depth. Default: 1 * * ## EXAMPLES * * wp term generate --count=10 */ public function generate( $args, $assoc_args ) { global $wpdb; list ( $taxonomy ) = $args; $defaults = array( 'count' => 100, 'max_depth' => 1, ); extract( array_merge( $defaults, $assoc_args ), EXTR_SKIP ); if ( !taxonomy_exists( $taxonomy ) ) { WP_CLI::error( sprintf( "'%s' is not a registered taxonomy.", $taxonomy ) ); } $label = get_taxonomy( $taxonomy )->labels->singular_name; $slug = sanitize_title_with_dashes( $label ); $hierarchical = get_taxonomy( $taxonomy )->hierarchical; $notify = \WP_CLI\Utils\make_progress_bar( 'Generating terms', $count ); $previous_term_id = 0; $current_parent = 0; $current_depth = 1; $max_id = (int) $wpdb->get_var( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy ORDER BY term_taxonomy_id DESC LIMIT 1" ); $suspend_cache_invalidation = wp_suspend_cache_invalidation( true ); $created = array(); for ( $i = $max_id + 1; $i <= $max_id + $count; $i++ ) { if ( $hierarchical ) { if ( $previous_term_id && $this->maybe_make_child() && $current_depth < $max_depth ) { $current_parent = $previous_term_id; $current_depth++; } else if ( $this->maybe_reset_depth() ) { $current_parent = 0; $current_depth = 1; } } $args = array( 'parent' => $current_parent, 'slug' => $slug . "-$i", ); $name = "$label $i"; $term = wp_insert_term( $name, $taxonomy, $args ); if ( is_wp_error( $term ) ) { WP_CLI::warning( $term ); } else { $created[] = $term['term_id']; $previous_term_id = $term['term_id']; } $notify->tick(); } wp_suspend_cache_invalidation( $suspend_cache_invalidation ); clean_term_cache( $created, $taxonomy ); $notify->finish(); } /** * Get term url * * ## OPTIONS * * <taxonomy> * : Taxonomy of the term(s) to get. * * <term-id>... * : One or more IDs of terms to get the URL. * * ## EXAMPLES * * wp term url post_tag 123 * * wp term url post_tag 123 324 */ public function url( $args ) { $term_link = get_term_link( (int)$args[1], $args[0] ); if ( $term_link && ! is_wp_error( $term_link ) ) { WP_CLI::line( $term_link ); } else { WP_CLI::error( "Invalid term." ); } } private function maybe_make_child() { // 50% chance of making child term return ( mt_rand(1, 2) == 1 ); } private function maybe_reset_depth() { // 10% chance of reseting to root depth return ( mt_rand(1, 10) == 7 ); } private function get_formatter( &$assoc_args ) { return new \WP_CLI\Formatter( $assoc_args, $this->fields, 'term' ); } } WP_CLI::add_command( 'term', 'Term_Command' ); <?php /** * Manage themes. * * @package wp-cli */ class Theme_Command extends \WP_CLI\CommandWithUpgrade { protected $item_type = 'theme'; protected $upgrade_refresh = 'wp_update_themes'; protected $upgrade_transient = 'update_themes'; protected $obj_fields = array( 'name', 'status', 'update', 'version' ); function __construct() { if ( is_multisite() ) { $this->obj_fields[] = 'enabled'; } parent::__construct(); $this->fetcher = new \WP_CLI\Fetchers\Theme; } protected function get_upgrader_class( $force ) { return $force ? '\\WP_CLI\\DestructiveThemeUpgrader' : 'Theme_Upgrader'; } /** * See the status of one or all themes. * * ## OPTIONS * * [<theme>] * : A particular theme to show the status for. */ function status( $args ) { parent::status( $args ); } /** * Search the wordpress.org theme repository. * * ## OPTIONS * * <search> * : The string to search for. * * [--per-page=<per-page>] * : Optional number of results to display. Defaults to 10. * * [--field=<field>] * : Prints the value of a single field for each theme. * * [--fields=<fields>] * : Ask for specific fields from the API. Defaults to name,slug,author,rating. Acceptable values: * * **name**: Theme Name * **slug**: Theme Slug * **version**: Current Version Number * **author**: Theme Author * **preview_url**: Theme Preview URL * **screenshot_url**: Theme Screenshot URL * **rating**: Theme Rating * **num_ratings**: Number of Theme Ratings * **homepage**: Theme Author's Homepage * **description**: Theme Description * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## EXAMPLES * * wp theme search automattic --per-page=20 * * wp theme search automattic --fields=name,version,slug,rating,num_ratings,description */ public function search( $args, $assoc_args ) { parent::_search( $args, $assoc_args ); } protected function status_single( $args ) { $theme = $this->fetcher->get_check( $args[0] ); $status = $this->format_status( $this->get_status( $theme ), 'long' ); $version = $theme->get('Version'); if ( $this->has_update( $theme->get_stylesheet() ) ) $version .= ' (%gUpdate available%n)'; echo WP_CLI::colorize( \WP_CLI\Utils\mustache_render( 'theme-status.mustache', array( 'slug' => $theme->get_stylesheet(), 'status' => $status, 'version' => $version, 'name' => $theme->get('Name'), 'author' => $theme->get('Author'), ) ) ); } protected function get_all_items() { return $this->get_item_list(); } protected function get_status( $theme ) { if ( $this->is_active_theme( $theme ) ) { return 'active'; } else if ( $theme->get_stylesheet_directory() === get_template_directory() ) { return 'parent'; } else { return 'inactive'; } } /** * Activate a theme. * * ## OPTIONS * * <theme> * : The theme to activate. */ public function activate( $args = array() ) { $theme = $this->fetcher->get_check( $args[0] ); $name = $theme->get('Name'); if ( 'active' === $this->get_status( $theme ) ) { WP_CLI::success( "The '$name' theme is already active." ); exit; } if ( $theme->get_stylesheet() != $theme->get_template() && ! $this->fetcher->get( $theme->get_template() ) ) { WP_CLI::error( "The '{$theme->get_stylesheet()}' theme cannot be activated without its parent, '{$theme->get_template()}'." ); } switch_theme( $theme->get_template(), $theme->get_stylesheet() ); if ( $this->is_active_theme( $theme ) ) { WP_CLI::success( "Switched to '$name' theme." ); } else { WP_CLI::error( "Could not switch to '$name' theme." ); } } /** * Enable a theme in a multisite install. * * ## OPTIONS * * <theme> * : The theme to enable. * * [--network] * : If set, the theme is enabled for the entire network * * [--activate] * : If set, the theme is activated for the current site. Note that * the "network" flag has no influence on this. * * ## EXAMPLES * * wp theme enable twentythirteen * * wp theme enable twentythirteen --network * * wp theme enable twentythirteen --activate */ public function enable( $args, $assoc_args ) { if ( ! is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } $theme = $this->fetcher->get_check( $args[0] ); $name = $theme->get( 'Name' ); # If the --network flag is set, we'll be calling the (get|update)_site_option functions $_site = ! empty( $assoc_args['network'] ) ? '_site' : ''; # Add the current theme to the allowed themes option or site option $allowed_themes = call_user_func( "get{$_site}_option", 'allowedthemes' ); if ( empty( $allowed_themes ) ) $allowed_themes = array(); $allowed_themes[ $theme->get_stylesheet() ] = true; call_user_func( "update{$_site}_option", 'allowedthemes', $allowed_themes ); if ( ! empty( $assoc_args['network'] ) ) WP_CLI::success( "Network enabled the '$name' theme." ); else WP_CLI::success( "Enabled the '$name' theme." ); # If the --activate flag is set, activate the theme for the current site if ( ! empty( $assoc_args['activate'] ) ) { $this->activate( $args ); } } /** * Disable a theme in a multisite install. * * ## OPTIONS * * <theme> * : The theme to disable. * * [--network] * : If set, the theme is disabled on the network level. Note that * individual sites may still have this theme enabled if it was * enabled for them independently. * * ## EXAMPLES * * wp theme disable twentythirteen * * wp theme disable twentythirteen --network */ public function disable( $args, $assoc_args ) { if ( ! is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } $theme = $this->fetcher->get_check( $args[0] ); $name = $theme->get( 'Name' ); # If the --network flag is set, we'll be calling the (get|update)_site_option functions $_site = ! empty( $assoc_args['network'] ) ? '_site' : ''; # Add the current theme to the allowed themes option or site option $allowed_themes = call_user_func( "get{$_site}_option", 'allowedthemes' ); if ( ! empty( $allowed_themes[ $theme->get_stylesheet() ] ) ) unset( $allowed_themes[ $theme->get_stylesheet() ] ); call_user_func( "update{$_site}_option", 'allowedthemes', $allowed_themes ); if ( ! empty( $assoc_args['network'] ) ) WP_CLI::success( "Network disabled the '$name' theme." ); else WP_CLI::success( "Disabled the '$name' theme." ); } private function is_active_theme( $theme ) { return $theme->get_stylesheet_directory() == get_stylesheet_directory(); } /** * Get the path to a theme or to the theme directory. * * ## OPTIONS * * [<theme>] * : The theme to get the path to. Path includes "style.css" file. * If not set, will return the path to the themes directory. * * [--dir] * : If set, get the path to the closest parent directory, instead of the * theme's "style.css" file. * * ## EXAMPLES * * cd $(wp theme path) */ public function path( $args, $assoc_args ) { if ( empty( $args ) ) { $path = WP_CONTENT_DIR . '/themes'; } else { $theme = $this->fetcher->get_check( $args[0] ); $path = $theme->get_stylesheet_directory(); if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'dir' ) ) $path .= '/style.css'; } WP_CLI::line( $path ); } protected function install_from_repo( $slug, $assoc_args ) { $api = themes_api( 'theme_information', array( 'slug' => $slug ) ); if ( is_wp_error( $api ) ) { return $api; } if ( isset( $assoc_args['version'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); } if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'force' ) && wp_get_theme( $slug )->exists() ) { // We know this will fail, so avoid a needless download of the package. return new WP_Error( 'already_installed', 'Theme already installed.' ); } WP_CLI::log( sprintf( 'Installing %s (%s)', html_entity_decode( $api->name, ENT_QUOTES ), $api->version ) ); if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'version' ) != 'dev' ) { WP_CLI::get_http_cache_manager()->whitelist_package( $api->download_link, $this->item_type, $api->slug, $api->version ); } $result = $this->get_upgrader( $assoc_args )->install( $api->download_link ); return $result; } protected function get_item_list() { $items = array(); if ( is_multisite() ) { $site_enabled = get_option( 'allowedthemes' ); if ( empty( $site_enabled ) ) $site_enabled = array(); $network_enabled = get_site_option( 'allowedthemes' ); if ( empty( $network_enabled ) ) $network_enabled = array(); } foreach ( wp_get_themes() as $key => $theme ) { $file = $theme->get_stylesheet_directory(); $update_info = $this->get_update_info( $theme->get_stylesheet() ); $items[ $file ] = array( 'name' => $key, 'status' => $this->get_status( $theme ), 'update' => (bool) $update_info, 'update_version' => $update_info['new_version'], 'update_package' => $update_info['package'], 'version' => $theme->get('Version'), 'update_id' => $theme->get_stylesheet(), 'title' => $theme->get('Name'), 'description' => $theme->get('Description'), ); if ( is_multisite() ) { if ( ! empty( $site_enabled[ $key ] ) && ! empty( $network_enabled[ $key ] ) ) $items[ $file ]['enabled'] = 'network,site'; elseif ( ! empty( $network_enabled[ $key ] ) ) $items[ $file ]['enabled'] = 'network'; elseif ( ! empty( $site_enabled[ $key ] ) ) $items[ $file ]['enabled'] = 'site'; else $items[ $file ]['enabled'] = 'no'; } } return $items; } protected function filter_item_list( $items, $args ) { $theme_files = array(); foreach ( $args as $arg ) { $theme_files[] = $this->fetcher->get_check( $arg )->get_stylesheet_directory(); } return \WP_CLI\Utils\pick_fields( $items, $theme_files ); } /** * Install a theme. * * ## OPTIONS * * <theme|zip|url>... * : A theme slug, the path to a local zip file, or URL to a remote zip file. * * [--version=<version>] * : If set, get that particular version from wordpress.org, instead of the * stable version. * * [--force] * : If set, the command will overwrite any installed version of the theme, without prompting * for confirmation. * * [--activate] * : If set, the theme will be activated immediately after install. * * ## EXAMPLES * * # Install the latest version from wordpress.org and activate * wp theme install twentytwelve --activate * * # Install from a local zip file * wp theme install ../my-theme.zip * * # Install from a remote zip file * wp theme install http://s3.amazonaws.com/bucketname/my-theme.zip?AWSAccessKeyId=123&Expires=456&Signature=abcdef */ function install( $args, $assoc_args ) { $theme_root = get_theme_root(); if ( $theme_root && ! is_dir( $theme_root ) ) { wp_mkdir_p( $theme_root ); register_theme_directory( $theme_root ); } parent::install( $args, $assoc_args ); } /** * Get a theme * * ## OPTIONS * * <theme> * : The theme to get. * * [--field=<field>] * : Instead of returning the whole theme, returns the value of a single field. * * [--fields=<fields>] * : Limit the output to specific fields. Defaults to all fields. * * [--format=<format>] * : Accepted values: table, json, csv. Default: table * * ## EXAMPLES * * wp theme get twentytwelve --format=json */ public function get( $args, $assoc_args ) { $theme = $this->fetcher->get_check( $args[0] ); // WP_Theme object employs magic getter, unfortunately $theme_vars = array( 'name', 'title', 'version', 'parent_theme', 'template_dir', 'stylesheet_dir', 'template', 'stylesheet', 'screenshot', 'description', 'author', 'tags', 'theme_root', 'theme_root_uri', ); $theme_obj = new stdClass; foreach ( $theme_vars as $var ) { $theme_obj->$var = $theme->$var; } $theme_obj->description = wordwrap( $theme_obj->description ); if ( empty( $assoc_args['fields'] ) ) { $assoc_args['fields'] = $theme_vars; } $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $theme_obj ); } /** * Update one or more themes. * * ## OPTIONS * * [<theme>...] * : One or more themes to update. * * [--all] * : If set, all themes that have updates will be updated. * * [--format=<format>] * : Output summary as table or summary. Defaults to table. * * [--version=<version>] * : If set, the theme will be updated to the specified version. * * [--dry-run] * : Preview which themes would be updated. * * ## EXAMPLES * * wp theme update twentyeleven twentytwelve * * wp theme update --all * * @alias upgrade */ function update( $args, $assoc_args ) { if ( isset( $assoc_args['version'] ) ) { foreach ( $this->fetcher->get_many( $args ) as $theme ) { $r = delete_theme( $theme->stylesheet ); if ( is_wp_error( $r ) ) { WP_CLI::warning( $r ); } else { $assoc_args['force'] = true; $this->install( array( $theme->stylesheet ), $assoc_args ); } } } else { parent::update_many( $args, $assoc_args ); } } /** * Check if the theme is installed. * * ## OPTIONS * * <theme> * : The theme to check. * * ## EXAMPLES * * wp theme is-installed twentytwelve * * @subcommand is-installed */ function is_installed( $args, $assoc_args = array() ) { $theme = wp_get_theme( $args[0] ); if ( $theme->exists() ) { exit( 0 ); } else { exit( 1 ); } } /** * Delete a theme. * * ## OPTIONS * * <theme>... * : One or more themes to delete. * * ## EXAMPLES * * wp theme delete twentyeleven * * @alias uninstall */ function delete( $args ) { foreach ( $this->fetcher->get_many( $args ) as $theme ) { $theme_slug = $theme->get_stylesheet(); if ( $this->is_active_theme( $theme ) ) { WP_CLI::warning( "Can't delete the currently active theme: $theme_slug" ); continue; } $r = delete_theme( $theme_slug ); if ( is_wp_error( $r ) ) { WP_CLI::warning( $r ); } else { WP_CLI::success( "Deleted '$theme_slug' theme." ); } } } /** * Get a list of themes. * * ## OPTIONS * * [--<field>=<value>] * : Filter results based on the value of a field. * * [--field=<field>] * : Prints the value of a single field for each theme. * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, json. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each theme: * * * name * * status * * update * * version * * These fields are optionally available: * * * update_version * * update_package * * update_id * * title * * description * * ## EXAMPLES * * wp theme list --status=inactive --format=csv * * @subcommand list */ public function list_( $_, $assoc_args ) { parent::_list( $_, $assoc_args ); } } /** * Manage theme mods. * */ class Theme_Mod_command extends WP_CLI_Command { /** * Get theme mod(s). * * ## OPTIONS * * [<mod>...] * : One or more mods to get. * * [--all] * : List all theme mods * * [--format=<format>] * : Accepted values: table, json. Default: table * * ## EXAMPLES * * wp theme mod get --all * wp theme mod get background_color --format=json * wp theme mod get background_color header_textcolor */ public function get( $args = array(), $assoc_args = array() ) { if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) { WP_CLI::error( "You must specify at least one mod or use --all." ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { $args = array(); } $list = array(); $mods = get_theme_mods(); if ( ! is_array( $mods ) ) { // if no mods are set (perhaps new theme), make sure foreach still works $mods = array(); } foreach ( $mods as $k => $v ) { // if mods were given, skip the others if ( ! empty( $args ) && ! in_array( $k, $args ) ) continue; if ( is_array( $v ) ) { $list[] = array( 'key' => $k, 'value' => '=>' ); foreach ( $v as $_k => $_v ) { $list[] = array( 'key' => " $_k", 'value' => $_v ); } } else { $list[] = array( 'key' => $k, 'value' => $v ); } } // For unset mods, show blank value foreach ( $args as $mod ) { if ( ! isset( $mods[ $mod ] ) ) { $list[] = array( 'key' => $mod, 'value' => '' ); } } $formatter = new \WP_CLI\Formatter( $assoc_args, array('key', 'value'), 'thememods' ); $formatter->display_items( $list ); } /** * Remove theme mod(s). * * ## OPTIONS * * [<mod>...] * : One or more mods to remove. * * [--all] * : Remove all theme mods * * ## EXAMPLES * * wp theme mod remove --all * wp theme mod remove background_color * wp theme mod remove background_color header_textcolor */ public function remove( $args = array(), $assoc_args = array() ) { if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) { WP_CLI::error( "You must specify at least one mod or use --all." ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) ) { remove_theme_mods(); WP_CLI::success( 'Theme mods removed.' ); return; } foreach ( $args as $mod ) { remove_theme_mod( $mod ); } WP_CLI::success( sprintf( '%d mods removed.', count( $args ) ) ); } /** * Set a theme mod. * * ## OPTIONS * * <mod> * : The name of the theme mod to set or update. * * <value> * : The new value. * * ## EXAMPLES * * wp theme mod set background_color 000000 */ public function set( $args = array(), $assoc_args = array() ) { list( $mod, $value ) = $args; set_theme_mod( $mod, $value ); if ( $value == get_theme_mod( $mod ) ) { WP_CLI::success( sprintf( "Theme mod %s set to %s", $mod, $value ) ); } else { WP_CLI::success( sprintf( "Could not update theme mod %s", $mod ) ); } } } WP_CLI::add_command( 'theme', 'Theme_Command' ); WP_CLI::add_command( 'theme mod', 'Theme_Mod_Command' ); <?php /** * Manage transients. * * ## EXAMPLES * * wp transient set my_key my_value 300 */ class Transient_Command extends WP_CLI_Command { /** * Get a transient value. * * <key> * : Key for the transient. * * [--json] * : Format output as JSON. * * [--network] * : Get the value of the network transient, instead of the single site. */ public function get( $args, $assoc_args ) { list( $key ) = $args; $func = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ) ? 'get_site_transient' : 'get_transient'; $value = $func( $key ); if ( false === $value ) { WP_CLI::warning( 'Transient with key "' . $key . '" is not set.' ); exit; } WP_CLI::print_value( $value, $assoc_args ); } /** * Set a transient value. <expiration> is the time until expiration, in seconds. * * <key> * : Key for the transient. * * <value> * : Value to be set for the transient. * * [<expiration>] * : Time until expiration, in seconds. * * [--network] * : Set the transient value on the network, instead of single site. */ public function set( $args, $assoc_args ) { list( $key, $value ) = $args; $expiration = \WP_CLI\Utils\get_flag_value( $args, 2, 0 ); $func = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ) ? 'set_site_transient' : 'set_transient'; if ( $func( $key, $value, $expiration ) ) { WP_CLI::success( 'Transient added.' ); } else { WP_CLI::error( 'Transient could not be set.' ); } } /** * Delete a transient value. * * <key> * : Key for the transient. * * [--network] * : Delete the value of a network transient, instead of that on a single site. */ public function delete( $args, $assoc_args ) { list( $key ) = $args; $func = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ) ? 'delete_site_transient' : 'delete_transient'; if ( $func( $key ) ) { WP_CLI::success( 'Transient deleted.' ); } else { $func = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ) ? 'get_site_transient' : 'get_transient'; if ( $func( $key ) ) WP_CLI::error( 'Transient was not deleted even though the transient appears to exist.' ); else WP_CLI::warning( 'Transient was not deleted; however, the transient does not appear to exist.' ); } } /** * See whether the transients API is using an object cache or the options table. */ public function type() { global $_wp_using_ext_object_cache, $wpdb; if ( $_wp_using_ext_object_cache ) $message = 'Transients are saved to the object cache.'; else $message = 'Transients are saved to the ' . $wpdb->prefix . 'options table.'; WP_CLI::line( $message ); } /** * Delete all expired transients. * * @subcommand delete-expired */ public function delete_expired() { global $wpdb, $_wp_using_ext_object_cache; // Always delete all transients from DB too. $time = current_time('timestamp'); $count = $wpdb->query( "DELETE a, b FROM $wpdb->options a, $wpdb->options b WHERE a.option_name LIKE '\_transient\_%' AND a.option_name NOT LIKE '\_transient\_timeout\_%' AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) ) AND b.option_value < $time" ); if ( $count > 0 ) { WP_CLI::success( "$count expired transients deleted from the database." ); } else { WP_CLI::success( "No expired transients found" ); } if ( $_wp_using_ext_object_cache ) { WP_CLI::warning( 'Transients are stored in an external object cache, and this command only deletes those stored in the database. You must flush the cache to delete all transients.'); } } /** * Delete all transients. * * @subcommand delete-all */ public function delete_all() { global $wpdb, $_wp_using_ext_object_cache; // Always delete all transients from DB too. $count = $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE '\_transient\_%' OR option_name LIKE '\_site\_transient\_%'" ); if ( $count > 0 ) { WP_CLI::success( "$count transients deleted from the database." ); } else { WP_CLI::success( "No transients found" ); } if ( $_wp_using_ext_object_cache ) { WP_CLI::warning( 'Transients are stored in an external object cache, and this command only deletes those stored in the database. You must flush the cache to delete all transients.'); } } } WP_CLI::add_command( 'transient', 'Transient_Command' ); <?php /** * Manage users. * * @package wp-cli */ class User_Command extends \WP_CLI\CommandWithDBObject { protected $obj_type = 'user'; protected $obj_fields = array( 'ID', 'user_login', 'display_name', 'user_email', 'user_registered', 'roles' ); public function __construct() { $this->fetcher = new \WP_CLI\Fetchers\User; } /** * List users. * * ## OPTIONS * * [--role=<role>] * : Only display users with a certain role. * * [--<field>=<value>] * : Control output by one or more arguments of get_users(). * * [--network] * : List all users in the network for multisite. * * [--field=<field>] * : Prints the value of a single field for each user. * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each user: * * * ID * * user_login * * display_name * * user_email * * user_registered * * roles * * These fields are optionally available: * * * user_pass * * user_nicename * * user_url * * user_activation_key * * user_status * * spam * * deleted * * caps * * cap_key * * allcaps * * filter * * ## EXAMPLES * * wp user list --field=ID * * wp user list --role=administrator --format=csv * * wp user list --fields=display_name,user_email --format=json * * @subcommand list */ public function list_( $args, $assoc_args ) { if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ) ) { if ( ! is_multisite() ) { WP_CLI::error( 'This is not a multisite install.' ); } $assoc_args['blog_id'] = 0; if ( isset( $assoc_args['fields'] ) ) { $fields = explode( ',', $assoc_args['fields'] ); $assoc_args['fields'] = array_diff( $fields, array( 'roles' ) ); } else { $assoc_args['fields'] = array_diff( $this->obj_fields, array( 'roles' ) ); } } $formatter = $this->get_formatter( $assoc_args ); if ( 'ids' == $formatter->format ) { $assoc_args['fields'] = 'ids'; } else { $assoc_args['fields'] = 'all_with_meta'; } $users = get_users( $assoc_args ); if ( 'ids' == $formatter->format ) { echo implode( ' ', $users ); } else { $it = WP_CLI\Utils\iterator_map( $users, function ( $user ) { if ( !is_object( $user ) ) return $user; $user->roles = implode( ',', $user->roles ); return $user; } ); $formatter->display_items( $it ); } } /** * Get a single user. * * ## OPTIONS * * <user> * : User ID, user email, or user login. * * [--field=<field>] * : Instead of returning the whole user, returns the value of a single field. * * [--fields=<fields>] * : Get a specific subset of the user's fields. * * [--format=<format>] * : Accepted values: table, json, csv. Default: table * * ## EXAMPLES * * wp user get 12 --field=login * * wp user get bob --format=json > bob.json */ public function get( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); $user_data = $user->to_array(); $user_data['roles'] = implode( ', ', $user->roles ); $formatter = $this->get_formatter( $assoc_args ); $formatter->display_item( $user_data ); } /** * Delete one or more users from the current site. * * ## OPTIONS * * <user>... * : The user login, user email, or user ID of the user(s) to delete. * * [--network] * : On multisite, delete the user from the entire network. * * [--reassign=<user-id>] * : User ID to reassign the posts to. * * [--yes] * : Answer yes to any confirmation prompts. * * ## EXAMPLES * * # Delete user 123 and reassign posts to user 567 * wp user delete 123 --reassign=567 */ public function delete( $args, $assoc_args ) { $network = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network' ) && is_multisite(); $reassign = \WP_CLI\Utils\get_flag_value( $assoc_args, 'reassign' ); if ( $network && $reassign ) { WP_CLI::error('Reassigning content to a different user is not supported on multisite.'); } if ( !$reassign ) { WP_CLI::confirm( '--reassign parameter not passed. All associated posts will be deleted. Proceed?', $assoc_args ); } $users = $this->fetcher->get_many( $args ); parent::_delete( $users, $assoc_args, function ( $user ) use ( $network, $reassign ) { $user_id = $user->ID; if ( $network ) { $r = wpmu_delete_user( $user_id ); $message = "Deleted user $user_id."; } else { $r = wp_delete_user( $user_id, $reassign ); $message = "Removed user $user_id from " . home_url(); } if ( $r ) { return array( 'success', $message ); } else { return array( 'error', "Failed deleting user $user_id." ); } } ); } /** * Create a user. * * ## OPTIONS * * <user-login> * : The login of the user to create. * * <user-email> * : The email address of the user to create. * * [--role=<role>] * : The role of the user to create. Default: default role * * [--user_pass=<password>] * : The user password. Default: randomly generated * * [--user_registered=<yyyy-mm-dd>] * : The date the user registered. Default: current date * * [--display_name=<name>] * : The display name. * * [--first_name=<first_name>] * : The user's first name. * * [--last_name=<last_name>] * : The user's last name. * * [--send-email] * : Send an email to the user with their new account details. * * [--porcelain] * : Output just the new user id. * * ## EXAMPLES * * wp user create bob bob@example.com --role=author */ public function create( $args, $assoc_args ) { $user = new stdClass; list( $user->user_login, $user->user_email ) = $args; if ( username_exists( $user->user_login ) ) { WP_CLI::error( "The '{$user->user_login}' username is already registered." ); } if ( !is_email( $user->user_email ) ) { WP_CLI::error( "The '{$user->user_email}' email address is invalid." ); } $user->user_registered = \WP_CLI\Utils\get_flag_value( $assoc_args, 'user_registered', strftime( "%F %T", current_time('timestamp') ) ); $user->display_name = \WP_CLI\Utils\get_flag_value( $assoc_args, 'display_name', false ); $user->first_name = \WP_CLI\Utils\get_flag_value( $assoc_args, 'first_name', false ); $user->last_name = \WP_CLI\Utils\get_flag_value( $assoc_args, 'last_name', false ); if ( isset( $assoc_args['user_pass'] ) ) { $user->user_pass = $assoc_args['user_pass']; } else { $user->user_pass = wp_generate_password(); $generated_pass = true; } $user->role = \WP_CLI\Utils\get_flag_value( $assoc_args, 'role', get_option('default_role') ); self::validate_role( $user->role ); if ( is_multisite() ) { $ret = wpmu_validate_user_signup( $user->user_login, $user->user_email ); if ( is_wp_error( $ret['errors'] ) && ! empty( $ret['errors']->errors ) ) { WP_CLI::error( $ret['errors'] ); } $user_id = wpmu_create_user( $user->user_login, $user->user_email, $user->user_login, $user->user_pass ); if ( ! $user_id ) { WP_CLI::error( "Unknown error creating new user" ); } $user->ID = $user_id; $user_id = wp_update_user( $user ); if ( is_wp_error( $user_id ) ) { WP_CLI::error( $user_id ); } } else { $user_id = wp_insert_user( $user ); } if ( ! $user_id || is_wp_error( $user_id ) ) { if ( ! $user_id ) { $user_id = 'Unknown error creating new user.'; } WP_CLI::error( $user_id ); } else { if ( false === $user->role ) { delete_user_option( $user_id, 'capabilities' ); delete_user_option( $user_id, 'user_level' ); } } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'send-email' ) ) { self::wp_new_user_notification( $user_id, $user->user_pass ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) { WP_CLI::line( $user_id ); } else { WP_CLI::success( "Created user $user_id." ); if ( isset( $generated_pass ) ) WP_CLI::line( "Password: $user->user_pass" ); } } /** * Update a user. * * ## OPTIONS * * <user>... * : The user login, user email or user ID of the user(s) to update. * * --<field>=<value> * : One or more fields to update. For accepted fields, see wp_update_user(). * * ## EXAMPLES * * wp user update 123 --display_name=Mary --user_pass=marypass */ public function update( $args, $assoc_args ) { if ( isset( $assoc_args['user_login'] ) ) { WP_CLI::warning( "User logins can't be changed." ); unset( $assoc_args['user_login'] ); } $user_ids = array(); foreach ( $this->fetcher->get_many( $args ) as $user ) { $user_ids[] = $user->ID; } parent::_update( $user_ids, $assoc_args, 'wp_update_user' ); } /** * Generate users. * * ## OPTIONS * * [--count=<number>] * : How many users to generate. Default: 100 * * [--role=<role>] * : The role of the generated users. Default: default role from WP */ public function generate( $args, $assoc_args ) { global $blog_id; $defaults = array( 'count' => 100, 'role' => get_option('default_role'), ); $assoc_args = array_merge( $defaults, $assoc_args ); $role = $assoc_args['role']; if ( ! empty( $role ) ) { self::validate_role( $role ); } $user_count = count_users(); $total = $user_count['total_users']; $limit = $assoc_args['count'] + $total; $notify = \WP_CLI\Utils\make_progress_bar( 'Generating users', $assoc_args['count'] ); for ( $i = $total; $i < $limit; $i++ ) { $login = sprintf( 'user_%d_%d', $blog_id, $i ); $name = "User $i"; $user_id = wp_insert_user( array( 'user_login' => $login, 'user_pass' => $login, 'nickname' => $name, 'display_name' => $name, 'role' => $role ) ); if ( false === $role ) { delete_user_option( $user_id, 'capabilities' ); delete_user_option( $user_id, 'user_level' ); } $notify->tick(); } $notify->finish(); } /** * Set the user role (for a particular blog). * * ## OPTIONS * * <user> * : User ID, user email, or user login. * * [<role>] * : Make the user have the specified role. If not passed, the default role is * used. * * ## EXAMPLES * * wp user set-role bob author * wp user set-role 12 author * * @subcommand set-role */ public function set_role( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); $role = \WP_CLI\Utils\get_flag_value( $args, 1, get_option('default_role') ); self::validate_role( $role ); // Multisite if ( function_exists( 'add_user_to_blog' ) ) add_user_to_blog( get_current_blog_id(), $user->ID, $role ); else $user->set_role( $role ); WP_CLI::success( "Added {$user->user_login} ({$user->ID}) to " . site_url() . " as {$role}" ); } /** * Add a role for a user. * * ## OPTIONS * * <user> * : User ID, user email, or user login. * * <role> * : Add the specified role to the user. * * ## EXAMPLES * * wp user add-role bob author * wp user add-role 12 author * * @subcommand add-role */ public function add_role( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); $role = $args[1]; self::validate_role( $role ); $user->add_role( $role ); WP_CLI::success( sprintf( "Added '%s' role for %s (%d).", $role, $user->user_login, $user->ID ) ); } /** * Remove a user's role. * * ## OPTIONS * * <user> * : User ID, user email, or user login. * * [<role>] * : A specific role to remove. * * ## EXAMPLES * * wp user remove-role bob * wp user remove-role 12 editor * * @subcommand remove-role */ public function remove_role( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); if ( isset( $args[1] ) ) { $role = $args[1]; self::validate_role( $role ); $user->remove_role( $role ); WP_CLI::success( sprintf( "Removed '%s' role for %s (%d).", $role, $user->user_login, $user->ID ) ); } else { // Multisite if ( function_exists( 'remove_user_from_blog' ) ) remove_user_from_blog( $user->ID, get_current_blog_id() ); else $user->remove_all_caps(); WP_CLI::success( "Removed {$user->user_login} ({$user->ID}) from " . site_url() ); } } /** * Add a capability for a user. * * ## OPTIONS * * <user> * : User ID, user email, or user login. * * <cap> * : The capability to add. * * ## EXAMPLES * * wp user add-cap john create_premium_item * wp user add-cap 15 edit_product * * @subcommand add-cap */ public function add_cap( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); if ( $user ) { $cap = $args[1]; $user->add_cap( $cap ); WP_CLI::success( sprintf( "Added '%s' capability for %s (%d).", $cap, $user->user_login, $user->ID ) ); } } /** * Remove a user's capability. * * ## OPTIONS * * <user> * : User ID, user email, or user login. * * <cap> * : The capability to be removed. * * ## EXAMPLES * * wp user remove-cap bob edit_themes * wp user remove-cap 11 publish_newsletters * * @subcommand remove-cap */ public function remove_cap( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); if ( $user ) { $cap = $args[1]; $user->remove_cap( $cap ); WP_CLI::success( sprintf( "Removed '%s' cap for %s (%d).", $cap, $user->user_login, $user->ID ) ); } } /** * List all user's capabilities. * * ## OPTIONS * * <user> * : User ID, user email, or login. * * ## EXAMPLES * * wp user list-caps admin * wp user list-caps 21 * * @subcommand list-caps */ public function list_caps( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); if ( $user ) { $user->get_role_caps(); $user_caps_list = $user->allcaps; foreach ( $user_caps_list as $cap => $active ) { if ( $active ) { \cli\line( $cap ); } } } } /** * Import users from a CSV file. * * ## OPTIONS * * <file> * : The local or remote CSV file of users to import. * * [--send-email] * : Send an email to new users with their account details. * * [--skip-update] * : Don't update users that already exist. * * ## EXAMPLES * * wp user import-csv /path/to/users.csv * wp user import-csv http://example.com/users.csv * * Sample users.csv file: * * user_login,user_email,display_name,role * bobjones,bobjones@example.com,Bob Jones,contributor * newuser1,newuser1@example.com,New User,author * existinguser,existinguser@example.com,Existing User,administrator * * @subcommand import-csv */ public function import_csv( $args, $assoc_args ) { $blog_users = get_users(); $filename = $args[0]; if ( 0 === stripos( $filename, 'http://' ) || 0 === stripos( $filename, 'https://' ) ) { $response = wp_remote_head( $filename ); $response_code = (string)wp_remote_retrieve_response_code( $response ); if ( in_array( $response_code[0], array( 4, 5 ) ) ) { WP_CLI::error( "Couldn't access remote CSV file (HTTP {$response_code} response)." ); } } else if ( ! file_exists( $filename ) ) { WP_CLI::error( sprintf( "Missing file: %s", $filename ) ); } foreach ( new \WP_CLI\Iterators\CSV( $filename ) as $i => $new_user ) { $defaults = array( 'role' => get_option('default_role'), 'user_pass' => wp_generate_password(), 'user_registered' => strftime( "%F %T", time() ), 'display_name' => false, ); $new_user = array_merge( $defaults, $new_user ); $secondary_roles = array(); if ( ! empty( $new_user['roles'] ) ) { $roles = array_map( 'trim', explode( ',', $new_user['roles'] ) ); $invalid_role = false; foreach( $roles as $role ) { if ( is_null( get_role( $role ) ) ) { WP_CLI::warning( "{$new_user['user_login']} has an invalid role" ); $invalid_role = true; break; } } if ( $invalid_role ) { continue; } $new_user['role'] = array_shift( $roles ); $secondary_roles = $roles; } else if ( 'none' === $new_user['role'] ) { $new_user['role'] = false; } elseif ( is_null( get_role( $new_user['role'] ) ) ) { WP_CLI::warning( "{$new_user['user_login']} has an invalid role" ); continue; } // User already exists and we just need to add them to the site if they aren't already there $existing_user = get_user_by( 'email', $new_user['user_email'] ); if ( !$existing_user ) { $existing_user = get_user_by( 'login', $new_user['user_login'] ); } if ( $existing_user && \WP_CLI\Utils\get_flag_value( $assoc_args, 'skip-update' ) ) { WP_CLI::log( "{$existing_user->user_login} exists and has been skipped" ); continue; } else if ( $existing_user ) { $new_user['ID'] = $existing_user->ID; $user_id = wp_update_user( $new_user ); if ( !in_array( $existing_user->user_login, wp_list_pluck( $blog_users, 'user_login' ) ) && $new_user['role'] ) { add_user_to_blog( get_current_blog_id(), $existing_user->ID, $new_user['role'] ); WP_CLI::log( "{$existing_user->user_login} added as {$new_user['role']}." ); } // Create the user } else { unset( $new_user['ID'] ); // Unset else it will just return the ID if ( is_multisite() ) { $ret = wpmu_validate_user_signup( $new_user['user_login'], $new_user['user_email'] ); if ( is_wp_error( $ret['errors'] ) && ! empty( $ret['errors']->errors ) ) { WP_CLI::warning( $ret['errors'] ); continue; } $user_id = wpmu_create_user( $new_user['user_login'], $new_user['user_email'], $new_user['user_pass'] ); if ( ! $user_id ) { WP_CLI::warning( "Unknown error creating new user" ); continue; } $new_user['ID'] = $user_id; $user_id = wp_update_user( $new_user ); if ( is_wp_error( $user_id ) ) { WP_CLI::warning( $user_id ); continue; } } else { $user_id = wp_insert_user( $new_user ); } if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'send-email' ) ) { self::wp_new_user_notification( $user_id, $new_user['user_pass'] ); } } if ( is_wp_error( $user_id ) ) { WP_CLI::warning( $user_id ); continue; } else if ( $new_user['role'] === false ) { delete_user_option( $user_id, 'capabilities' ); delete_user_option( $user_id, 'user_level' ); } $user = get_user_by( 'id', $user_id ); foreach( $secondary_roles as $secondary_role ) { $user->add_role( $secondary_role ); } if ( !empty( $existing_user ) ) { WP_CLI::success( $new_user['user_login'] . " updated" ); } else { WP_CLI::success( $new_user['user_login'] . " created" ); } } } /** * Check whether the role is valid * * @param string */ private static function validate_role( $role ) { if ( ! empty( $role ) && is_null( get_role( $role ) ) ) { WP_CLI::error( sprintf( "Role doesn't exist: %s", $role ) ); } } /** * Acommodate three different behaviors for wp_new_user_notification() * - 4.3.1 and above: expect second argument to be deprecated * - 4.3: Second argument was repurposed as $notify * - Below 4.3: Send the password in the notification * * @param string $user_id * @param string $password */ private static function wp_new_user_notification( $user_id, $password ) { if ( \WP_CLI\Utils\wp_version_compare( '4.3.1', '>=' ) ) { wp_new_user_notification( $user_id, null, 'both' ); } else if ( \WP_CLI\Utils\wp_version_compare( '4.3', '>=' ) ) { wp_new_user_notification( $user_id, 'both' ); } else { wp_new_user_notification( $user_id, $password ); } } } /** * Manage user custom fields. * * ## OPTIONS * * --format=json * : Encode/decode values as JSON. * * ## EXAMPLES * * wp user meta set 123 description "Mary is a WordPress developer." * * wp user meta update admin first_name "George" */ class User_Meta_Command extends \WP_CLI\CommandWithMeta { protected $meta_type = 'user'; public function __construct() { $this->fetcher = new \WP_CLI\Fetchers\User; } /** * Get meta field value. * * ## OPTIONS * * <user> * : The user login, user email, or user ID of the user to get metadata for. * * <key> * : The metadata key. * * [--format=<format>] * : Accepted values: table, json. Default: table */ public function get( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); parent::get( $args, $assoc_args ); } /** * Delete a meta field. * * <user> * : The user login, user email, or user ID of the user to delete metadata from. * * <key> * : The metadata key. * * [<value>] * : The value to delete. If omitted, all rows with key will deleted. */ public function delete( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); parent::delete( $args, $assoc_args ); } /** * Add a meta field. * * <user> * : The user login, user email, or user ID of the user to add metadata for. * * <key> * : The metadata key. * * <value> * : The new metadata value. * * [--format=<format>] * : The serialization format for the value. Default is plaintext. */ public function add( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); parent::add( $args, $assoc_args ); } /** * Update a meta field. * * <user> * : The user login, user email, or user ID of the user to update metadata for. * * <key> * : The metadata key. * * <value> * : The new metadata value. * * [--format=<format>] * : The serialization format for the value. Default is plaintext. * * @alias set */ public function update( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); parent::update( $args, $assoc_args ); } /** * Replace user_login value with user ID * user meta is a special case that also supports user_login * * @param array * @return array */ private function replace_login_with_user_id( $args ) { $user = $this->fetcher->get_check( $args[0] ); $args[0] = $user->ID; return $args; } } /** * Manage user terms. * * * ## EXAMPLES * * wp user term set 123 test category */ class User_Term_Command extends \WP_CLI\CommandWithTerms { protected $obj_type = 'user'; } WP_CLI::add_command( 'user', 'User_Command' ); WP_CLI::add_command( 'user meta', 'User_Meta_Command' ); WP_CLI::add_command( 'user term', 'User_Term_Command' ); <?php /** * Manage sidebar widgets. * * ## EXAMPLES * * # List widgets on a given sidebar * wp widget list sidebar-1 * * # Add a calendar widget to the second position on the sidebar * wp widget add calendar sidebar-1 2 * * # Update option(s) associated with a given widget * wp widget update calendar-1 --title="Calendar" * * # Delete one or more widgets entirely * wp widget delete calendar-2 archive-1 */ class Widget_Command extends WP_CLI_Command { private $fields = array( 'name', 'id', 'position', 'options', ); /** * List widgets associated with a sidebar. * * <sidebar-id> * : ID for the corresponding sidebar. * * [--fields=<fields>] * : Limit the output to specific object fields. * * [--format=<format>] * : Accepted values: table, csv, json, count, ids. Default: table * * ## AVAILABLE FIELDS * * These fields will be displayed by default for each widget: * * * name * * id * * position * * options * * There are no optionally available fields. * * ## EXAMPLES * * wp widget list sidebar-1 --fields=name --format=csv * * @subcommand list */ public function list_( $args, $assoc_args ) { list( $sidebar_id ) = $args; $this->validate_sidebar( $sidebar_id ); $output_widgets = $this->get_sidebar_widgets( $sidebar_id ); if ( ! empty( $assoc_args['format'] ) && 'ids' === $assoc_args['format'] ) { $output_widgets = wp_list_pluck( $output_widgets, 'id' ); } $formatter = new \WP_CLI\Formatter( $assoc_args, $this->fields ); $formatter->display_items( $output_widgets ); } /** * Add a widget to a sidebar. * * <name> * : Widget name. * * <sidebar-id> * : ID for the corresponding sidebar. * * [<position>] * : Widget's current position within the sidebar. Defaults to last * * [--<field>=<value>] * : Widget option to add, with its new value * * ## EXAMPLES * * wp widget add calendar sidebar-1 2 --title="Calendar" * * @subcommand add */ public function add( $args, $assoc_args ) { list( $name, $sidebar_id ) = $args; $position = \WP_CLI\Utils\get_flag_value( $args, 2, 1 ) - 1; $this->validate_sidebar( $sidebar_id ); if ( false == ( $widget = $this->get_widget_obj( $name ) ) ) { WP_CLI::error( "Invalid widget type." ); } /** * Adding a widget is as easy as: * 1. Creating a new widget option * 2. Adding the widget to the sidebar * 3. Positioning appropriately */ $widget_options = $option_keys = $this->get_widget_options( $name ); if ( ! isset( $widget_options['_multiwidget'] ) ) { $widget_options['_multiwidget'] = 1; } unset( $option_keys['_multiwidget'] ); $option_keys = array_keys( $option_keys ); $last_key = array_pop( $option_keys ); $option_index = $last_key + 1; $widget_options[ $option_index ] = $this->sanitize_widget_options( $name, $assoc_args, array() ); $this->update_widget_options( $name, $widget_options ); $widget_id = $name . '-' . $option_index; $this->move_sidebar_widget( $widget_id, null, $sidebar_id, null, $position ); WP_CLI::success( "Added widget to sidebar." ); } /** * Update a given widget's options. * * <widget-id> * : Unique ID for the widget * * [--<field>=<value>] * : Field to update, with its new value * * ## EXAMPLES * * wp widget update calendar-1 --title="Calendar" * * @subcommand update */ public function update( $args, $assoc_args ) { list( $widget_id ) = $args; $this->validate_sidebar_widget( $widget_id ); if ( empty( $assoc_args ) ) { WP_CLI::error( "No options specified to update." ); } list( $name, $option_index ) = $this->get_widget_data( $widget_id ); $widget_options = $this->get_widget_options( $name ); $clean_options = $this->sanitize_widget_options( $name, $assoc_args, $widget_options[ $option_index ] ); $widget_options[ $option_index ] = array_merge( (array)$widget_options[ $option_index ], $clean_options ); $this->update_widget_options( $name, $widget_options ); WP_CLI::success( "Widget updated." ); } /** * Move a widget from one position on a sidebar to another. * * <widget-id> * : Unique ID for the widget * * [--position=<position>] * : Assign the widget to a new position. * * [--sidebar-id=<sidebar-id>] * : Assign the widget to a new sidebar * * ## EXAMPLES * * wp widget move recent-comments-2 --position=2 * * wp widget move recent-comments-2 --sidebar-id=wp_inactive_widgets * * @subcommand move */ public function move( $args, $assoc_args ) { list( $widget_id ) = $args; $this->validate_sidebar_widget( $widget_id ); if ( empty( $assoc_args['position'] ) && empty( $assoc_args['sidebar-id'] ) ) { WP_CLI::error( "A new position or new sidebar must be specified." ); } list( $name, $option_index, $current_sidebar_id, $current_sidebar_index ) = $this->get_widget_data( $widget_id ); $new_sidebar_id = ! empty( $assoc_args['sidebar-id'] ) ? $assoc_args['sidebar-id'] : $current_sidebar_id; $this->validate_sidebar( $new_sidebar_id ); $new_sidebar_index = ! empty( $assoc_args['position'] ) ? $assoc_args['position'] - 1 : $current_sidebar_index; // Moving between sidebars adds to the top if ( $new_sidebar_id != $current_sidebar_id && $new_sidebar_index == $current_sidebar_index ) { // Human-readable positions are different than numerically indexed array $new_sidebar_index = 0; } $this->move_sidebar_widget( $widget_id, $current_sidebar_id, $new_sidebar_id, $current_sidebar_index, $new_sidebar_index ); WP_CLI::success( "Widget moved." ); } /** * Deactivate one or more widgets from an active sidebar. * * <widget-id>... * : Unique ID for the widget(s) * * ## EXAMPLES * * wp widget deactivate recent-comments-2 * * @subcommand deactivate */ public function deactivate( $args, $assoc_args ) { foreach( $args as $widget_id ) { $this->validate_sidebar_widget( $widget_id ); list( $name, $option_index, $sidebar_id, $sidebar_index ) = $this->get_widget_data( $widget_id ); if ( 'wp_inactive_widgets' == $sidebar_id ) { WP_CLI::warning( sprintf( "'%s' is already deactivated.", $widget_id ) ); continue; } $this->move_sidebar_widget( $widget_id, $sidebar_id, 'wp_inactive_widgets', $sidebar_index, 0 ); } WP_CLI::success( "Widget(s) deactivated" ); } /** * Delete one or more widgets from a sidebar. * * <widget-id>... * : Unique ID for the widget(s) * * ## EXAMPLES * * wp widget delete recent-comments-2 * * @subcommand delete */ public function delete( $args, $assoc_args ) { foreach( $args as $widget_id ) { $this->validate_sidebar_widget( $widget_id ); // Remove the widget's settings list( $name, $option_index, $sidebar_id, $sidebar_index ) = $this->get_widget_data( $widget_id ); $widget_options = $this->get_widget_options( $name ); unset( $widget_options[ $option_index ] ); $this->update_widget_options( $name, $widget_options ); // Remove the widget from the sidebar $all_widgets = $this->wp_get_sidebars_widgets(); unset( $all_widgets[ $sidebar_id ][ $sidebar_index ] ); $all_widgets[ $sidebar_id ] = array_values( $all_widgets[ $sidebar_id ] ); update_option( 'sidebars_widgets', $all_widgets ); } WP_CLI::success( "Widget(s) removed from sidebar." ); } /** * Check whether a sidebar is a valid sidebar * * @param string $sidebar_id */ private function validate_sidebar( $sidebar_id ) { global $wp_registered_sidebars; \WP_CLI\Utils\wp_register_unused_sidebar(); if ( ! array_key_exists( $sidebar_id, $wp_registered_sidebars ) ) { WP_CLI::error( "Invalid sidebar." ); } } /** * Check whether the specified widget is on the sidebar * * @param string $widget_id */ private function validate_sidebar_widget( $widget_id ) { $sidebars_widgets = $this->wp_get_sidebars_widgets(); $widget_exists = false; foreach( $sidebars_widgets as $sidebar_id => $widgets ) { if ( in_array( $widget_id, $widgets ) ) { $widget_exists = true; break; } } if ( false === $widget_exists ) { WP_CLI::error( "Specified widget isn't present on sidebar." ); } } /** * Get the widgets (and their associated data) for a given sidebar * * @param string $sidebar_id * @return array */ private function get_sidebar_widgets( $sidebar_id ) { $all_widgets = $this->wp_get_sidebars_widgets(); if ( empty( $all_widgets[ $sidebar_id ] ) ) { return array(); } $prepared_widgets = array(); foreach( $all_widgets[ $sidebar_id ] as $key => $widget_id ) { $prepared_widget = new stdClass; $parts = explode( '-', $widget_id ); $option_index = array_pop( $parts ); $widget_name = implode( '-', $parts ); $prepared_widget->name = $widget_name; $prepared_widget->id = $widget_id; $prepared_widget->position = $key + 1; $widget_options = get_option( 'widget_' . $widget_name ); $prepared_widget->options = $widget_options[ $option_index ]; $prepared_widgets[] = $prepared_widget; } return $prepared_widgets; } /** * Re-implementation of wp_get_sidebars_widgets() * because the original has a nasty global component */ private function wp_get_sidebars_widgets() { $sidebars_widgets = get_option( 'sidebars_widgets', array() ); if ( is_array( $sidebars_widgets ) && isset( $sidebars_widgets['array_version'] ) ) { unset( $sidebars_widgets['array_version'] ); } return $sidebars_widgets; } /** * Get the widget's name, option index, sidebar, and sidebar index from its ID * * @param string $widget_id * @return array */ private function get_widget_data( $widget_id ) { $parts = explode( '-', $widget_id ); $option_index = array_pop( $parts ); $name = implode( '-', $parts ); $sidebar_id = false; $sidebar_index = false; $all_widgets = $this->wp_get_sidebars_widgets(); foreach( $all_widgets as $s_id => &$widgets ) { if ( false !== ( $key = array_search( $widget_id, $widgets ) ) ) { $sidebar_id = $s_id; $sidebar_index = $key; break; } } return array( $name, $option_index, $sidebar_id, $sidebar_index ); } /** * Get the options for a given widget * * @param string $name * @return array */ private function get_widget_options( $name ) { return get_option( 'widget_' . $name, array() ); } /** * Update the options for a given widget * * @param string $name * @param mixed */ private function update_widget_options( $name, $value ) { update_option( 'widget_' . $name, $value ); } /** * Reposition a widget within a sidebar or move to another sidebar. * * @param string $widget_id * @param string|null $current_sidebar_id * @param string $new_sidebar_id * @param int|null $current_index * @param int $new_index */ private function move_sidebar_widget( $widget_id, $current_sidebar_id, $new_sidebar_id, $current_index, $new_index ) { $all_widgets = $this->wp_get_sidebars_widgets(); $needs_placement = true; // Existing widget if ( $current_sidebar_id && ! is_null( $current_index ) ) { $widgets = $all_widgets[ $current_sidebar_id ]; if ( $current_sidebar_id !== $new_sidebar_id ) { unset( $widgets[ $current_index ] ); } else { $part = array_splice( $widgets, $current_index, 1 ); array_splice( $widgets, $new_index, 0, $part ); $needs_placement = false; } $all_widgets[ $current_sidebar_id ] = array_values( $widgets ); } if ( $needs_placement ) { $widgets = ! empty( $all_widgets[ $new_sidebar_id ] ) ? $all_widgets[ $new_sidebar_id ] : array(); $before = array_slice( $widgets, 0, $new_index, true ); $after = array_slice( $widgets, $new_index, count( $widgets ), true ); $widgets = array_merge( $before, array( $widget_id ), $after ); $all_widgets[ $new_sidebar_id ] = array_values( $widgets ); } update_option( 'sidebars_widgets', $all_widgets ); } /** * Get a widget's instantiated object based on its name * * @param string $id_base Name of the widget * @return WP_Widget|false */ private function get_widget_obj( $id_base ) { global $wp_widget_factory; $widget = wp_filter_object_list( $wp_widget_factory->widgets, array( 'id_base' => $id_base ) ); if ( empty( $widget ) ) { false; } return array_pop( $widget ); } /** * Clean up a widget's options based on its update callback * * @param string $id_base Name of the widget * @param mixed $dirty_options * @param mixed $old_options * @return mixed */ private function sanitize_widget_options( $id_base, $dirty_options, $old_options ) { $widget = $this->get_widget_obj( $id_base ); if ( empty( $widget ) ) { return array(); } // No easy way to determine expected array keys for $dirty_options // because Widget API dependent on the form fields return @$widget->update( $dirty_options, $old_options ); } } WP_CLI::add_command( 'widget', 'Widget_Command' ); <?php return array( 'path' => array( 'runtime' => '=<path>', 'file' => '<path>', 'desc' => 'Path to the WordPress files.', ), 'url' => array( 'runtime' => '=<url>', 'file' => '<url>', 'desc' => 'Pretend request came from given URL. In multisite, this argument is how the target site is specified.', ), 'blog' => array( 'deprecated' => 'Use --url instead.', 'runtime' => '=<url>', ), 'config' => array( 'deprecated' => 'Use the WP_CLI_CONFIG_PATH environment variable instead.', 'runtime' => '=<path>', ), 'user' => array( 'runtime' => '=<id|login|email>', 'file' => '<id|login|email>', 'desc' => 'Set the WordPress user.', ), 'skip-plugins' => array( 'runtime' => '[=<plugin>]', 'file' => '<list>', 'desc' => 'Skip loading all or some plugins.', 'default' => '', ), 'skip-themes' => array( 'runtime' => '[=<theme>]', 'file' => '<list>', 'desc' => 'Skip loading all or some themes.', 'default' => '', ), 'require' => array( 'runtime' => '=<path>', 'file' => '<path>', 'desc' => 'Load PHP file before running the command (may be used more than once).', 'multiple' => true, 'default' => array(), ), 'disabled_commands' => array( 'file' => '<list>', 'default' => array(), 'desc' => '(Sub)commands to disable.', ), 'color' => array( 'runtime' => true, 'file' => '<bool>', 'default' => 'auto', 'desc' => 'Whether to colorize the output.', ), 'debug' => array( 'runtime' => '', 'file' => '<bool>', 'default' => false, 'desc' => 'Show all PHP errors; add verbosity to WP-CLI bootstrap.', ), 'prompt' => array( 'runtime' => '', 'file' => false, 'default' => false, 'desc' => 'Prompt the user to enter values for all command arguments.', ), 'quiet' => array( 'runtime' => '', 'file' => '<bool>', 'default' => false, 'desc' => 'Suppress informational messages.', ), 'apache_modules' => array( 'file' => '<list>', 'desc' => 'List of Apache Modules that are to be reported as loaded.', 'multiple' => true, 'default' => array(), ), # --allow-root => (NOT RECOMMENDED) Allow wp-cli to run as root. This poses # a security risk, so you probably do not want to do this. 'allow-root' => array( 'file' => false, # Explicit. Just in case the default changes. 'runtime' => '', 'hidden' => true, ), ); <?php namespace WP_CLI\Dispatcher; /** * Get the path to a command, e.g. "core download" * * @param WP_CLI\Dispatcher\Subcommand $command * @return string */ function get_path( $command ) { $path = array(); do { array_unshift( $path, $command->get_name() ); } while ( $command = $command->get_parent() ); return $path; } <?php class WP_Export_Oxymel extends Oxymel { public function optional( $tag_name, $contents ) { if ( $contents ) { $this->$tag_name( $contents ); } return $this; } public function optional_cdata( $tag_name, $contents ) { if ( $contents ) { $this->$tag_name->contains->cdata( $contents )->end; } return $this; } public function cdata( $text ) { if ( !seems_utf8( $text ) ) { $text = utf8_encode( $text ); } return parent::cdata( $text ); } } <?php /** * Represents a set of posts and other site data to be exported. * * An immutable object, which gathers all data needed for the export. */ class WP_Export_Query { const QUERY_CHUNK = 100; private static $defaults = array( 'post_ids' => null, 'post_type' => null, 'status' => null, 'author' => null, 'start_date' => null, 'end_date' => null, 'start_id' => null, 'category' => null, ); private $post_ids; private $filters; private $xml_gen; private $wheres = array(); private $joins = array(); private $author; private $category; public $missing_parents = false; public function __construct( $filters = array() ) { $this->filters = wp_parse_args( $filters, self::$defaults ); $this->post_ids = $this->calculate_post_ids(); } public function post_ids() { return $this->post_ids; } public function charset() { return get_bloginfo( 'charset' ); } public function site_metadata() { $metadata = array( 'name' => $this->bloginfo_rss( 'name' ), 'url' => $this->bloginfo_rss( 'url' ), 'language' => $this->bloginfo_rss( 'language' ), 'description' => $this->bloginfo_rss( 'description' ), 'pubDate' => date( 'D, d M Y H:i:s +0000' ), 'site_url' => is_multisite()? network_home_url() : $this->bloginfo_rss( 'url' ), 'blog_url' => $this->bloginfo_rss( 'url' ), ); return $metadata; } public function wp_generator_tag() { return apply_filters( 'the_generator', get_the_generator( 'export' ), 'export' ); } public function authors() { global $wpdb; $authors = array(); $author_ids = $wpdb->get_col( "SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft'" ); foreach ( (array) $author_ids as $author_id ) { $authors[] = get_userdata( $author_id ); } $authors = array_filter( $authors ); return $authors; } public function categories() { if ( $this->category ) { return array( $this->category ); } if ( $this->filters['post_type'] ) { return array(); } $categories = (array) get_categories( array( 'get' => 'all' ) ); $this->check_for_orphaned_terms( $categories ); $categories = self::topologically_sort_terms( $categories ); return $categories; } public function tags() { if ( $this->filters['post_type'] ) { return array(); } $tags = (array) get_tags( array( 'get' => 'all' ) ); $this->check_for_orphaned_terms( $tags ); return $tags; } public function custom_taxonomies_terms() { if ( $this->filters['post_type'] ) { return array(); } $custom_taxonomies = get_taxonomies( array( '_builtin' => false ) ); $custom_terms = (array) get_terms( $custom_taxonomies, array( 'get' => 'all' ) ); $this->check_for_orphaned_terms( $custom_terms ); $custom_terms = self::topologically_sort_terms( $custom_terms ); return $custom_terms; } public function nav_menu_terms() { $nav_menus = wp_get_nav_menus(); foreach( $nav_menus as &$term ) { $term->description = ''; } return $nav_menus; } public function exportify_post( $post ) { $GLOBALS['wp_query']->in_the_loop = true; $previous_global_post = \WP_CLI\Utils\get_flag_value( $GLOBALS, 'post' ); $GLOBALS['post'] = $post; setup_postdata( $post ); $post->post_content = apply_filters( 'the_content_export', $post->post_content ); $post->post_excerpt = apply_filters( 'the_excerpt_export', $post->post_excerpt ); $post->is_sticky = is_sticky( $post->ID ) ? 1 : 0; $post->terms = self::get_terms_for_post( $post ); $post->meta = self::get_meta_for_post( $post ); $post->comments = self::get_comments_for_post( $post ); $GLOBALS['post'] = $previous_global_post; return $post; } public function posts() { $posts_iterator = new WP_Post_IDs_Iterator( $this->post_ids, self::QUERY_CHUNK ); return new WP_Map_Iterator( $posts_iterator, array( $this, 'exportify_post' ) ); } private function calculate_post_ids() { global $wpdb; if ( is_array( $this->filters['post_ids'] ) ) { return $this->filters['post_ids']; } $this->post_type_where(); $this->status_where(); $this->author_where(); $this->start_date_where(); $this->end_date_where(); $this->start_id_where(); $this->category_where(); $where = implode( ' AND ', array_filter( $this->wheres ) ); if ( $where ) $where = "WHERE $where"; $join = implode( ' ', array_filter( $this->joins ) ); $post_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} AS p $join $where" ); $post_ids = array_merge( $post_ids, $this->attachments_for_specific_post_types( $post_ids ) ); return $post_ids; } private function post_type_where() { global $wpdb; $post_types_filters = array( 'can_export' => true ); if ( $this->filters['post_type'] ) { $post_types = $this->filters['post_type']; // Flatten single post types if ( is_array( $post_types ) && 1 === count( $post_types ) ) { $post_types = array_shift( $post_types ); } $post_types_filters = array_merge( $post_types_filters, array( 'name' => $post_types ) ); } // Multiple post types if ( is_array( $post_types_filters['name'] ) ) { $post_types = array(); foreach ( $post_types_filters['name'] as $post_type ) { if ( post_type_exists( $post_type ) ) { $post_types[] = $post_type; } } } else { $post_types = get_post_types( $post_types_filters ); } if ( ! $post_types ) { $this->wheres[] = 'p.post_type IS NULL'; return; } $this->wheres[] = _wp_export_build_IN_condition( 'p.post_type', $post_types ); } private function status_where() { global $wpdb; if ( !$this->filters['status'] ) { $this->wheres[] = "p.post_status != 'auto-draft'"; return; } $this->wheres[] = $wpdb->prepare( 'p.post_status = %s', $this->filters['status'] ); } private function author_where() { global $wpdb; $user = $this->find_user_from_any_object( $this->filters['author'] ); if ( !$user || is_wp_error( $user ) ) { return; } $this->author = $user; $this->wheres[] = $wpdb->prepare( 'p.post_author = %d', $user->ID ); } private function start_date_where() { global $wpdb; $timestamp = strtotime( $this->filters['start_date'] ); if ( !$timestamp ) { return; } $this->wheres[] = $wpdb->prepare( 'p.post_date >= %s', date( 'Y-m-d 00:00:00', $timestamp ) ); } private function end_date_where() { global $wpdb; if ( preg_match( '/^\d{4}-\d{2}$/', $this->filters['end_date'] ) ) { $timestamp = $this->get_timestamp_for_the_last_day_of_a_month( $this->filters['end_date'] ); } else { $timestamp = strtotime( $this->filters['end_date'] ); } if ( !$timestamp ) { return; } $this->wheres[] = $wpdb->prepare( 'p.post_date <= %s', date( 'Y-m-d 23:59:59', $timestamp ) ); } private function start_id_where() { global $wpdb; $start_id = absint( $this->filters['start_id'] ); if ( 0 === $start_id ) { return; } $this->wheres[] = $wpdb->prepare( 'p.ID >= %d', $start_id ); } private function get_timestamp_for_the_last_day_of_a_month( $yyyy_mm ) { return strtotime( "$yyyy_mm +1month -1day" ); } private function category_where() { global $wpdb; if ( 'post' != $this->filters['post_type'] && ! in_array( 'post', (array) $this->filters['post_type'] ) ) { return; } $category = $this->find_category_from_any_object( $this->filters['category'] ); if ( !$category ) { return; } $this->category = $category; $this->joins[] = "INNER JOIN {$wpdb->term_relationships} AS tr ON (p.ID = tr.object_id)"; $this->wheres[] = $wpdb->prepare( 'tr.term_taxonomy_id = %d', $category->term_taxonomy_id ); } private function attachments_for_specific_post_types( $post_ids ) { global $wpdb; if ( !$this->filters['post_type'] ) { return array(); } $attachment_ids = array(); while ( $batch_of_post_ids = array_splice( $post_ids, 0, self::QUERY_CHUNK ) ) { $post_parent_condition = _wp_export_build_IN_condition( 'post_parent', $batch_of_post_ids ); $attachment_ids = array_merge( $attachment_ids, (array)$wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'attachment' AND $post_parent_condition" ) ); } return array_map( 'intval', $attachment_ids ); } private function bloginfo_rss( $section ) { return apply_filters( 'bloginfo_rss', get_bloginfo_rss( $section ), $section ); } private function find_user_from_any_object( $user ) { if ( is_numeric( $user ) ) { return get_user_by( 'id', $user ); } elseif ( is_string( $user ) ) { return get_user_by( 'login', $user ); } elseif ( isset( $user->ID ) ) { return get_user_by( 'id', $user->ID ); } return false; } private function find_category_from_any_object( $category ) { if ( is_numeric( $category ) ) { return get_term( $category, 'category' ); } elseif ( is_string( $category ) ) { $term = term_exists( $category, 'category' ); return isset( $term['term_id'] )? get_term( $term['term_id'], 'category' ) : false; } elseif ( isset( $category->term_id ) ) { return get_term( $category->term_id, 'category' ); } return false; } private static function topologically_sort_terms( $terms ) { $sorted = array(); while ( $term = array_shift( $terms ) ) { if ( $term->parent == 0 || isset( $sorted[$term->parent] ) ) $sorted[$term->term_id] = $term; else $terms[] = $term; } return $sorted; } private function check_for_orphaned_terms( $terms ) { $term_ids = array(); $have_parent = array(); foreach ( $terms as $term ) { $term_ids[ $term->term_id ] = true; if ( $term->parent != 0 ) $have_parent[] = $term; } foreach ( $have_parent as $has_parent ) { if ( ! isset( $term_ids[ $has_parent->parent ] ) ) { $this->missing_parents = $has_parent; throw new WP_Export_Term_Exception( sprintf( __( 'Term is missing a parent: %s (%d)' ), $has_parent->slug, $has_parent->term_taxonomy_id ) ); } } } private static function get_terms_for_post( $post ) { $taxonomies = get_object_taxonomies( $post->post_type ); if ( empty( $taxonomies ) ) return array(); $terms = wp_get_object_terms( $post->ID, $taxonomies ); $terms = $terms? $terms : array(); return $terms; } private static function get_meta_for_post( $post ) { global $wpdb; $meta_for_export = array(); $meta_from_db = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) ); foreach ( $meta_from_db as $meta ) { if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) continue; if ( in_array( $meta->meta_key, array( '_edit_lock', '_wp_attachment_metadata', '_wp_attached_file' ) ) ) { continue; } $meta_for_export[] = $meta; } return $meta_for_export; } private static function get_comments_for_post( $post ) { global $wpdb; $comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) ); foreach( $comments as $comment ) { $meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $comment->comment_ID ) ); $meta = $meta? $meta : array(); $comment->meta = $meta; } return $comments; } } class WP_Export_Exception extends RuntimeException { } class WP_Export_Term_Exception extends RuntimeException { } <?php /** * Version number for the export format. * * Bump this when something changes that might affect compatibility. * * @since 2.5.0 */ define( 'WXR_VERSION', '1.2' ); /** * Responsible for formatting the data in WP_Export_Query to WXR */ class WP_Export_WXR_Formatter { public function __construct( $export ) { $this->export = $export; $this->wxr_version = WXR_VERSION; } public function before_posts() { $before_posts_xml = ''; $before_posts_xml .= $this->header(); $before_posts_xml .= $this->site_metadata(); $before_posts_xml .= $this->authors(); $before_posts_xml .= $this->categories(); $before_posts_xml .= $this->tags(); $before_posts_xml .= $this->nav_menu_terms(); $before_posts_xml .= $this->custom_taxonomies_terms(); $before_posts_xml .= $this->rss2_head_action(); return $before_posts_xml; } public function posts() { return new WP_Map_Iterator( $this->export->posts(), array( $this, 'post' ) ); } public function after_posts() { return $this->footer(); } public function header() { $oxymel = new Oxymel; $charset = $this->export->charset(); $wp_generator_tag = $this->export->wp_generator_tag(); $comment = <<<COMMENT This is a WordPress eXtended RSS file generated by WordPress as an export of your site. It contains information about your site's posts, pages, comments, categories, and other content. You may use this file to transfer that content from one site to another. This file is not intended to serve as a complete backup of your site. To import this information into a WordPress site follow these steps: 1. Log in to that site as an administrator. 2. Go to Tools: Import in the WordPress admin panel. 3. Install the "WordPress" importer from the list. 4. Activate & Run Importer. 5. Upload this file using the form provided on that page. 6. You will first be asked to map the authors in this export file to users on the site. For each author, you may choose to map to an existing user on the site or to create a new user. 7. WordPress will then import each of the posts, pages, comments, categories, etc. contained in this file into your site. COMMENT; return $oxymel ->xml ->comment( $comment ) ->raw( $wp_generator_tag ) ->open_rss( array( 'version' => '2.0', 'xmlns:excerpt' => "http://wordpress.org/export/{$this->wxr_version}/excerpt/", 'xmlns:content' => "http://purl.org/rss/1.0/modules/content/", 'xmlns:wfw' => "http://wellformedweb.org/CommentAPI/", 'xmlns:dc' => "http://purl.org/dc/elements/1.1/", 'xmlns:wp' => "http://wordpress.org/export/{$this->wxr_version}/", ) ) ->open_channel ->to_string(); } public function site_metadata() { $oxymel = new Oxymel; $metadata = $this->export->site_metadata(); return $oxymel ->title( $metadata['name'] ) ->link( $metadata['url'] ) ->description( $metadata['description'] ) ->pubDate( $metadata['pubDate'] ) ->language( $metadata['language'] ) ->tag( 'wp:wxr_version', $this->wxr_version ) ->tag( 'wp:base_site_url', $metadata['site_url'] ) ->tag( 'wp:base_blog_url', $metadata['blog_url'] ) ->to_string(); } public function authors() { $oxymel = new Oxymel; $authors = $this->export->authors(); foreach ( $authors as $author ) { $oxymel ->tag( 'wp:author' )->contains ->tag( 'wp:author_id', $author->ID ) ->tag( 'wp:author_login', $author->user_login ) ->tag( 'wp:author_email', $author->user_email ) ->tag( 'wp:author_display_name' )->contains->cdata( $author->display_name )->end ->tag( 'wp:author_first_name' )->contains->cdata( $author->user_firstname )->end ->tag( 'wp:author_last_name' )->contains->cdata( $author->user_lastname )->end ->end; } return $oxymel->to_string(); } public function categories() { $oxymel = new WP_Export_Oxymel; $categories = $this->export->categories(); foreach( $categories as $term_id => $category ) { $category->parent_slug = $category->parent? $categories[$category->parent]->slug : ''; $oxymel->tag( 'wp:category' )->contains ->tag( 'wp:term_id', $category->term_id ) ->tag( 'wp:category_nicename', $category->slug ) ->tag( 'wp:category_parent', $category->parent_slug ) ->optional_cdata( 'wp:cat_name', $category->name ) ->optional_cdata( 'wp:category_description', $category->description ) ->end; } return $oxymel->to_string(); } public function tags() { $oxymel = new WP_Export_Oxymel; $tags = $this->export->tags(); foreach( $tags as $tag ) { $oxymel->tag( 'wp:tag' )->contains ->tag( 'wp:term_id', $tag->term_id ) ->tag( 'wp:tag_slug', $tag->slug ) ->optional_cdata( 'wp:tag_name', $tag->name ) ->optional_cdata( 'wp:tag_description', $tag->description ) ->end; } return $oxymel->to_string(); } public function nav_menu_terms() { return $this->terms( $this->export->nav_menu_terms() ); } public function custom_taxonomies_terms() { return $this->terms( $this->export->custom_taxonomies_terms() ); } public function rss2_head_action() { ob_start(); do_action( 'rss2_head' ); $action_output = ob_get_clean(); return $action_output; } public function post( $post ) { $oxymel = new WP_Export_Oxymel; $GLOBALS['wp_query']->in_the_loop = true; $GLOBALS['post'] = $post; setup_postdata( $post ); $oxymel->item->contains ->title( apply_filters( 'the_title_rss', $post->post_title ) ) ->link( esc_url( apply_filters('the_permalink_rss', get_permalink() ) ) ) ->pubDate( mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ) ) ->tag( 'dc:creator', get_the_author_meta( 'login' ) ) ->guid( esc_url( get_the_guid() ), array( 'isPermaLink' => 'false' ) ) ->description( '' ) ->tag( 'content:encoded' )->contains->cdata( $post->post_content )->end ->tag( 'excerpt:encoded' )->contains->cdata( $post->post_excerpt )->end ->tag( 'wp:post_id', $post->ID ) ->tag( 'wp:post_date', $post->post_date ) ->tag( 'wp:post_date_gmt', $post->post_date_gmt ) ->tag( 'wp:comment_status', $post->comment_status ) ->tag( 'wp:ping_status', $post->ping_status ) ->tag( 'wp:post_name', $post->post_name ) ->tag( 'wp:status', $post->post_status ) ->tag( 'wp:post_parent', $post->post_parent ) ->tag( 'wp:menu_order', $post->menu_order ) ->tag( 'wp:post_type', $post->post_type ) ->tag( 'wp:post_password', $post->post_password ) ->tag( 'wp:is_sticky', $post->is_sticky ) ->optional( 'wp:attachment_url', wp_get_attachment_url( $post->ID ) ); foreach( $post->terms as $term ) { $oxymel ->category( array( 'domain' => $term->taxonomy, 'nicename' => $term->slug ) )->contains->cdata( $term->name )->end; } foreach( $post->meta as $meta ) { $oxymel ->tag( 'wp:postmeta' )->contains ->tag( 'wp:meta_key', $meta->meta_key ) ->tag( 'wp:meta_value' )->contains->cdata( $meta->meta_value )->end ->end; } foreach( $post->comments as $comment ) { $oxymel ->tag( 'wp:comment' )->contains ->tag( 'wp:comment_id', $comment->comment_ID ) ->tag( 'wp:comment_author' )->contains->cdata( $comment->comment_author )->end ->tag( 'wp:comment_author_email', $comment->comment_author_email ) ->tag( 'wp:comment_author_url', esc_url( $comment->comment_author_url ) ) ->tag( 'wp:comment_author_IP', $comment->comment_author_IP ) ->tag( 'wp:comment_date', $comment->comment_date ) ->tag( 'wp:comment_date_gmt', $comment->comment_date_gmt ) ->tag( 'wp:comment_content' )->contains->cdata( $comment->comment_content )->end ->tag( 'wp:comment_approved', $comment->comment_approved ) ->tag( 'wp:comment_type', $comment->comment_type ) ->tag( 'wp:comment_parent', $comment->comment_parent ) ->tag( 'wp:comment_user_id', $comment->user_id ) ->oxymel( $this->comment_meta( $comment ) ) ->end; } $oxymel ->end; return $oxymel->to_string(); } public function footer() { $oxymel = new Oxymel; return $oxymel->close_channel->close_rss->to_string(); } protected function terms( $terms ) { $oxymel = new WP_Export_Oxymel; foreach( $terms as $term ) { $term->parent_slug = $term->parent? $terms[$term->parent]->slug : ''; $oxymel->tag( 'wp:term' )->contains ->tag( 'wp:term_id', $term->term_id ) ->tag( 'wp:term_taxonomy', $term->taxonomy ) ->tag( 'wp:term_slug', $term->slug ); if ( 'nav_menu' != $term->taxonomy ) { $oxymel ->tag( 'wp:term_parent', $term->parent_slug ); } $oxymel ->optional_cdata( 'wp:term_name', $term->name ) ->optional_cdata( 'wp:term_description', $term->description ) ->end; } return $oxymel->to_string(); } protected function comment_meta( $comment ) { global $wpdb; $metas = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $comment->comment_ID ) ); if ( !$metas ) { return new Oxymel; } $oxymel = new WP_Export_Oxymel; foreach( $metas as $meta ) { $oxymel->tag( 'wp:commentmeta' )->contains ->tag( 'wp:meta_key', $meta->meta_key ) ->tag( 'wp:meta_value' )->contains->cdata( $meta->meta_value )->end ->end; } return $oxymel; } } <?php function wp_export( $args = array() ) { $defaults = array( 'filters' => array(), 'format' => 'WP_Export_WXR_Formatter', 'writer' => 'WP_Export_Returner', 'writer_args' => null, ); $args = wp_parse_args( $args, $defaults ); $export_query = new WP_Export_Query( $args['filters'] ); $formatter = new $args['format']( $export_query ); $writer = new $args['writer']( $formatter, $args['writer_args'] ); try { return $writer->export(); } catch ( WP_Export_Exception $e ) { return new WP_Error( 'wp-export-error', $e->getMessage() ); } } function wp_export_new_style_args_from_old_style_args( $args ) { if ( isset( $args['content'] ) ) { if ( 'all' == $args['content'] ) { unset( $args['content'] ); } else { $args['post_type'] = $args['content']; } } return $args; } // TEMPORARY function _wp_export_build_IN_condition( $column_name, $values, $format = '%s' ) { global $wpdb; if ( !is_array( $values ) || empty( $values ) ) { return ''; } $formats = implode( ', ', array_fill( 0, count( $values ), $format ) ); return $wpdb->prepare( "$column_name IN ($formats)", $values ); } <?php class WP_Map_Iterator extends IteratorIterator { function __construct( $iterator, $callback ) { $this->callback = $callback; parent::__construct( $iterator ); } function current() { $original_current = parent::current(); return call_user_func( $this->callback, $original_current ); } } class WP_Post_IDs_Iterator implements Iterator { private $limit = 100; private $post_ids; private $ids_left; private $results = array(); public function __construct( $post_ids, $limit = null ) { $this->db = $GLOBALS['wpdb']; $this->post_ids = $post_ids; $this->ids_left = $post_ids; if ( !is_null( $limit ) ) { $this->limit = $limit; } } public function current() { return $this->results[$this->index_in_results]; } public function key() { return $this->global_index; } public function next() { $this->index_in_results++; $this->global_index++; } public function rewind() { $this->results = array(); $this->global_index = 0; $this->index_in_results = 0; $this->ids_left = $this->post_ids; } public function valid() { if ( isset( $this->results[$this->index_in_results] ) ) { return true; } if ( empty( $this->ids_left ) ) { return false; } $has_posts = $this->load_next_posts_from_db(); if ( !$has_posts ) { return false; } $this->index_in_results = 0; return true; } private function load_next_posts_from_db() { $next_batch_post_ids = array_splice( $this->ids_left, 0, $this->limit ); $in_post_ids_sql = _wp_export_build_IN_condition( 'ID', $next_batch_post_ids ); $this->results = $this->db->get_results( "SELECT * FROM {$this->db->posts} WHERE $in_post_ids_sql" ); if ( !$this->results ) { if ( $this->db->last_error ) { throw new WP_Iterator_Exception( 'Database error: ' . $this->db->last_error ); } else { return false; } } return true; } } class WP_Iterator_Exception extends Exception { } <?php abstract class WP_Export_Base_Writer { protected $formatter; function __construct( $formatter ) { $this->formatter = $formatter; } public function export() { $this->write( $this->formatter->before_posts() ); foreach( $this->formatter->posts() as $post_in_wxr ) { $this->write( $post_in_wxr ); } $this->write( $this->formatter->after_posts() ); } abstract protected function write( $xml ); } class WP_Export_XML_Over_HTTP extends WP_Export_Base_Writer { private $file_name; function __construct( $formatter, $file_name ) { parent::__construct( $formatter ); $this->file_name = $file_name; } public function export() { try { $export = $this->get_export(); $this->send_headers(); echo $export; } catch ( WP_Export_Exception $e ) { $message = apply_filters( 'export_error_message', $e->getMessage() ); wp_die( $message, __( 'Export Error' ), array( 'back_link' => true ) ); } catch ( WP_Export_Term_Exception $e ) { do_action( 'export_term_orphaned', $this->formatter->export->missing_parents ); $message = apply_filters( 'export_term_error_message', $e->getMessage() ); wp_die( $message, __( 'Export Error' ), array( 'back_link' => true ) ); } } protected function write( $xml ) { $this->result .= $xml; } protected function get_export() { $this->result = ''; parent::export(); return $this->result; } protected function send_headers() { header( 'Content-Description: File Transfer' ); header( 'Content-Disposition: attachment; filename=' . $this->file_name ); header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true ); } } class WP_Export_Returner extends WP_Export_Base_Writer { private $result = ''; public function export() { $this->private = ''; try { parent::export(); } catch ( WP_Export_Exception $e ) { $message = apply_filters( 'export_error_message', $e->getMessage() ); return new WP_Error( 'wp-export-error', $message ); } catch ( WP_Export_Term_Exception $e ) { do_action( 'export_term_orphaned', $this->formatter->export->missing_parents ); $message = apply_filters( 'export_term_error_message', $e->getMessage() ); return new WP_Error( 'wp-export-error', $message ); } return $this->result; } protected function write( $xml ) { $this->result .= $xml; } } class WP_Export_File_Writer extends WP_Export_Base_Writer { private $f; private $file_name; public function __construct( $formatter, $file_name ) { parent::__construct( $formatter ); $this->file_name = $file_name; } public function export() { $this->f = fopen( $this->file_name, 'w' ); if ( !$this->f ) { throw new WP_Export_Exception( sprintf( __( 'WP Export: error opening %s for writing.' ), $this->file_name ) ); } try { parent::export(); } catch ( WP_Export_Exception $e ) { throw $e; } catch ( WP_Export_Term_Exception $e ) { throw $e; } fclose( $this->f ); } protected function write( $xml ) { $res = fwrite( $this->f, $xml); if ( false === $res ) { throw new WP_Export_Exception( __( 'WP Export: error writing to export file.' ) ); } } } class WP_Export_Split_Files_Writer extends WP_Export_Base_Writer { private $result = ''; private $f; private $next_file_number = 0; private $current_file_size = 0; function __construct( $formatter, $writer_args = array() ) { parent::__construct( $formatter ); //TODO: check if args are not missing $this->max_file_size = is_null( $writer_args['max_file_size'] ) ? 15 * MB_IN_BYTES : $writer_args['max_file_size']; $this->destination_directory = $writer_args['destination_directory']; $this->filename_template = $writer_args['filename_template']; $this->before_posts_xml = $this->formatter->before_posts(); $this->after_posts_xml = $this->formatter->after_posts(); } public function export() { $this->start_new_file(); foreach( $this->formatter->posts() as $post_xml ) { if ( $this->current_file_size + strlen( $post_xml ) > $this->max_file_size ) { $this->start_new_file(); } $this->write( $post_xml ); } $this->close_current_file(); } protected function write( $xml ) { $res = fwrite( $this->f, $xml); if ( false === $res ) { throw new WP_Export_Exception( __( 'WP Export: error writing to export file.' ) ); } $this->current_file_size += strlen( $xml ); } private function start_new_file() { if ( $this->f ) { $this->close_current_file(); } $file_path = $this->next_file_path(); $this->f = fopen( $file_path, 'w' ); if ( !$this->f ) { throw new WP_Export_Exception( sprintf( __( 'WP Export: error opening %s for writing.' ), $file_path ) ); } do_action( 'wp_export_new_file', $file_path ); $this->current_file_size = 0; $this->write( $this->before_posts_xml ); } private function close_current_file() { if ( !$this->f ) { return; } $this->write( $this->after_posts_xml ); fclose( $this->f ); } private function next_file_name() { $next_file_name = sprintf( $this->filename_template, $this->next_file_number ); $this->next_file_number++; return $next_file_name; } private function next_file_path() { return untrailingslashit( $this->destination_directory ) . DIRECTORY_SEPARATOR . $this->next_file_name(); } } <?php // Used by `wp server` to route requests. namespace WP_CLI\Router; /** * This is a copy of WordPress's add_filter() function. * * We duplicate it because WordPress is not loaded yet. */ function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) { global $wp_filter, $merged_filters; $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority); $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args); unset( $merged_filters[ $tag ] ); return true; } /** * This is a copy of WordPress's _wp_filter_build_unique_id() function. * * We duplicate it because WordPress is not loaded yet. */ function _wp_filter_build_unique_id($tag, $function, $priority) { global $wp_filter; static $filter_id_count = 0; if ( is_string($function) ) return $function; if ( is_object($function) ) { // Closures are currently implemented as objects $function = array( $function, '' ); } else { $function = (array) $function; } if (is_object($function[0]) ) { // Object Class Calling if ( function_exists('spl_object_hash') ) { return spl_object_hash($function[0]) . $function[1]; } else { $obj_idx = get_class($function[0]).$function[1]; if ( !isset($function[0]->wp_filter_id) ) { if ( false === $priority ) return false; $obj_idx .= isset($wp_filter[$tag][$priority]) ? count((array)$wp_filter[$tag][$priority]) : $filter_id_count; $function[0]->wp_filter_id = $filter_id_count; ++$filter_id_count; } else { $obj_idx .= $function[0]->wp_filter_id; } return $obj_idx; } } else if ( is_string($function[0]) ) { // Static Calling return $function[0] . '::' . $function[1]; } } function _get_full_host( $url ) { $parsed_url = parse_url( $url ); $host = $parsed_url['host']; if ( isset( $parsed_url['port'] ) && $parsed_url['port'] != 80 ) $host .= ':' . $parsed_url['port']; return $host; } // We need to trick WordPress into using the URL set by `wp server`, especially on multisite. add_filter( 'option_home', function ( $url ) { $GLOBALS['_wp_cli_original_url'] = $url; return 'http://' . $_SERVER['HTTP_HOST']; }, 20 ); add_filter( 'option_siteurl', function ( $url ) { if ( !isset( $GLOBALS['_wp_cli_original_url'] ) ) get_option('home'); // trigger the option_home filter $home_url_host = _get_full_host( $GLOBALS['_wp_cli_original_url'] ); $site_url_host = _get_full_host( $url ); if ( $site_url_host == $home_url_host ) { $url = str_replace( $site_url_host, $_SERVER['HTTP_HOST'], $url ); } return $url; }, 20 ); $root = $_SERVER['DOCUMENT_ROOT']; $path = '/'. ltrim( parse_url( urldecode( $_SERVER['REQUEST_URI'] ) )['path'], '/' ); if ( file_exists( $root.$path ) ) { if ( is_dir( $root.$path ) && substr( $path, -1 ) !== '/' ) { header( "Location: $path/" ); exit; } if ( strpos( $path, '.php' ) !== false ) { chdir( dirname( $root.$path ) ); require_once $root.$path; } else { return false; } } else { chdir( $root ); require_once 'index.php'; } <?php // Utilities that depend on WordPress code. namespace WP_CLI\Utils; function wp_not_installed() { if ( !is_blog_installed() && !defined( 'WP_INSTALLING' ) ) { \WP_CLI::error( "The site you have requested is not installed.\n" . 'Run `wp core install`.' ); } } function wp_debug_mode() { if ( \WP_CLI::get_config( 'debug' ) ) { if ( !defined( 'WP_DEBUG' ) ) define( 'WP_DEBUG', true ); error_reporting( E_ALL & ~E_DEPRECATED & ~E_STRICT ); } else { \wp_debug_mode(); } // XDebug already sends errors to STDERR ini_set( 'display_errors', function_exists( 'xdebug_debug_zval' ) ? false : 'STDERR' ); } function replace_wp_die_handler() { \remove_filter( 'wp_die_handler', '_default_wp_die_handler' ); \add_filter( 'wp_die_handler', function() { return __NAMESPACE__ . '\\' . 'wp_die_handler'; } ); } function wp_die_handler( $message ) { if ( is_wp_error( $message ) ) { $message = $message->get_error_message(); } if ( preg_match( '|^\<h1>(.+?)</h1>|', $message, $matches ) ) { $message = $matches[1]; } $message = html_entity_decode( $message ); \WP_CLI::error( $message ); } function wp_redirect_handler( $url ) { \WP_CLI::warning( 'Some code is trying to do a URL redirect. Backtrace:' ); ob_start(); debug_print_backtrace(); fwrite( STDERR, ob_get_clean() ); return $url; } function maybe_require( $since, $path ) { if ( wp_version_compare( $since, '>=' ) ) { require $path; } } function get_upgrader( $class ) { if ( !class_exists( '\WP_Upgrader' ) ) require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; return new $class( new \WP_CLI\UpgraderSkin ); } /** * Converts a plugin basename back into a friendly slug. */ function get_plugin_name( $basename ) { if ( false === strpos( $basename, '/' ) ) $name = basename( $basename, '.php' ); else $name = dirname( $basename ); return $name; } function is_plugin_skipped( $file ) { $name = get_plugin_name( str_replace( WP_PLUGIN_DIR . '/', '', $file ) ); $skipped_plugins = \WP_CLI::get_runner()->config['skip-plugins']; if ( true === $skipped_plugins ) return true; if ( ! is_array( $skipped_plugins ) ) { $skipped_plugins = explode( ',', $skipped_plugins ); } return in_array( $name, array_filter( $skipped_plugins ) ); } function get_theme_name( $path ) { return basename( $path ); } function is_theme_skipped( $path ) { $name = get_theme_name( $path ); $skipped_themes = \WP_CLI::get_runner()->config['skip-themes']; if ( true === $skipped_themes ) return true; if ( ! is_array( $skipped_themes ) ) { $skipped_themes = explode( ',', $skipped_themes ); } return in_array( $name, array_filter( $skipped_themes ) ); } /** * Register the sidebar for unused widgets * Core does this in /wp-admin/widgets.php, which isn't helpful */ function wp_register_unused_sidebar() { register_sidebar(array( 'name' => __('Inactive Widgets'), 'id' => 'wp_inactive_widgets', 'class' => 'inactive-sidebar', 'description' => __( 'Drag widgets here to remove them from the sidebar but keep their settings.' ), 'before_widget' => '', 'after_widget' => '', 'before_title' => '', 'after_title' => '', )); } /** * Attempts to determine which object cache is being used. * * Note that the guesses made by this function are based on the WP_Object_Cache classes * that define the 3rd party object cache extension. Changes to those classes could render * problems with this function's ability to determine which object cache is being used. * * @return string */ function wp_get_cache_type() { global $_wp_using_ext_object_cache, $wp_object_cache; if ( ! empty( $_wp_using_ext_object_cache ) ) { // Test for Memcached PECL extension memcached object cache (https://github.com/tollmanz/wordpress-memcached-backend) if ( isset( $wp_object_cache->m ) && is_a( $wp_object_cache->m, 'Memcached' ) ) { $message = 'Memcached'; // Test for Memcache PECL extension memcached object cache (http://wordpress.org/extend/plugins/memcached/) } elseif ( isset( $wp_object_cache->mc ) ) { $is_memcache = true; foreach ( $wp_object_cache->mc as $bucket ) { if ( ! is_a( $bucket, 'Memcache' ) ) $is_memcache = false; } if ( $is_memcache ) $message = 'Memcache'; // Test for Xcache object cache (http://plugins.svn.wordpress.org/xcache/trunk/object-cache.php) } elseif ( is_a( $wp_object_cache, 'XCache_Object_Cache' ) ) { $message = 'Xcache'; // Test for WinCache object cache (http://wordpress.org/extend/plugins/wincache-object-cache-backend/) } elseif ( class_exists( 'WinCache_Object_Cache' ) ) { $message = 'WinCache'; // Test for APC object cache (http://wordpress.org/extend/plugins/apc/) } elseif ( class_exists( 'APC_Object_Cache' ) ) { $message = 'APC'; // Test for Redis Object Cache (https://github.com/alleyinteractive/wp-redis) } elseif ( isset( $wp_object_cache->redis ) && is_a( $wp_object_cache->redis, 'Redis' ) ) { $message = 'Redis'; } else { $message = 'Unknown'; } } else { $message = 'Default'; } return $message; } /** * Clear all of the caches for memory management */ function wp_clear_object_cache() { global $wpdb, $wp_object_cache; $wpdb->queries = array(); // or define( 'WP_IMPORTING', true ); if ( ! is_object( $wp_object_cache ) ) { return; } $wp_object_cache->group_ops = array(); $wp_object_cache->stats = array(); $wp_object_cache->memcache_debug = array(); $wp_object_cache->cache = array(); if ( is_callable( $wp_object_cache, '__remoteset' ) ) { $wp_object_cache->__remoteset(); // important } } /** * Get a set of tables in the database. * * Interprets common command-line options into a resolved set of table names. * * @param array $args Provided table names, or tables with wildcards. * @param array $assoc_args Optional flags for groups of tables (e.g. --network) * @return array $tables */ function wp_get_table_names( $args, $assoc_args = array() ) { global $wpdb; // Prioritize any supplied $args as tables if ( ! empty( $args ) ) { $new_tables = array(); $get_tables_for_glob = function( $glob ) { global $wpdb; static $all_tables = array(); if ( ! $all_tables ) { $all_tables = $wpdb->get_col( 'SHOW TABLES' ); } $tables = array(); foreach ( $all_tables as $table) { if ( fnmatch( $glob, $table ) ) { $tables[] = $table; } } return $tables; }; foreach( $args as $key => $table ) { if ( false !== strpos( $table, '*' ) || false !== strpos( $table, '?' ) ) { $expanded_tables = $get_tables_for_glob( $table ); if ( empty( $expanded_tables ) ) { \WP_CLI::error( "Couldn't find any tables matching: {$table}" ); } $new_tables = array_merge( $new_tables, $expanded_tables ); } else { $new_tables[] = $table; } } return $new_tables; } // Fall back to flag if no tables were passed $table_type = 'wordpress'; if ( get_flag_value( $assoc_args, 'network' ) ) { $table_type = 'network'; } if ( get_flag_value( $assoc_args, 'all-tables-with-prefix' ) ) { $table_type = 'all-tables-with-prefix'; } if ( get_flag_value( $assoc_args, 'all-tables' ) ) { $table_type = 'all-tables'; } $network = 'network' == $table_type; if ( 'all-tables' == $table_type ) { return $wpdb->get_col( 'SHOW TABLES' ); } $prefix = $network ? $wpdb->base_prefix : $wpdb->prefix; $matching_tables = $wpdb->get_col( $wpdb->prepare( "SHOW TABLES LIKE %s", $prefix . '%' ) ); if ( 'all-tables-with-prefix' == $table_type ) { return $matching_tables; } if ( $scope = get_flag_value( $assoc_args, 'scope' ) ) { return $wpdb->tables( $scope ); } $allowed_tables = array(); $allowed_table_types = array( 'tables', 'global_tables' ); if ( $network ) { $allowed_table_types[] = 'ms_global_tables'; } foreach( $allowed_table_types as $table_type ) { foreach( $wpdb->$table_type as $table ) { $allowed_tables[] = $prefix . $table; } } // Given our matching tables, also allow site-specific tables on the network foreach( $matching_tables as $key => $matched_table ) { if ( in_array( $matched_table, $allowed_tables ) ) { continue; } if ( $network ) { $valid_table = false; foreach( array_merge( $wpdb->tables, $wpdb->old_tables ) as $maybe_site_table ) { if ( preg_match( "#{$prefix}([\d]+)_{$maybe_site_table}#", $matched_table ) ) { $valid_table = true; } } if ( $valid_table ) { continue; } } unset( $matching_tables[ $key ] ); } return array_values( $matching_tables ); } <?php // Utilities that do NOT depend on WordPress code. namespace WP_CLI\Utils; use \Composer\Semver\Comparator; use \Composer\Semver\Semver; use \WP_CLI\Dispatcher; use \WP_CLI\Iterators\Transform; function inside_phar() { return 0 === strpos( WP_CLI_ROOT, 'phar://' ); } // Files that need to be read by external programs have to be extracted from the Phar archive. function extract_from_phar( $path ) { if ( ! inside_phar() ) { return $path; } $fname = basename( $path ); $tmp_path = get_temp_dir() . "wp-cli-$fname"; copy( $path, $tmp_path ); register_shutdown_function( function() use ( $tmp_path ) { @unlink( $tmp_path ); } ); return $tmp_path; } function load_dependencies() { if ( inside_phar() ) { require WP_CLI_ROOT . '/vendor/autoload.php'; return; } $has_autoload = false; foreach ( get_vendor_paths() as $vendor_path ) { if ( file_exists( $vendor_path . '/autoload.php' ) ) { require $vendor_path . '/autoload.php'; $has_autoload = true; break; } } if ( !$has_autoload ) { fputs( STDERR, "Internal error: Can't find Composer autoloader.\nTry running: composer install\n" ); exit(3); } } function get_vendor_paths() { $vendor_paths = array( WP_CLI_ROOT . '/../../../vendor', // part of a larger project / installed via Composer (preferred) WP_CLI_ROOT . '/vendor', // top-level project / installed as Git clone ); $maybe_composer_json = WP_CLI_ROOT . '/../../../composer.json'; if ( file_exists( $maybe_composer_json ) && is_readable( $maybe_composer_json ) ) { $composer = json_decode( file_get_contents( $maybe_composer_json ) ); if ( ! empty( $composer->{'vendor-dir'} ) ) { array_unshift( $vendor_paths, WP_CLI_ROOT . '/../../../' . $composer->{'vendor-dir'} ); } } return $vendor_paths; } // Using require() directly inside a class grants access to private methods to the loaded code function load_file( $path ) { require_once $path; } function load_command( $name ) { $path = WP_CLI_ROOT . "/php/commands/$name.php"; if ( is_readable( $path ) ) { include_once $path; } } function load_all_commands() { $cmd_dir = WP_CLI_ROOT . '/php/commands'; $iterator = new \DirectoryIterator( $cmd_dir ); foreach ( $iterator as $filename ) { if ( '.php' != substr( $filename, -4 ) ) continue; include_once "$cmd_dir/$filename"; } } /** * Like array_map(), except it returns a new iterator, instead of a modified array. * * Example: * * $arr = array('Football', 'Socker'); * * $it = iterator_map($arr, 'strtolower', function($val) { * return str_replace('foo', 'bar', $val); * }); * * foreach ( $it as $val ) { * var_dump($val); * } * * @param array|object Either a plain array or another iterator * @param callback The function to apply to an element * @return object An iterator that applies the given callback(s) */ function iterator_map( $it, $fn ) { if ( is_array( $it ) ) { $it = new \ArrayIterator( $it ); } if ( !method_exists( $it, 'add_transform' ) ) { $it = new Transform( $it ); } foreach ( array_slice( func_get_args(), 1 ) as $fn ) { $it->add_transform( $fn ); } return $it; } /** * Search for file by walking up the directory tree until the first file is found or until $stop_check($dir) returns true * @param string|array The files (or file) to search for * @param string|null The directory to start searching from; defaults to CWD * @param callable Function which is passed the current dir each time a directory level is traversed * @return null|string Null if the file was not found */ function find_file_upward( $files, $dir = null, $stop_check = null ) { $files = (array) $files; if ( is_null( $dir ) ) { $dir = getcwd(); } while ( @is_readable( $dir ) ) { // Stop walking up when the supplied callable returns true being passed the $dir if ( is_callable( $stop_check ) && call_user_func( $stop_check, $dir ) ) { return null; } foreach ( $files as $file ) { $path = $dir . DIRECTORY_SEPARATOR . $file; if ( file_exists( $path ) ) { return $path; } } $parent_dir = dirname( $dir ); if ( empty($parent_dir) || $parent_dir === $dir ) { break; } $dir = $parent_dir; } return null; } function is_path_absolute( $path ) { // Windows if ( isset($path[1]) && ':' === $path[1] ) return true; return $path[0] === '/'; } /** * Composes positional arguments into a command string. * * @param array * @return string */ function args_to_str( $args ) { return ' ' . implode( ' ', array_map( 'escapeshellarg', $args ) ); } /** * Composes associative arguments into a command string. * * @param array * @return string */ function assoc_args_to_str( $assoc_args ) { $str = ''; foreach ( $assoc_args as $key => $value ) { if ( true === $value ) $str .= " --$key"; else $str .= " --$key=" . escapeshellarg( $value ); } return $str; } /** * Given a template string and an arbitrary number of arguments, * returns the final command, with the parameters escaped. */ function esc_cmd( $cmd ) { if ( func_num_args() < 2 ) trigger_error( 'esc_cmd() requires at least two arguments.', E_USER_WARNING ); $args = func_get_args(); $cmd = array_shift( $args ); return vsprintf( $cmd, array_map( 'escapeshellarg', $args ) ); } function locate_wp_config() { static $path; if ( null === $path ) { if ( file_exists( ABSPATH . 'wp-config.php' ) ) $path = ABSPATH . 'wp-config.php'; elseif ( file_exists( ABSPATH . '../wp-config.php' ) && ! file_exists( ABSPATH . '/../wp-settings.php' ) ) $path = ABSPATH . '../wp-config.php'; else $path = false; if ( $path ) $path = realpath( $path ); } return $path; } function wp_version_compare( $since, $operator ) { return version_compare( str_replace( array( '-src' ), '', $GLOBALS['wp_version'] ), $since, $operator ); } /** * Output items in a table, JSON, CSV, ids, or the total count * * @param string $format Format to use: 'table', 'json', 'csv', 'ids', 'count' * @param array $items Data to output * @param array|string $fields Named fields for each item of data. Can be array or comma-separated list */ function format_items( $format, $items, $fields ) { $assoc_args = compact( 'format', 'fields' ); $formatter = new \WP_CLI\Formatter( $assoc_args ); $formatter->display_items( $items ); } /** * Write data as CSV to a given file. * * @param resource $fd File descriptor * @param array $rows Array of rows to output * @param array $headers List of CSV columns (optional) */ function write_csv( $fd, $rows, $headers = array() ) { if ( ! empty( $headers ) ) { fputcsv( $fd, $headers ); } foreach ( $rows as $row ) { if ( ! empty( $headers ) ) { $row = pick_fields( $row, $headers ); } fputcsv( $fd, array_values( $row ) ); } } /** * Pick fields from an associative array or object. * * @param array|object Associative array or object to pick fields from * @param array List of fields to pick * @return array */ function pick_fields( $item, $fields ) { $item = (object) $item; $values = array(); foreach ( $fields as $field ) { $values[ $field ] = isset( $item->$field ) ? $item->$field : null; } return $values; } /** * Launch system's $EDITOR to edit text * * @param str $content Text to edit (eg post content) * @return str|bool Edited text, if file is saved from editor * False, if no change to file */ function launch_editor_for_input( $input, $title = 'WP-CLI' ) { $tmpfile = wp_tempnam( $title ); if ( !$tmpfile ) \WP_CLI::error( 'Error creating temporary file.' ); $output = ''; file_put_contents( $tmpfile, $input ); $editor = getenv( 'EDITOR' ); if ( !$editor ) { if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) ) $editor = 'notepad'; else $editor = 'vi'; } $descriptorspec = array( STDIN, STDOUT, STDERR ); $process = proc_open( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes ); $r = proc_close( $process ); if ( $r ) { exit( $r ); } $output = file_get_contents( $tmpfile ); unlink( $tmpfile ); if ( $output === $input ) return false; return $output; } /** * @param string MySQL host string, as defined in wp-config.php * @return array */ function mysql_host_to_cli_args( $raw_host ) { $assoc_args = array(); $host_parts = explode( ':', $raw_host ); if ( count( $host_parts ) == 2 ) { list( $assoc_args['host'], $extra ) = $host_parts; $extra = trim( $extra ); if ( is_numeric( $extra ) ) { $assoc_args['port'] = intval( $extra ); $assoc_args['protocol'] = 'tcp'; } else if ( $extra !== '' ) { $assoc_args['socket'] = $extra; } } else { $assoc_args['host'] = $raw_host; } return $assoc_args; } function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) { if ( !$descriptors ) $descriptors = array( STDIN, STDOUT, STDERR ); if ( isset( $assoc_args['host'] ) ) { $assoc_args = array_merge( $assoc_args, mysql_host_to_cli_args( $assoc_args['host'] ) ); } $pass = $assoc_args['pass']; unset( $assoc_args['pass'] ); $old_pass = getenv( 'MYSQL_PWD' ); putenv( 'MYSQL_PWD=' . $pass ); $final_cmd = $cmd . assoc_args_to_str( $assoc_args ); $proc = proc_open( $final_cmd, $descriptors, $pipes ); if ( !$proc ) exit(1); $r = proc_close( $proc ); putenv( 'MYSQL_PWD=' . $old_pass ); if ( $r ) exit( $r ); } /** * Render PHP or other types of files using Mustache templates. * * IMPORTANT: Automatic HTML escaping is disabled! */ function mustache_render( $template_name, $data ) { if ( ! file_exists( $template_name ) ) $template_name = WP_CLI_ROOT . "/templates/$template_name"; $template = file_get_contents( $template_name ); $m = new \Mustache_Engine( array( 'escape' => function ( $val ) { return $val; } ) ); return $m->render( $template, $data ); } function make_progress_bar( $message, $count ) { if ( \cli\Shell::isPiped() ) return new \WP_CLI\NoOp; return new \cli\progress\Bar( $message, $count ); } function parse_url( $url ) { $url_parts = \parse_url( $url ); if ( !isset( $url_parts['scheme'] ) ) { $url_parts = parse_url( 'http://' . $url ); } return $url_parts; } /** * Check if we're running in a Windows environment (cmd.exe). */ function is_windows() { return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; } /** * Replace magic constants in some PHP source code. * * @param string $source The PHP code to manipulate. * @param string $path The path to use instead of the magic constants */ function replace_path_consts( $source, $path ) { $replacements = array( '__FILE__' => "'$path'", '__DIR__' => "'" . dirname( $path ) . "'" ); $old = array_keys( $replacements ); $new = array_values( $replacements ); return str_replace( $old, $new, $source ); } /** * Make a HTTP request to a remote URL * * @param string $method * @param string $url * @param array $headers * @param array $options * @return object */ function http_request( $method, $url, $data = null, $headers = array(), $options = array() ) { $cert_path = '/rmccue/requests/library/Requests/Transport/cacert.pem'; if ( inside_phar() ) { // cURL can't read Phar archives $options['verify'] = extract_from_phar( WP_CLI_ROOT . '/vendor' . $cert_path ); } else { foreach( get_vendor_paths() as $vendor_path ) { if ( file_exists( $vendor_path . $cert_path ) ) { $options['verify'] = $vendor_path . $cert_path; break; } } if ( empty( $options['verify'] ) ){ WP_CLI::error_log( "Cannot find SSL certificate." ); } } try { $request = \Requests::request( $url, $headers, $data, $method, $options ); return $request; } catch( \Requests_Exception $ex ) { // Handle SSL certificate issues gracefully \WP_CLI::warning( $ex->getMessage() ); $options['verify'] = false; try { return \Requests::request( $url, $headers, $data, $method, $options ); } catch( \Requests_Exception $ex ) { \WP_CLI::error( $ex->getMessage() ); } } } /** * Increments a version string using the "x.y.z-pre" format * * Can increment the major, minor or patch number by one * If $new_version == "same" the version string is not changed * If $new_version is not a known keyword, it will be used as the new version string directly * * @param string $current_version * @param string $new_version * @return string */ function increment_version( $current_version, $new_version ) { // split version assuming the format is x.y.z-pre $current_version = explode( '-', $current_version, 2 ); $current_version[0] = explode( '.', $current_version[0] ); switch ( $new_version ) { case 'same': // do nothing break; case 'patch': $current_version[0][2]++; $current_version = array( $current_version[0] ); // drop possible pre-release info break; case 'minor': $current_version[0][1]++; $current_version[0][2] = 0; $current_version = array( $current_version[0] ); // drop possible pre-release info break; case 'major': $current_version[0][0]++; $current_version[0][1] = 0; $current_version[0][2] = 0; $current_version = array( $current_version[0] ); // drop possible pre-release info break; default: // not a keyword $current_version = array( array( $new_version ) ); break; } // reconstruct version string $current_version[0] = implode( '.', $current_version[0] ); $current_version = implode( '-', $current_version ); return $current_version; } /** * Compare two version strings to get the named semantic version * * @param string $new_version * @param string $original_version * @return string $name 'major', 'minor', 'patch' */ function get_named_sem_ver( $new_version, $original_version ) { if ( ! Comparator::greaterThan( $new_version, $original_version ) ) { return ''; } $parts = explode( '-', $original_version ); list( $major, $minor, $patch ) = explode( '.', $parts[0] ); if ( Semver::satisfies( $new_version, "{$major}.{$minor}.x" ) ) { return 'patch'; } else if ( Semver::satisfies( $new_version, "{$major}.x.x" ) ) { return 'minor'; } else { return 'major'; } } /** * Return the flag value or, if it's not set, the $default value. * * @param array $args Arguments array. * @param string $flag Flag to get the value. * @param mixed $default Default value for the flag. Default: NULL * @return mixed */ function get_flag_value( $args, $flag, $default = null ) { return isset( $args[ $flag ] ) ? $args[ $flag ] : $default; } /** * Get the temp directory, and let the user know if it isn't writable. * * @return string */ function get_temp_dir() { static $temp = ''; $trailingslashit = function( $path ) { return rtrim( $path ) . '/'; }; if ( $temp ) return $trailingslashit( $temp ); if ( function_exists( 'sys_get_temp_dir' ) ) { $temp = sys_get_temp_dir(); } else if ( ini_get( 'upload_tmp_dir' ) ) { $temp = ini_get( 'upload_tmp_dir' ); } else { $temp = '/tmp/'; } if ( ! @is_writable( $temp ) ) { WP_CLI::warning( "Temp directory isn't writable: {$temp}" ); } return $trailingslashit( $temp ); } <?php // Can be used by plugins/themes to check if WP-CLI is running or not define( 'WP_CLI', true ); define( 'WP_CLI_VERSION', trim( file_get_contents( WP_CLI_ROOT . '/VERSION' ) ) ); define( 'WP_CLI_START_MICROTIME', microtime( true ) ); // Set common headers, to prevent warnings from plugins $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0'; $_SERVER['HTTP_USER_AGENT'] = ''; $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; include WP_CLI_ROOT . '/php/utils.php'; include WP_CLI_ROOT . '/php/dispatcher.php'; include WP_CLI_ROOT . '/php/class-wp-cli.php'; include WP_CLI_ROOT . '/php/class-wp-cli-command.php'; \WP_CLI\Utils\load_dependencies(); WP_CLI::get_runner()->start(); <?php /** * A modified version of wp-settings.php, tailored for CLI use. */ use \WP_CLI\Utils; /** * Stores the location of the WordPress directory of functions, classes, and core content. * * @since 1.0.0 */ define( 'WPINC', 'wp-includes' ); // Include files required for initialization. require( ABSPATH . WPINC . '/load.php' ); require( ABSPATH . WPINC . '/default-constants.php' ); /* * These can't be directly globalized in version.php. When updating, * we're including version.php from another install and don't want * these values to be overridden if already set. */ global $wp_version, $wp_db_version, $tinymce_version, $required_php_version, $required_mysql_version, $wp_local_package; require( ABSPATH . WPINC . '/version.php' ); /** * If not already configured, `$blog_id` will default to 1 in a single site * configuration. In multisite, it will be overridden by default in ms-settings.php. * * @global int $blog_id * @since 2.0.0 */ global $blog_id; // Set initial default constants including WP_MEMORY_LIMIT, WP_MAX_MEMORY_LIMIT, WP_DEBUG, WP_CONTENT_DIR and WP_CACHE. wp_initial_constants(); // Check for the required PHP version and for the MySQL extension or a database drop-in. wp_check_php_mysql_versions(); // Disable magic quotes at runtime. Magic quotes are added using wpdb later in wp-settings.php. @ini_set( 'magic_quotes_runtime', 0 ); @ini_set( 'magic_quotes_sybase', 0 ); // WordPress calculates offsets from UTC. date_default_timezone_set( 'UTC' ); // Turn register_globals off. wp_unregister_GLOBALS(); // Standardize $_SERVER variables across setups. wp_fix_server_vars(); // Start loading timer. timer_start(); // Check if we're in WP_DEBUG mode. Utils\wp_debug_mode(); // Define WP_LANG_DIR if not set. wp_set_lang_dir(); // Load early WordPress files. require( ABSPATH . WPINC . '/compat.php' ); require( ABSPATH . WPINC . '/functions.php' ); require( ABSPATH . WPINC . '/class-wp.php' ); require( ABSPATH . WPINC . '/class-wp-error.php' ); require( ABSPATH . WPINC . '/plugin.php' ); require( ABSPATH . WPINC . '/pomo/mo.php' ); // WP_CLI: Early hooks Utils\replace_wp_die_handler(); add_filter( 'wp_redirect', 'WP_CLI\\Utils\\wp_redirect_handler' ); if ( defined( 'WP_INSTALLING' ) && is_multisite() ) { $values = array( 'ms_files_rewriting' => null, 'active_sitewide_plugins' => array(), '_site_transient_update_core' => null, '_site_transient_update_themes' => null, '_site_transient_update_plugins' => null, 'WPLANG' => '', ); foreach ( $values as $key => $value ) { add_filter( "pre_site_option_$key", function () use ( $values, $key ) { return $values[ $key ]; } ); } unset( $values, $key, $value ); } // In a multisite install, die if unable to find site given in --url parameter if ( is_multisite() ) { add_action( 'ms_site_not_found', function( $current_site, $domain, $path ) { WP_CLI::error( "Site {$domain}{$path} not found." ); }, 10, 3 ); } // Include the wpdb class and, if present, a db.php database drop-in. require_wp_db(); // WP-CLI: Handle db error ourselves, instead of waiting for dead_db() global $wpdb; if ( !empty( $wpdb->error ) ) wp_die( $wpdb->error ); // Set the database table prefix and the format specifiers for database table columns. // @codingStandardsIgnoreStart $GLOBALS['table_prefix'] = $table_prefix; // @codingStandardsIgnoreEnd wp_set_wpdb_vars(); // Start the WordPress object cache, or an external object cache if the drop-in is present. wp_start_object_cache(); // WP-CLI: the APC cache is not available on the command-line, so bail, to prevent cache poisoning if ( $GLOBALS['_wp_using_ext_object_cache'] && class_exists( 'APC_Object_Cache' ) ) { WP_CLI::warning( 'Running WP-CLI while the APC object cache is activated can result in cache corruption.' ); WP_CLI::confirm( 'Given the consequences, do you wish to continue?' ); } // Attach the default filters. require( ABSPATH . WPINC . '/default-filters.php' ); // Initialize multisite if enabled. if ( is_multisite() ) { require( ABSPATH . WPINC . '/ms-blogs.php' ); require( ABSPATH . WPINC . '/ms-settings.php' ); } elseif ( ! defined( 'MULTISITE' ) ) { define( 'MULTISITE', false ); } register_shutdown_function( 'shutdown_action_hook' ); // Stop most of WordPress from being loaded if we just want the basics. if ( SHORTINIT ) return false; // Load the L10n library. require_once( ABSPATH . WPINC . '/l10n.php' ); // Run the installer if WordPress is not installed. Utils\wp_not_installed(); // Load most of WordPress. require( ABSPATH . WPINC . '/class-wp-walker.php' ); require( ABSPATH . WPINC . '/class-wp-ajax-response.php' ); require( ABSPATH . WPINC . '/formatting.php' ); require( ABSPATH . WPINC . '/capabilities.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-roles.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-role.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-user.php' ); require( ABSPATH . WPINC . '/query.php' ); Utils\maybe_require( '3.7-alpha-25139', ABSPATH . WPINC . '/date.php' ); require( ABSPATH . WPINC . '/theme.php' ); require( ABSPATH . WPINC . '/class-wp-theme.php' ); require( ABSPATH . WPINC . '/template.php' ); require( ABSPATH . WPINC . '/user.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-user-query.php' ); Utils\maybe_require( '4.0', ABSPATH . WPINC . '/session.php' ); require( ABSPATH . WPINC . '/meta.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-meta-query.php' ); require( ABSPATH . WPINC . '/general-template.php' ); require( ABSPATH . WPINC . '/link-template.php' ); require( ABSPATH . WPINC . '/author-template.php' ); require( ABSPATH . WPINC . '/post.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-walker-page.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-walker-page-dropdown.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-post.php' ); require( ABSPATH . WPINC . '/post-template.php' ); Utils\maybe_require( '3.6-alpha-23451', ABSPATH . WPINC . '/revision.php' ); Utils\maybe_require( '3.6-alpha-23451', ABSPATH . WPINC . '/post-formats.php' ); require( ABSPATH . WPINC . '/post-thumbnail-template.php' ); require( ABSPATH . WPINC . '/category.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-walker-category.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-walker-category-dropdown.php' ); require( ABSPATH . WPINC . '/category-template.php' ); require( ABSPATH . WPINC . '/comment.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-comment.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-comment-query.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-walker-comment.php' ); require( ABSPATH . WPINC . '/comment-template.php' ); require( ABSPATH . WPINC . '/rewrite.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-rewrite.php' ); require( ABSPATH . WPINC . '/feed.php' ); require( ABSPATH . WPINC . '/bookmark.php' ); require( ABSPATH . WPINC . '/bookmark-template.php' ); require( ABSPATH . WPINC . '/kses.php' ); require( ABSPATH . WPINC . '/cron.php' ); require( ABSPATH . WPINC . '/deprecated.php' ); require( ABSPATH . WPINC . '/script-loader.php' ); require( ABSPATH . WPINC . '/taxonomy.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-term.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-tax-query.php' ); require( ABSPATH . WPINC . '/update.php' ); require( ABSPATH . WPINC . '/canonical.php' ); require( ABSPATH . WPINC . '/shortcodes.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/embed.php' ); require( ABSPATH . WPINC . '/class-wp-embed.php' ); require( ABSPATH . WPINC . '/media.php' ); Utils\maybe_require( '4.4-alpha-34903', ABSPATH . WPINC . '/class-wp-oembed-controller.php' ); require( ABSPATH . WPINC . '/http.php' ); require_once( ABSPATH . WPINC . '/class-http.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-http-streams.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-http-curl.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-http-proxy.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-http-cookie.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-http-encoding.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-http-response.php' ); require( ABSPATH . WPINC . '/widgets.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-widget.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/class-wp-widget-factory.php' ); require( ABSPATH . WPINC . '/nav-menu.php' ); require( ABSPATH . WPINC . '/nav-menu-template.php' ); require( ABSPATH . WPINC . '/admin-bar.php' ); Utils\maybe_require( '4.4-alpha-34928', ABSPATH . WPINC . '/rest-api.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php' ); Utils\maybe_require( '4.4-beta4-35719', ABSPATH . WPINC . '/rest-api/class-wp-rest-request.php' ); // Load multisite-specific files. if ( is_multisite() ) { require( ABSPATH . WPINC . '/ms-functions.php' ); require( ABSPATH . WPINC . '/ms-default-filters.php' ); require( ABSPATH . WPINC . '/ms-deprecated.php' ); } // Define constants that rely on the API to obtain the default value. // Define must-use plugin directory constants, which may be overridden in the sunrise.php drop-in. wp_plugin_directory_constants(); $symlinked_plugins_supported = function_exists( 'wp_register_plugin_realpath' ); if ( $symlinked_plugins_supported ) { $GLOBALS['wp_plugin_paths'] = array(); } // Load must-use plugins. foreach ( wp_get_mu_plugins() as $mu_plugin ) { include_once( $mu_plugin ); } unset( $mu_plugin ); // Load network activated plugins. if ( is_multisite() ) { foreach( wp_get_active_network_plugins() as $network_plugin ) { if ( !Utils\is_plugin_skipped( $network_plugin ) ) { if ( $symlinked_plugins_supported ) wp_register_plugin_realpath( $network_plugin ); include_once( $network_plugin ); } } unset( $network_plugin ); } do_action( 'muplugins_loaded' ); if ( is_multisite() ) ms_cookie_constants( ); // Define constants after multisite is loaded. Cookie-related constants may be overridden in ms_network_cookies(). wp_cookie_constants( ); // Define and enforce our SSL constants wp_ssl_constants( ); // Don't create common globals, but we still need wp_is_mobile() and $pagenow // require( ABSPATH . WPINC . '/vars.php' ); $GLOBALS['pagenow'] = null; function wp_is_mobile() { return false; } // Make taxonomies and posts available to plugins and themes. // @plugin authors: warning: these get registered again on the init hook. create_initial_taxonomies(); create_initial_post_types(); // Register the default theme directory root register_theme_directory( get_theme_root() ); // Load active plugins. foreach ( wp_get_active_and_valid_plugins() as $plugin ) { if ( !Utils\is_plugin_skipped( $plugin ) ) { if ( $symlinked_plugins_supported ) wp_register_plugin_realpath( $plugin ); include_once( $plugin ); } } unset( $plugin, $symlinked_plugins_supported ); // Load pluggable functions. require( ABSPATH . WPINC . '/pluggable.php' ); require( ABSPATH . WPINC . '/pluggable-deprecated.php' ); // Set internal encoding. wp_set_internal_encoding(); // Run wp_cache_postload() if object cache is enabled and the function exists. if ( WP_CACHE && function_exists( 'wp_cache_postload' ) ) wp_cache_postload(); do_action( 'plugins_loaded' ); // Define constants which affect functionality if not already defined. wp_functionality_constants( ); // Add magic quotes and set up $_REQUEST ( $_GET + $_POST ) wp_magic_quotes(); do_action( 'sanitize_comment_cookies' ); /** * WordPress Query object * @global object $wp_the_query * @since 2.0.0 */ $GLOBALS['wp_the_query'] = new WP_Query(); /** * Holds the reference to @see $wp_the_query * Use this global for WordPress queries * @global object $wp_query * @since 1.5.0 */ $GLOBALS['wp_query'] = $GLOBALS['wp_the_query']; /** * Holds the WordPress Rewrite object for creating pretty URLs * @global object $wp_rewrite * @since 1.5.0 */ $GLOBALS['wp_rewrite'] = new WP_Rewrite(); /** * WordPress Object * @global object $wp * @since 2.0.0 */ $GLOBALS['wp'] = new WP(); /** * WordPress Widget Factory Object * @global object $wp_widget_factory * @since 2.8.0 */ $GLOBALS['wp_widget_factory'] = new WP_Widget_Factory(); /** * WordPress User Roles * @global object $wp_roles * @since 2.0.0 */ $GLOBALS['wp_roles'] = new WP_Roles(); do_action( 'setup_theme' ); // Define the template related constants. wp_templating_constants( ); // Load the default text localization domain. load_default_textdomain(); $locale = get_locale(); $locale_file = WP_LANG_DIR . "/$locale.php"; if ( ( 0 === validate_file( $locale ) ) && is_readable( $locale_file ) ) require( $locale_file ); unset( $locale_file ); // Pull in locale data after loading text domain. require_once( ABSPATH . WPINC . '/locale.php' ); /** * WordPress Locale object for loading locale domain date and various strings. * @global object $wp_locale * @since 2.1.0 */ $GLOBALS['wp_locale'] = new WP_Locale(); // Load the functions for the active theme, for both parent and child theme if applicable. global $pagenow; if ( ! defined( 'WP_INSTALLING' ) || 'wp-activate.php' === $pagenow ) { if ( !Utils\is_theme_skipped( TEMPLATEPATH ) ) { if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) ) include( STYLESHEETPATH . '/functions.php' ); if ( file_exists( TEMPLATEPATH . '/functions.php' ) ) include( TEMPLATEPATH . '/functions.php' ); } } do_action( 'after_setup_theme' ); // Set up current user. $GLOBALS['wp']->init(); /** * Most of WP is loaded at this stage, and the user is authenticated. WP continues * to load on the init hook that follows (e.g. widgets), and many plugins instantiate * themselves on it for all sorts of reasons (e.g. they need a user, a taxonomy, etc.). * * If you wish to plug an action once WP is loaded, use the wp_loaded hook below. */ do_action( 'init' ); // Check site status # if ( is_multisite() ) { // WP-CLI if ( is_multisite() && !defined('WP_INSTALLING') ) { if ( true !== ( $file = ms_site_check() ) ) { require( $file ); die(); } unset($file); } /** * This hook is fired once WP, all plugins, and the theme are fully loaded and instantiated. * * AJAX requests should use wp-admin/admin-ajax.php. admin-ajax.php can handle requests for * users not logged in. * * @link http://codex.wordpress.org/AJAX_in_Plugins * * @since 3.0.0 */ do_action('wp_loaded'); <?php use Behat\Behat\Context\ClosuredContextInterface, Behat\Behat\Context\TranslatedContextInterface, Behat\Behat\Context\BehatContext, Behat\Behat\Event\SuiteEvent; use \WP_CLI\Process; use \WP_CLI\Utils; // Inside a community package if ( file_exists( __DIR__ . '/utils.php' ) ) { require_once __DIR__ . '/utils.php'; require_once __DIR__ . '/Process.php'; // Inside WP-CLI } else { require_once __DIR__ . '/../../php/utils.php'; require_once __DIR__ . '/../../php/WP_CLI/Process.php'; } /** * Features context. */ class FeatureContext extends BehatContext implements ClosuredContextInterface { private static $cache_dir, $suite_cache_dir; private static $db_settings = array( 'dbname' => 'wp_cli_test', 'dbuser' => 'wp_cli_test', 'dbpass' => 'password1', 'dbhost' => '127.0.0.1', ); private $running_procs = array(); public $variables = array(); /** * Get the environment variables required for launched `wp` processes * @beforeSuite */ private static function get_process_env_variables() { // Ensure we're using the expected `wp` binary $bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ?: realpath( __DIR__ . "/../../bin" ); $env = array( 'PATH' => $bin_dir . ':' . getenv( 'PATH' ), 'BEHAT_RUN' => 1 ); if ( $config_path = getenv( 'WP_CLI_CONFIG_PATH' ) ) { $env['WP_CLI_CONFIG_PATH'] = $config_path; } return $env; } // We cache the results of `wp core download` to improve test performance // Ideally, we'd cache at the HTTP layer for more reliable tests private static function cache_wp_files() { self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test core-download-cache'; if ( is_readable( self::$cache_dir . '/wp-config-sample.php' ) ) return; $cmd = Utils\esc_cmd( 'wp core download --force --path=%s', self::$cache_dir ); Process::create( $cmd, null, self::get_process_env_variables() )->run_check(); } /** * @BeforeSuite */ public static function prepare( SuiteEvent $event ) { self::cache_wp_files(); } /** * @AfterSuite */ public static function afterSuite( SuiteEvent $event ) { if ( self::$suite_cache_dir ) { Process::create( Utils\esc_cmd( 'rm -r %s', self::$suite_cache_dir ), null, self::get_process_env_variables() )->run(); } } /** * @BeforeScenario */ public function beforeScenario( $event ) { $this->variables['SRC_DIR'] = realpath( __DIR__ . '/../..' ); } /** * @AfterScenario */ public function afterScenario( $event ) { if ( isset( $this->variables['RUN_DIR'] ) ) { // remove altered WP install, unless there's an error if ( $event->getResult() < 4 ) { $this->proc( Utils\esc_cmd( 'rm -r %s', $this->variables['RUN_DIR'] ) )->run(); } } foreach ( $this->running_procs as $proc ) { self::terminate_proc( $proc ); } } /** * Terminate a process and any of its children. */ private static function terminate_proc( $proc ) { $status = proc_get_status( $proc ); $master_pid = $status['pid']; $output = `ps -o ppid,pid,command | grep ^$master_pid`; foreach ( explode( "\n", $output ) as $line ) { if ( preg_match( '/^(\d+)\s+(\d+)/', $line, $matches ) ) { $parent = $matches[1]; $child = $matches[2]; if ( $parent == $master_pid ) { if ( ! posix_kill( $child, 9 ) ) { throw new RuntimeException( posix_strerror( posix_get_last_error() ) ); } } } } posix_kill( $master_pid, 9 ); } public static function create_cache_dir() { self::$suite_cache_dir = sys_get_temp_dir() . '/' . uniqid( "wp-cli-test-suite-cache-", TRUE ); mkdir( self::$suite_cache_dir ); return self::$suite_cache_dir; } /** * Initializes context. * Every scenario gets it's own context object. * * @param array $parameters context parameters (set them up through behat.yml) */ public function __construct( array $parameters ) { $this->drop_db(); $this->set_cache_dir(); $this->variables['CORE_CONFIG_SETTINGS'] = Utils\assoc_args_to_str( self::$db_settings ); } public function getStepDefinitionResources() { return glob( __DIR__ . '/../steps/*.php' ); } public function getHookDefinitionResources() { return array(); } public function replace_variables( $str ) { return preg_replace_callback( '/\{([A-Z_]+)\}/', array( $this, '_replace_var' ), $str ); } private function _replace_var( $matches ) { $cmd = $matches[0]; foreach ( array_slice( $matches, 1 ) as $key ) { $cmd = str_replace( '{' . $key . '}', $this->variables[ $key ], $cmd ); } return $cmd; } public function create_run_dir() { if ( !isset( $this->variables['RUN_DIR'] ) ) { $this->variables['RUN_DIR'] = sys_get_temp_dir() . '/' . uniqid( "wp-cli-test-run-", TRUE ); mkdir( $this->variables['RUN_DIR'] ); } } public function build_phar( $version = 'same' ) { $this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/' . uniqid( "wp-cli-build-", TRUE ) . '.phar'; $this->proc( Utils\esc_cmd( 'php -dphar.readonly=0 %1$s %2$s --version=%3$s && chmod +x %2$s', __DIR__ . '/../../utils/make-phar.php', $this->variables['PHAR_PATH'], $version ) )->run_check(); } private function set_cache_dir() { $path = sys_get_temp_dir() . '/wp-cli-test-cache'; $this->proc( Utils\esc_cmd( 'mkdir -p %s', $path ) )->run_check(); $this->variables['CACHE_DIR'] = $path; } private static function run_sql( $sql ) { Utils\run_mysql_command( 'mysql --no-defaults', array( 'execute' => $sql, 'host' => self::$db_settings['dbhost'], 'user' => self::$db_settings['dbuser'], 'pass' => self::$db_settings['dbpass'], ) ); } public function create_db() { $dbname = self::$db_settings['dbname']; self::run_sql( "CREATE DATABASE IF NOT EXISTS $dbname" ); } public function drop_db() { $dbname = self::$db_settings['dbname']; self::run_sql( "DROP DATABASE IF EXISTS $dbname" ); } public function proc( $command, $assoc_args = array(), $path = '' ) { if ( !empty( $assoc_args ) ) $command .= Utils\assoc_args_to_str( $assoc_args ); $env = self::get_process_env_variables(); if ( isset( $this->variables['SUITE_CACHE_DIR'] ) ) { $env['WP_CLI_CACHE_DIR'] = $this->variables['SUITE_CACHE_DIR']; } if ( isset( $this->variables['RUN_DIR'] ) ) { $cwd = "{$this->variables['RUN_DIR']}/{$path}"; } else { $cwd = null; } return Process::create( $command, $cwd, $env ); } /** * Start a background process. Will automatically be closed when the tests finish. */ public function background_proc( $cmd ) { $descriptors = array( 0 => STDIN, 1 => array( 'pipe', 'w' ), 2 => array( 'pipe', 'w' ), ); $proc = proc_open( $cmd, $descriptors, $pipes, $this->variables['RUN_DIR'], self::get_process_env_variables() ); sleep(1); $status = proc_get_status( $proc ); if ( !$status['running'] ) { throw new RuntimeException( stream_get_contents( $pipes[2] ) ); } else { $this->running_procs[] = $proc; } } public function move_files( $src, $dest ) { rename( $this->variables['RUN_DIR'] . "/$src", $this->variables['RUN_DIR'] . "/$dest" ); } public function add_line_to_wp_config( &$wp_config_code, $line ) { $token = "/* That's all, stop editing!"; $wp_config_code = str_replace( $token, "$line\n\n$token", $wp_config_code ); } public function download_wp( $subdir = '' ) { $dest_dir = $this->variables['RUN_DIR'] . "/$subdir"; if ( $subdir ) { mkdir( $dest_dir ); } $this->proc( Utils\esc_cmd( "cp -r %s/* %s", self::$cache_dir, $dest_dir ) )->run_check(); // disable emailing mkdir( $dest_dir . '/wp-content/mu-plugins' ); copy( __DIR__ . '/../extra/no-mail.php', $dest_dir . '/wp-content/mu-plugins/no-mail.php' ); } public function create_config( $subdir = '' ) { $params = self::$db_settings; $params['dbprefix'] = $subdir ?: 'wp_'; $params['skip-salts'] = true; $this->proc( 'wp core config', $params, $subdir )->run_check(); } public function install_wp( $subdir = '' ) { $this->create_db(); $this->create_run_dir(); $this->download_wp( $subdir ); $this->create_config( $subdir ); $install_args = array( 'url' => 'http://example.com', 'title' => 'WP CLI Site', 'admin_user' => 'admin', 'admin_email' => 'admin@example.com', 'admin_password' => 'password1' ); $this->proc( 'wp core install', $install_args, $subdir )->run_check(); } } <?php // Utility functions used by Behat steps function assertEquals( $expected, $actual ) { if ( $expected != $actual ) { throw new Exception( "Actual value: " . var_export( $actual, true ) ); } } function assertNumeric( $actual ) { if ( !is_numeric( $actual ) ) { throw new Exception( "Actual value: " . var_export( $actual, true ) ); } } function assertNotNumeric( $actual ) { if ( is_numeric( $actual ) ) { throw new Exception( "Actual value: " . var_export( $actual, true ) ); } } function checkString( $output, $expected, $action, $message = false ) { switch ( $action ) { case 'be': $r = $expected === rtrim( $output, "\n" ); break; case 'contain': $r = false !== strpos( $output, $expected ); break; case 'not contain': $r = false === strpos( $output, $expected ); break; default: throw new Behat\Behat\Exception\PendingException(); } if ( !$r ) { if ( false === $message ) $message = $output; throw new Exception( $message ); } } function compareTables( $expected_rows, $actual_rows, $output ) { // the first row is the header and must be present if ( $expected_rows[0] != $actual_rows[0] ) { throw new \Exception( $output ); } unset( $actual_rows[0] ); unset( $expected_rows[0] ); $missing_rows = array_diff( $expected_rows, $actual_rows ); if ( !empty( $missing_rows ) ) { throw new \Exception( $output ); } } function compareContents( $expected, $actual ) { if ( gettype( $expected ) != gettype( $actual ) ) { return false; } if ( is_object( $expected ) ) { foreach ( get_object_vars( $expected ) as $name => $value ) { if ( ! compareContents( $value, $actual->$name ) ) return false; } } else if ( is_array( $expected ) ) { foreach ( $expected as $key => $value ) { if ( ! compareContents( $value, $actual[$key] ) ) return false; } } else { return $expected === $actual; } return true; } /** * Compare two strings containing JSON to ensure that @a $actualJson contains at * least what the JSON string @a $expectedJson contains. * * @return whether or not @a $actualJson contains @a $expectedJson * @retval true @a $actualJson contains @a $expectedJson * @retval false @a $actualJson does not contain @a $expectedJson * * @param[in] $actualJson the JSON string to be tested * @param[in] $expectedJson the expected JSON string * * Examples: * expected: {'a':1,'array':[1,3,5]} * * 1 ) * actual: {'a':1,'b':2,'c':3,'array':[1,2,3,4,5]} * return: true * * 2 ) * actual: {'b':2,'c':3,'array':[1,2,3,4,5]} * return: false * element 'a' is missing from the root object * * 3 ) * actual: {'a':0,'b':2,'c':3,'array':[1,2,3,4,5]} * return: false * the value of element 'a' is not 1 * * 4 ) * actual: {'a':1,'b':2,'c':3,'array':[1,2,4,5]} * return: false * the contents of 'array' does not include 3 */ function checkThatJsonStringContainsJsonString( $actualJson, $expectedJson ) { $actualValue = json_decode( $actualJson ); $expectedValue = json_decode( $expectedJson ); if ( !$actualValue ) { return false; } return compareContents( $expectedValue, $actualValue ); } /** * Compare two strings to confirm $actualCSV contains $expectedCSV * Both strings are expected to have headers for their CSVs. * $actualCSV must match all data rows in $expectedCSV * * @param string A CSV string * @param array A nested array of values * @return bool Whether $actualCSV contains $expectedCSV */ function checkThatCsvStringContainsValues( $actualCSV, $expectedCSV ) { $actualCSV = array_map( 'str_getcsv', explode( PHP_EOL, $actualCSV ) ); if ( empty( $actualCSV ) ) return false; // Each sample must have headers $actualHeaders = array_values( array_shift( $actualCSV ) ); $expectedHeaders = array_values( array_shift( $expectedCSV ) ); // Each expectedCSV must exist somewhere in actualCSV in the proper column $expectedResult = 0; foreach ( $expectedCSV as $expected_row ) { $expected_row = array_combine( $expectedHeaders, $expected_row ); foreach ( $actualCSV as $actual_row ) { if ( count( $actualHeaders ) != count( $actual_row ) ) continue; $actual_row = array_intersect_key( array_combine( $actualHeaders, $actual_row ), $expected_row ); if ( $actual_row == $expected_row ) $expectedResult++; } } return $expectedResult >= count( $expectedCSV ); } <?php function wp_mail() { // do nothing } <?php use Behat\Gherkin\Node\PyStringNode, Behat\Gherkin\Node\TableNode, WP_CLI\Process; $steps->Given( '/^an empty directory$/', function ( $world ) { $world->create_run_dir(); } ); $steps->Given( '/^an empty cache/', function ( $world ) { $world->variables['SUITE_CACHE_DIR'] = FeatureContext::create_cache_dir(); } ); $steps->Given( '/^an? ([^\s]+) file:$/', function ( $world, $path, PyStringNode $content ) { $content = (string) $content . "\n"; $full_path = $world->variables['RUN_DIR'] . "/$path"; Process::create( \WP_CLI\utils\esc_cmd( 'mkdir -p %s', dirname( $full_path ) ) )->run_check(); file_put_contents( $full_path, $content ); } ); $steps->Given( '/^WP files$/', function ( $world ) { $world->download_wp(); } ); $steps->Given( '/^wp-config\.php$/', function ( $world ) { $world->create_config(); } ); $steps->Given( '/^a database$/', function ( $world ) { $world->create_db(); } ); $steps->Given( '/^a WP install$/', function ( $world ) { $world->install_wp(); } ); $steps->Given( "/^a WP install in '([^\s]+)'$/", function ( $world, $subdir ) { $world->install_wp( $subdir ); } ); $steps->Given( '/^a WP multisite (subdirectory|subdomain)?\s?install$/', function ( $world, $type = 'subdirectory' ) { $world->install_wp(); $subdomains = ! empty( $type ) && 'subdomain' === $type ? 1 : 0; $world->proc( 'wp core install-network', array( 'title' => 'WP CLI Network', 'subdomains' => $subdomains ) )->run_check(); } ); $steps->Given( '/^these installed and active plugins:$/', function( $world, $stream ) { $plugins = implode( ' ', array_map( 'trim', explode( PHP_EOL, (string)$stream ) ) ); $world->proc( "wp plugin install $plugins --activate" )->run_check(); } ); $steps->Given( '/^a custom wp-content directory$/', function ( $world ) { $wp_config_path = $world->variables['RUN_DIR'] . "/wp-config.php"; $wp_config_code = file_get_contents( $wp_config_path ); $world->move_files( 'wp-content', 'my-content' ); $world->add_line_to_wp_config( $wp_config_code, "define( 'WP_CONTENT_DIR', dirname(__FILE__) . '/my-content' );" ); $world->move_files( 'my-content/plugins', 'my-plugins' ); $world->add_line_to_wp_config( $wp_config_code, "define( 'WP_PLUGIN_DIR', __DIR__ . '/my-plugins' );" ); file_put_contents( $wp_config_path, $wp_config_code ); } ); $steps->Given( '/^download:$/', function ( $world, TableNode $table ) { foreach ( $table->getHash() as $row ) { $path = $world->replace_variables( $row['path'] ); if ( file_exists( $path ) ) { // assume it's the same file and skip re-download continue; } Process::create( \WP_CLI\Utils\esc_cmd( 'curl -sSL %s > %s', $row['url'], $path ) )->run_check(); } } ); $steps->Given( '/^save (STDOUT|STDERR) ([\'].+[^\'])?as \{(\w+)\}$/', function ( $world, $stream, $output_filter, $key ) { $stream = strtolower( $stream ); if ( $output_filter ) { $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/'; if ( false !== preg_match( $output_filter, $world->result->$stream, $matches ) ) $output = array_pop( $matches ); else $output = ''; } else { $output = $world->result->$stream; } $world->variables[ $key ] = trim( $output, "\n" ); } ); $steps->Given( '/^a new Phar(?: with version "([^"]+)")$/', function ( $world, $version ) { $world->build_phar( $version ); } ); $steps->Given( '/^save the (.+) file ([\'].+[^\'])?as \{(\w+)\}$/', function ( $world, $filepath, $output_filter, $key ) { $full_file = file_get_contents( $world->replace_variables( $filepath ) ); if ( $output_filter ) { $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/'; if ( false !== preg_match( $output_filter, $full_file, $matches ) ) $output = array_pop( $matches ); else $output = ''; } else { $output = $full_file; } $world->variables[ $key ] = trim( $output, "\n" ); } ); $steps->Given('/^a misconfigured WP_CONTENT_DIR constant directory$/', function($world) { $wp_config_path = $world->variables['RUN_DIR'] . "/wp-config.php"; $wp_config_code = file_get_contents( $wp_config_path ); $world->add_line_to_wp_config( $wp_config_code, "define( 'WP_CONTENT_DIR', '' );" ); file_put_contents( $wp_config_path, $wp_config_code ); } );<?php use Behat\Gherkin\Node\PyStringNode, Behat\Gherkin\Node\TableNode; $steps->Then( '/^the return code should be (\d+)$/', function ( $world, $return_code ) { if ( $return_code != $world->result->return_code ) { throw new RuntimeException( $world->result ); } } ); $steps->Then( '/^(STDOUT|STDERR) should (be|contain|not contain):$/', function ( $world, $stream, $action, PyStringNode $expected ) { $stream = strtolower( $stream ); $expected = $world->replace_variables( (string) $expected ); checkString( $world->result->$stream, $expected, $action, $world->result ); } ); $steps->Then( '/^(STDOUT|STDERR) should be a number$/', function ( $world, $stream ) { $stream = strtolower( $stream ); assertNumeric( trim( $world->result->$stream, "\n" ) ); } ); $steps->Then( '/^(STDOUT|STDERR) should not be a number$/', function ( $world, $stream ) { $stream = strtolower( $stream ); assertNotNumeric( trim( $world->result->$stream, "\n" ) ); } ); $steps->Then( '/^STDOUT should be a table containing rows:$/', function ( $world, TableNode $expected ) { $output = $world->result->stdout; $actual_rows = explode( "\n", rtrim( $output, "\n" ) ); $expected_rows = array(); foreach ( $expected->getRows() as $row ) { $expected_rows[] = $world->replace_variables( implode( "\t", $row ) ); } compareTables( $expected_rows, $actual_rows, $output ); } ); $steps->Then( '/^STDOUT should end with a table containing rows:$/', function ( $world, TableNode $expected ) { $output = $world->result->stdout; $actual_rows = explode( "\n", rtrim( $output, "\n" ) ); $expected_rows = array(); foreach ( $expected->getRows() as $row ) { $expected_rows[] = $world->replace_variables( implode( "\t", $row ) ); } $start = array_search( $expected_rows[0], $actual_rows ); if ( false === $start ) throw new \Exception( $world->result ); compareTables( $expected_rows, array_slice( $actual_rows, $start ), $output ); } ); $steps->Then( '/^STDOUT should be JSON containing:$/', function ( $world, PyStringNode $expected ) { $output = $world->result->stdout; $expected = $world->replace_variables( (string) $expected ); if ( !checkThatJsonStringContainsJsonString( $output, $expected ) ) { throw new \Exception( $world->result ); } }); $steps->Then( '/^STDOUT should be a JSON array containing:$/', function ( $world, PyStringNode $expected ) { $output = $world->result->stdout; $expected = $world->replace_variables( (string) $expected ); $actualValues = json_decode( $output ); $expectedValues = json_decode( $expected ); $missing = array_diff( $expectedValues, $actualValues ); if ( !empty( $missing ) ) { throw new \Exception( $world->result ); } }); $steps->Then( '/^STDOUT should be CSV containing:$/', function ( $world, TableNode $expected ) { $output = $world->result->stdout; $expected_rows = $expected->getRows(); foreach ( $expected as &$row ) { foreach ( $row as &$value ) { $value = $world->replace_variables( $value ); } } if ( ! checkThatCsvStringContainsValues( $output, $expected_rows ) ) throw new \Exception( $world->result ); } ); $steps->Then( '/^(STDOUT|STDERR) should be empty$/', function ( $world, $stream ) { $stream = strtolower( $stream ); if ( !empty( $world->result->$stream ) ) { throw new \Exception( $world->result ); } } ); $steps->Then( '/^(STDOUT|STDERR) should not be empty$/', function ( $world, $stream ) { $stream = strtolower( $stream ); if ( '' === rtrim( $world->result->$stream, "\n" ) ) { throw new Exception( $world->result ); } } ); $steps->Then( '/^the (.+) (file|directory) should (exist|not exist|be:|contain:|not contain:)$/', function ( $world, $path, $type, $action, $expected = null ) { $path = $world->replace_variables( $path ); // If it's a relative path, make it relative to the current test dir if ( '/' !== $path[0] ) $path = $world->variables['RUN_DIR'] . "/$path"; if ( 'file' == $type ) { $test = 'file_exists'; } else if ( 'directory' == $type ) { $test = 'is_dir'; } switch ( $action ) { case 'exist': if ( ! $test( $path ) ) { throw new Exception( $world->result ); } break; case 'not exist': if ( $test( $path ) ) { throw new Exception( $world->result ); } break; default: if ( ! $test( $path ) ) { throw new Exception( "$path doesn't exist." ); } $action = substr( $action, 0, -1 ); $expected = $world->replace_variables( (string) $expected ); if ( 'file' == $type ) { $contents = file_get_contents( $path ); } else if ( 'directory' == $type ) { $files = glob( rtrim( $path, '/' ) . '/*' ); foreach( $files as &$file ) { $file = str_replace( $path . '/', '', $file ); } $contents = implode( PHP_EOL, $files ); } checkString( $contents, $expected, $action ); } } ); <?php use Behat\Gherkin\Node\PyStringNode, Behat\Gherkin\Node\TableNode, WP_CLI\Process; function invoke_proc( $proc, $mode ) { $map = array( 'run' => 'run_check', 'try' => 'run' ); $method = $map[ $mode ]; return $proc->$method(); } $steps->When( '/^I launch in the background `([^`]+)`$/', function ( $world, $cmd ) { $world->background_proc( $cmd ); } ); $steps->When( '/^I (run|try) `([^`]+)`$/', function ( $world, $mode, $cmd ) { $cmd = $world->replace_variables( $cmd ); $world->result = invoke_proc( $world->proc( $cmd ), $mode ); } ); $steps->When( "/^I (run|try) `([^`]+)` from '([^\s]+)'$/", function ( $world, $mode, $cmd, $subdir ) { $cmd = $world->replace_variables( $cmd ); $world->result = invoke_proc( $world->proc( $cmd, array(), $subdir ), $mode ); } ); $steps->When( '/^I (run|try) the previous command again$/', function ( $world, $mode ) { if ( !isset( $world->result ) ) throw new \Exception( 'No previous command.' ); $proc = Process::create( $world->result->command, $world->result->cwd, $world->result->env ); $world->result = invoke_proc( $proc, $mode ); } ); <?php /** * An example application using php-cli-tools and Buzz */ require_once __DIR__ . '/vendor/autoload.php'; define('BUZZ_PATH', realpath('../Buzz')); define('SCRIPT_NAME', array_shift($argv)); require_once BUZZ_PATH . '/lib/Buzz/ClassLoader.php'; Buzz\ClassLoader::register(); class HttpConsole { protected $_host; protected $_prompt; public function __construct($host) { $this->_host = 'http://' . $host; $this->_prompt = '%K' . $this->_host . '%n/%K>%n '; } public function handleRequest($type, $path) { $request = new Buzz\Message\Request($type, $path, $this->_host); $response = new Buzz\Message\Response; $client = new Buzz\Client\FileGetContents(); $client->send($request, $response); // Display headers foreach ($response->getHeaders() as $i => $header) { if ($i == 0) { \cli\line('%G{:header}%n', compact('header')); continue; } list($key, $value) = explode(': ', $header, 2); \cli\line('%W{:key}%n: {:value}', compact('key', 'value')); } \cli\line("\n"); print $response->getContent() . "\n"; switch ($type) { } } public function run() { while (true) { $cmd = \cli\prompt($this->_prompt, false, null); if (preg_match('/^(HEAD|GET|POST|PUT|DELETE) (\S+)$/', $cmd, $matches)) { $this->handleRequest($matches[1], $matches[2]); continue; } if ($cmd == '\q') { break; } } } } try { $console = new HttpConsole(array_shift($argv) ?: '127.0.0.1:80'); $console->run(); } catch (\Exception $e) { \cli\err("\n\n%R" . $e->getMessage() . "%n\n"); } ?> <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; use cli\arguments\Argument; use cli\arguments\HelpScreen; use cli\arguments\InvalidArguments; use cli\arguments\Lexer; /** * Parses command line arguments. */ class Arguments implements \ArrayAccess { protected $_flags = array(); protected $_options = array(); protected $_strict = false; protected $_input = array(); protected $_invalid = array(); protected $_parsed; protected $_lexer; /** * Initializes the argument parser. If you wish to change the default behaviour * you may pass an array of options as the first argument. Valid options are * `'help'` and `'strict'`, each a boolean. * * `'help'` is `true` by default, `'strict'` is false by default. * * @param array $options An array of options for this parser. */ public function __construct($options = array()) { $options += array( 'strict' => false, 'input' => array_slice($_SERVER['argv'], 1) ); $this->_input = $options['input']; $this->setStrict($options['strict']); if (isset($options['flags'])) { $this->addFlags($options['flags']); } if (isset($options['options'])) { $this->addOptions($options['options']); } } /** * Get the list of arguments found by the defined definitions. * * @return array */ public function getArguments() { if (!isset($this->_parsed)) { $this->parse(); } return $this->_parsed; } public function getHelpScreen() { return new HelpScreen($this); } /** * Encodes the parsed arguments as JSON. * * @return string */ public function asJSON() { return json_encode($this->_parsed); } /** * Returns true if a given argument was parsed. * * @param mixed $offset An Argument object or the name of the argument. * @return bool */ public function offsetExists($offset) { if ($offset instanceOf Argument) { $offset = $offset->key; } return array_key_exists($offset, $this->_parsed); } /** * Get the parsed argument's value. * * @param mixed $offset An Argument object or the name of the argument. * @return mixed */ public function offsetGet($offset) { if ($offset instanceOf Argument) { $offset = $offset->key; } if (isset($this->_parsed[$offset])) { return $this->_parsed[$offset]; } } /** * Sets the value of a parsed argument. * * @param mixed $offset An Argument object or the name of the argument. * @param mixed $value The value to set */ public function offsetSet($offset, $value) { if ($offset instanceOf Argument) { $offset = $offset->key; } $this->_parsed[$offset] = $value; } /** * Unset a parsed argument. * * @param mixed $offset An Argument object or the name of the argument. */ public function offsetUnset($offset) { if ($offset instanceOf Argument) { $offset = $offset->key; } unset($this->_parsed[$offset]); } /** * Adds a flag (boolean argument) to the argument list. * * @param mixed $flag A string representing the flag, or an array of strings. * @param array $settings An array of settings for this flag. * @setting string description A description to be shown in --help. * @setting bool default The default value for this flag. * @setting bool stackable Whether the flag is repeatable to increase the value. * @setting array aliases Other ways to trigger this flag. * @return $this */ public function addFlag($flag, $settings = array()) { if (is_string($settings)) { $settings = array('description' => $settings); } if (is_array($flag)) { $settings['aliases'] = $flag; $flag = array_shift($settings['aliases']); } if (isset($this->_flags[$flag])) { $this->_warn('flag already exists: ' . $flag); return $this; } $settings += array( 'default' => false, 'stackable' => false, 'description' => null, 'aliases' => array() ); $this->_flags[$flag] = $settings; return $this; } /** * Add multiple flags at once. The input array should be keyed with the * primary flag character, and the values should be the settings array * used by {addFlag}. * * @param array $flags An array of flags to add * @return $this */ public function addFlags($flags) { foreach ($flags as $flag => $settings) { if (is_numeric($flag)) { $this->_warn('No flag character given'); continue; } $this->addFlag($flag, $settings); } return $this; } /** * Adds an option (string argument) to the argument list. * * @param mixed $option A string representing the option, or an array of strings. * @param array $settings An array of settings for this option. * @setting string description A description to be shown in --help. * @setting bool default The default value for this option. * @setting array aliases Other ways to trigger this option. * @return $this */ public function addOption($option, $settings = array()) { if (is_string($settings)) { $settings = array('description' => $settings); } if (is_array($option)) { $settings['aliases'] = $option; $option = array_shift($settings['aliases']); } if (isset($this->_options[$option])) { $this->_warn('option already exists: ' . $option); return $this; } $settings += array( 'default' => null, 'description' => null, 'aliases' => array() ); $this->_options[$option] = $settings; return $this; } /** * Add multiple options at once. The input array should be keyed with the * primary option string, and the values should be the settings array * used by {addOption}. * * @param array $options An array of options to add * @return $this */ public function addOptions($options) { foreach ($options as $option => $settings) { if (is_numeric($option)) { $this->_warn('No option string given'); continue; } $this->addOption($option, $settings); } return $this; } /** * Enable or disable strict mode. If strict mode is active any invalid * arguments found by the parser will throw `cli\arguments\InvalidArguments`. * * Even if strict is disabled, invalid arguments are logged and can be * retrieved with `cli\Arguments::getInvalidArguments()`. * * @param bool $strict True to enable, false to disable. * @return $this */ public function setStrict($strict) { $this->_strict = (bool)$strict; return $this; } /** * Get the list of invalid arguments the parser found. * * @return array */ public function getInvalidArguments() { return $this->_invalid; } /** * Get a flag by primary matcher or any defined aliases. * * @param mixed $flag Either a string representing the flag or an * cli\arguments\Argument object. * @return array */ public function getFlag($flag) { if ($flag instanceOf Argument) { $obj = $flag; $flag = $flag->value; } if (isset($this->_flags[$flag])) { return $this->_flags[$flag]; } foreach ($this->_flags as $master => $settings) { if (in_array($flag, (array)$settings['aliases'])) { if (isset($obj)) { $obj->key = $master; } $cache[$flag] =& $settings; return $settings; } } } public function getFlags() { return $this->_flags; } public function hasFlags() { return !empty($this->_flags); } /** * Returns true if the given argument is defined as a flag. * * @param mixed $argument Either a string representing the flag or an * cli\arguments\Argument object. * @return bool */ public function isFlag($argument) { return (null !== $this->getFlag($argument)); } /** * Returns true if the given flag is stackable. * * @param mixed $flag Either a string representing the flag or an * cli\arguments\Argument object. * @return bool */ public function isStackable($flag) { $settings = $this->getFlag($flag); return isset($settings) && (true === $settings['stackable']); } /** * Get an option by primary matcher or any defined aliases. * * @param mixed $option Either a string representing the option or an * cli\arguments\Argument object. * @return array */ public function getOption($option) { if ($option instanceOf Argument) { $obj = $option; $option = $option->value; } if (isset($this->_options[$option])) { return $this->_options[$option]; } foreach ($this->_options as $master => $settings) { if (in_array($option, (array)$settings['aliases'])) { if (isset($obj)) { $obj->key = $master; } return $settings; } } } public function getOptions() { return $this->_options; } public function hasOptions() { return !empty($this->_options); } /** * Returns true if the given argument is defined as an option. * * @param mixed $argument Either a string representing the option or an * cli\arguments\Argument object. * @return bool */ public function isOption($argument) { return (null != $this->getOption($argument)); } /** * Parses the argument list with the given options. The returned argument list * will use either the first long name given or the first name in the list * if a long name is not given. * * @return array * @throws arguments\InvalidArguments */ public function parse() { $this->_invalid = array(); $this->_parsed = array(); $this->_lexer = new Lexer($this->_input); foreach ($this->_lexer as $argument) { if ($this->_parseFlag($argument)) { continue; } if ($this->_parseOption($argument)) { continue; } array_push($this->_invalid, $argument->raw); } if ($this->_strict && !empty($this->_invalid)) { throw new InvalidArguments($this->_invalid); } } private function _warn($message) { trigger_error('[' . __CLASS__ .'] ' . $message, E_USER_WARNING); } private function _parseFlag($argument) { if (!$this->isFlag($argument)) { return false; } if ($this->isStackable($argument)) { if (!isset($this[$argument])) { $this[$argument->key] = 0; } $this[$argument->key] += 1; } else { $this[$argument->key] = true; } return true; } private function _parseOption($option) { if (!$this->isOption($option)) { return false; } // Peak ahead to make sure we get a value. if ($this->_lexer->end() || !$this->_lexer->peek->isValue) { $optionSettings = $this->getOption($option->key); if (empty($optionSettings['default'])) { // Oops! Got no value and no default , throw a warning and continue. $this->_warn('no value given for ' . $option->raw); $this[$option->key] = null; } else { // No value and we have a default, so we set to the default $this[$option->key] = $optionSettings['default']; } return true; } // Store as array and join to string after looping for values $values = array(); // Loop until we find a flag in peak-ahead foreach ($this->_lexer as $value) { array_push($values, $value->raw); if (!$this->_lexer->end() && !$this->_lexer->peek->isValue) { break; } } $this[$option->key] = join($values, ' '); return true; } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; abstract class Memoize { protected $_memoCache = array(); public function __get($name) { if (isset($this->_memoCache[$name])) { return $this->_memoCache[$name]; } // Hide probable private methods if (0 == strncmp($name, '_', 1)) { return ($this->_memoCache[$name] = null); } if (!method_exists($this, $name)) { return ($this->_memoCache[$name] = null); } $method = array($this, $name); ($this->_memoCache[$name] = call_user_func($method)); return $this->_memoCache[$name]; } protected function _unmemo($name) { if ($name === true) { $this->_memoCache = array(); } else { unset($this->_memoCache[$name]); } } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; use cli\Streams; /** * The `Notify` class is the basis of all feedback classes, such as Indicators * and Progress meters. The default behaviour is to refresh output after 100ms * have passed. This is done to preventing the screen from flickering and keep * slowdowns from output to a minimum. * * The most basic form of Notifier has no maxim, and simply displays a series * of characters to indicate progress is being made. */ abstract class Notify { protected $_current = 0; protected $_first = true; protected $_interval; protected $_message; protected $_start; protected $_timer; /** * Instatiates a Notification object. * * @param string $msg The text to display next to the Notifier. * @param int $interval The interval in milliseconds between updates. */ public function __construct($msg, $interval = 100) { $this->_message = $msg; $this->_interval = (int)$interval; } /** * This method should be used to print out the Notifier. This method is * called from `cli\Notify::tick()` after `cli\Notify::$_interval` has passed. * * @abstract * @param boolean $finish * @see cli\Notify::tick() */ abstract public function display($finish = false); /** * Reset the notifier state so the same instance can be used in multiple loops. */ public function reset() { $this->_current = 0; $this->_first = true; $this->_start = null; $this->_timer = null; } /** * Returns the formatted tick count. * * @return string The formatted tick count. */ public function current() { return number_format($this->_current); } /** * Calculates the time elapsed since the Notifier was first ticked. * * @return int The elapsed time in seconds. */ public function elapsed() { if (!$this->_start) { return 0; } $elapsed = time() - $this->_start; return $elapsed; } /** * Calculates the speed (number of ticks per second) at which the Notifier * is being updated. * * @return int The number of ticks performed in 1 second. */ public function speed() { static $tick, $iteration = 0, $speed = 0; if (!$this->_start) { return 0; } else if (!$tick) { $tick = $this->_start; } $now = microtime(true); $span = $now - $tick; if ($span > 1) { $iteration++; $tick = $now; $speed = ($this->_current / $iteration) / $span; } return $speed; } /** * Takes a time span given in seconds and formats it for display. The * returned string will be in MM:SS form. * * @param int $time The time span in seconds to format. * @return string The formatted time span. */ public function formatTime($time) { return floor($time / 60) . ':' . str_pad($time % 60, 2, 0, STR_PAD_LEFT); } /** * Finish our Notification display. Should be called after the Notifier is * no longer needed. * * @see cli\Notify::display() */ public function finish() { Streams::out("\r"); $this->display(true); Streams::line(); } /** * Increments are tick counter by the given amount. If no amount is provided, * the ticker is incremented by 1. * * @param int $increment The amount to increment by. */ public function increment($increment = 1) { $this->_current += $increment; } /** * Determines whether the display should be updated or not according to * our interval setting. * * @return boolean `true` if the display should be updated, `false` otherwise. */ public function shouldUpdate() { $now = microtime(true) * 1000; if (empty($this->_timer)) { $this->_start = (int)(($this->_timer = $now) / 1000); return true; } if (($now - $this->_timer) > $this->_interval) { $this->_timer = $now; return true; } return false; } /** * This method is the meat of all Notifiers. First we increment the ticker * and then update the display if enough time has passed since our last tick. * * @param int $increment The amount to increment by. * @see cli\Notify::increment() * @see cli\Notify::shouldUpdate() * @see cli\Notify::display() */ public function tick($increment = 1) { $this->increment($increment); if ($this->shouldUpdate()) { Streams::out("\r"); $this->display(); } } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; /** * A more complex type of Notifier, `Progress` Notifiers always have a maxim * value and generally show some form of percent complete or estimated time * to completion along with the standard Notifier displays. * * @see cli\Notify */ abstract class Progress extends \cli\Notify { protected $_total = 0; /** * Instantiates a Progress Notifier. * * @param string $msg The text to display next to the Notifier. * @param int $total The total number of ticks we will be performing. * @param int $interval The interval in milliseconds between updates. * @see cli\Progress::setTotal() */ public function __construct($msg, $total, $interval = 100) { parent::__construct($msg, $interval); $this->setTotal($total); } /** * Set the max increments for this progress notifier. * * @param int $total The total number of times this indicator should be `tick`ed. * @throws \InvalidArgumentException Thrown if the `$total` is less than 0. */ public function setTotal($total) { $this->_total = (int)$total; if ($this->_total < 0) { throw new \InvalidArgumentException('Maximum value out of range, must be positive.'); } } /** * Reset the progress state so the same instance can be used in multiple loops. */ public function reset($total = null) { parent::reset(); if ($total) { $this->setTotal($total); } } /** * Behaves in a similar manner to `cli\Notify::current()`, but the output * is padded to match the length of `cli\Progress::total()`. * * @return string The formatted and padded tick count. * @see cli\Progress::total() */ public function current() { $size = strlen($this->total()); return str_pad(parent::current(), $size); } /** * Returns the formatted total expected ticks. * * @return string The formatted total ticks. */ public function total() { return number_format($this->_total); } /** * Calculates the estimated total time for the tick count to reach the * total ticks given. * * @return int The estimated total number of seconds for all ticks to be * completed. This is not the estimated time left, but total. * @see cli\Notify::speed() * @see cli\Notify::elapsed() */ public function estimated() { $speed = $this->speed(); if (!$speed || !$this->elapsed()) { return 0; } $estimated = round($this->_total / $speed); return $estimated; } /** * Forces the current tick count to the total ticks given at instatiation * time before passing on to `cli\Notify::finish()`. */ public function finish() { $this->_current = $this->_total; parent::finish(); } /** * Increments are tick counter by the given amount. If no amount is provided, * the ticker is incremented by 1. * * @param int $increment The amount to increment by. */ public function increment($increment = 1) { $this->_current = min($this->_total, $this->_current + $increment); } /** * Calculate the percentage completed. * * @return float The percent completed. */ public function percent() { if ($this->_total == 0) { return 1; } return ($this->_current / $this->_total); } } <?php namespace cli; class Streams { protected static $out = STDOUT; protected static $in = STDIN; protected static $err = STDERR; static function _call( $func, $args ) { $method = __CLASS__ . '::' . $func; return call_user_func_array( $method, $args ); } static public function isTty() { return (function_exists('posix_isatty') && posix_isatty(static::$out)); } /** * Handles rendering strings. If extra scalar arguments are given after the `$msg` * the string will be rendered with `sprintf`. If the second argument is an `array` * then each key in the array will be the placeholder name. Placeholders are of the * format {:key}. * * @param string $msg The message to render. * @param mixed ... Either scalar arguments or a single array argument. * @return string The rendered string. */ public static function render( $msg ) { $args = func_get_args(); // No string replacement is needed if( count( $args ) == 1 ) { return Colors::colorize( $msg ); } // If the first argument is not an array just pass to sprintf if( !is_array( $args[1] ) ) { // Colorize the message first so sprintf doesn't bitch at us $args[0] = Colors::colorize( $args[0] ); // Escape percent characters for sprintf $args[0] = preg_replace('/(%([^\w]|$))/', "%$1", $args[0]); return call_user_func_array( 'sprintf', $args ); } // Here we do named replacement so formatting strings are more understandable foreach( $args[1] as $key => $value ) { $msg = str_replace( '{:' . $key . '}', $value, $msg ); } return Colors::colorize( $msg ); } /** * Shortcut for printing to `STDOUT`. The message and parameters are passed * through `sprintf` before output. * * @param string $msg The message to output in `printf` format. * @param mixed ... Either scalar arguments or a single array argument. * @return void * @see \cli\render() */ public static function out( $msg ) { fwrite( static::$out, self::_call( 'render', func_get_args() ) ); } /** * Pads `$msg` to the width of the shell before passing to `cli\out`. * * @param string $msg The message to pad and pass on. * @param mixed ... Either scalar arguments or a single array argument. * @return void * @see cli\out() */ public static function out_padded( $msg ) { $msg = self::_call( 'render', func_get_args() ); self::out( str_pad( $msg, \cli\Shell::columns() ) ); } /** * Prints a message to `STDOUT` with a newline appended. See `\cli\out` for * more documentation. * * @see cli\out() */ public static function line( $msg = '' ) { // func_get_args is empty if no args are passed even with the default above. $args = array_merge( func_get_args(), array( '' ) ); $args[0] .= "\n"; self::_call( 'out', $args ); } /** * Shortcut for printing to `STDERR`. The message and parameters are passed * through `sprintf` before output. * * @param string $msg The message to output in `printf` format. With no string, * a newline is printed. * @param mixed ... Either scalar arguments or a single array argument. * @return void */ public static function err( $msg = '' ) { // func_get_args is empty if no args are passed even with the default above. $args = array_merge( func_get_args(), array( '' ) ); $args[0] .= "\n"; fwrite( static::$err, self::_call( 'render', $args ) ); } /** * Takes input from `STDIN` in the given format. If an end of transmission * character is sent (^D), an exception is thrown. * * @param string $format A valid input format. See `fscanf` for documentation. * If none is given, all input up to the first newline * is accepted. * @param boolean $hide If true will hide what the user types in. * @return string The input with whitespace trimmed. * @throws \Exception Thrown if ctrl-D (EOT) is sent as input. */ public static function input( $format = null, $hide = false ) { if ( $hide ) Shell::hide(); if( $format ) { fscanf( static::$in, $format . "\n", $line ); } else { $line = fgets( static::$in ); } if ( $hide ) { Shell::hide( false ); echo "\n"; } if( $line === false ) { throw new \Exception( 'Caught ^D during input' ); } return trim( $line ); } /** * Displays an input prompt. If no default value is provided the prompt will * continue displaying until input is received. * * @param string $question The question to ask the user. * @param bool|string $default A default value if the user provides no input. * @param string $marker A string to append to the question and default value * on display. * @param boolean $hide Optionally hides what the user types in. * @return string The users input. * @see cli\input() */ public static function prompt( $question, $default = null, $marker = ': ', $hide = false ) { if( $default && strpos( $question, '[' ) === false ) { $question .= ' [' . $default . ']'; } while( true ) { self::out( $question . $marker ); $line = self::input( null, $hide ); if( !empty( $line ) ) return $line; if( $default !== false ) return $default; } } /** * Presents a user with a multiple choice question, useful for 'yes/no' type * questions (which this public static function defaults too). * * @param string $question The question to ask the user. * @param string $choice A string of characters allowed as a response. Case is ignored. * @param string $default The default choice. NULL if a default is not allowed. * @return string The users choice. * @see cli\prompt() */ public static function choose( $question, $choice = 'yn', $default = 'n' ) { if( !is_string( $choice ) ) { $choice = join( '', $choice ); } // Make every choice character lowercase except the default $choice = str_ireplace( $default, strtoupper( $default ), strtolower( $choice ) ); // Seperate each choice with a forward-slash $choices = trim( join( '/', preg_split( '//', $choice ) ), '/' ); while( true ) { $line = self::prompt( sprintf( '%s? [%s]', $question, $choices ), $default, '' ); if( stripos( $choice, $line ) !== false ) { return strtolower( $line ); } if( !empty( $default ) ) { return strtolower( $default ); } } } /** * Displays an array of strings as a menu where a user can enter a number to * choose an option. The array must be a single dimension with either strings * or objects with a `__toString()` method. * * @param array $items The list of items the user can choose from. * @param string $default The index of the default item. * @param string $title The message displayed to the user when prompted. * @return string The index of the chosen item. * @see cli\line() * @see cli\input() * @see cli\err() */ public static function menu( $items, $default = null, $title = 'Choose an item' ) { $map = array_values( $items ); if( $default && strpos( $title, '[' ) === false && isset( $items[$default] ) ) { $title .= ' [' . $items[$default] . ']'; } foreach( $map as $idx => $item ) { self::line( ' %d. %s', $idx + 1, (string)$item ); } self::line(); while( true ) { fwrite( static::$out, sprintf( '%s: ', $title ) ); $line = self::input(); if( is_numeric( $line ) ) { $line--; if( isset( $map[$line] ) ) { return array_search( $map[$line], $items ); } if( $line < 0 || $line >= count( $map ) ) { self::err( 'Invalid menu selection: out of range' ); } } else if( isset( $default ) ) { return $default; } } } /** * Sets one of the streams (input, output, or error) to a `stream` type resource. * * Valid $whichStream values are: * - 'in' (default: STDIN) * - 'out' (default: STDOUT) * - 'err' (default: STDERR) * * Any custom streams will be closed for you on shutdown, so please don't close stream * resources used with this method. * * @param string $whichStream The stream property to update * @param resource $stream The new stream resource to use * @return void * @throws \Exception Thrown if $stream is not a resource of the 'stream' type. */ public static function setStream( $whichStream, $stream ) { if( !is_resource( $stream ) || get_resource_type( $stream ) !== 'stream' ) { throw new \Exception( 'Invalid resource type!' ); } if( property_exists( __CLASS__, $whichStream ) ) { static::${$whichStream} = $stream; } register_shutdown_function( function() use ($stream) { fclose( $stream ); } ); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; use cli\Shell; use cli\Streams; use cli\table\Ascii; use cli\table\Renderer; use cli\table\Tabular; /** * The `Table` class is used to display data in a tabular format. */ class Table { protected $_renderer; protected $_headers = array(); protected $_footers = array(); protected $_width = array(); protected $_rows = array(); /** * Initializes the `Table` class. * * There are 3 ways to instantiate this class: * * 1. Pass an array of strings as the first parameter for the column headers * and a 2-dimensional array as the second parameter for the data rows. * 2. Pass an array of hash tables (string indexes instead of numerical) * where each hash table is a row and the indexes of the *first* hash * table are used as the header values. * 3. Pass nothing and use `setHeaders()` and `addRow()` or `setRows()`. * * @param array $headers Headers used in this table. Optional. * @param array $rows The rows of data for this table. Optional. * @param array $footers Footers used in this table. Optional. */ public function __construct(array $headers = null, array $rows = null, array $footers = null) { if (!empty($headers)) { // If all the rows is given in $headers we use the keys from the // first row for the header values if ($rows === null) { $rows = $headers; $keys = array_keys(array_shift($headers)); $headers = array(); foreach ($keys as $header) { $headers[$header] = $header; } } $this->setHeaders($headers); $this->setRows($rows); } if (!empty($footers)) { $this->setFooters($footers); } if (Shell::isPiped()) { $this->setRenderer(new Tabular()); } else { $this->setRenderer(new Ascii()); } } public function resetTable() { $this->_headers = array(); $this->_width = array(); $this->_rows = array(); $this->_footers = array(); return $this; } /** * Sets the renderer used by this table. * * @param table\Renderer $renderer The renderer to use for output. * @see table\Renderer * @see table\Ascii * @see table\Tabular */ public function setRenderer(Renderer $renderer) { $this->_renderer = $renderer; } /** * Loops through the row and sets the maximum width for each column. * * @param array $row The table row. * @return array $row */ protected function checkRow(array $row) { foreach ($row as $column => $str) { $width = Colors::length($str); if (!isset($this->_width[$column]) || $width > $this->_width[$column]) { $this->_width[$column] = $width; } } return $row; } /** * Output the table to `STDOUT` using `cli\line()`. * * If STDOUT is a pipe or redirected to a file, should output simple * tab-separated text. Otherwise, renders table with ASCII table borders * * @uses cli\Shell::isPiped() Determine what format to output * * @see cli\Table::renderRow() */ public function display() { foreach( $this->getDisplayLines() as $line ) { Streams::line( $line ); } } /** * Get the table lines to output. * * @see cli\Table::display() * @see cli\Table::renderRow() * * @return array */ public function getDisplayLines() { $this->_renderer->setWidths($this->_width); $border = $this->_renderer->border(); $out = array(); if (isset($border)) { $out[] = $border; } $out[] = $this->_renderer->row($this->_headers); if (isset($border)) { $out[] = $border; } foreach ($this->_rows as $row) { $row = $this->_renderer->row($row); $row = explode( PHP_EOL, $row ); $out = array_merge( $out, $row ); } if (isset($border)) { $out[] = $border; } if ($this->_footers) { $out[] = $this->_renderer->row($this->_footers); if (isset($border)) { $out[] = $border; } } return $out; } /** * Sort the table by a column. Must be called before `cli\Table::display()`. * * @param int $column The index of the column to sort by. */ public function sort($column) { if (!isset($this->_headers[$column])) { trigger_error('No column with index ' . $column, E_USER_NOTICE); return; } usort($this->_rows, function($a, $b) use ($column) { return strcmp($a[$column], $b[$column]); }); } /** * Set the headers of the table. * * @param array $headers An array of strings containing column header names. */ public function setHeaders(array $headers) { $this->_headers = $this->checkRow($headers); } /** * Set the footers of the table. * * @param array $footers An array of strings containing column footers names. */ public function setFooters(array $footers) { $this->_footers = $this->checkRow($footers); } /** * Add a row to the table. * * @param array $row The row data. * @see cli\Table::checkRow() */ public function addRow(array $row) { $this->_rows[] = $this->checkRow($row); } /** * Clears all previous rows and adds the given rows. * * @param array $rows A 2-dimensional array of row data. * @see cli\Table::addRow() */ public function setRows(array $rows) { $this->_rows = array(); foreach ($rows as $row) { $this->addRow($row); } } public function countRows() { return count($this->_rows); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author Ryan Sullivan <rsullivan@connectstudios.com> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; /** * The `Tree` class is used to display data in a tree-like format. */ class Tree { protected $_renderer; protected $_data = array(); /** * Sets the renderer used by this tree. * * @param tree\Renderer $renderer The renderer to use for output. * @see tree\Renderer * @see tree\Ascii * @see tree\Markdown */ public function setRenderer(tree\Renderer $renderer) { $this->_renderer = $renderer; } /** * Set the data. * Format: * [ * 'Label' => [ * 'Thing' => ['Thing'], * ], * 'Thing', * ] * @param array $data */ public function setData(array $data) { $this->_data = $data; } /** * Render the tree and return it as a string. * * @return string|null */ public function render() { return $this->_renderer->render($this->_data); } /** * Display the rendered tree */ public function display() { echo $this->render(); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\arguments; use cli\Memoize; /** * Represents an Argument or a value and provides several helpers related to parsing an argument list. */ class Argument extends Memoize { /** * The canonical name of this argument, used for aliasing. * * @param string */ public $key; private $_argument; private $_raw; /** * @param string $argument The raw argument, leading dashes included. */ public function __construct($argument) { $this->_raw = $argument; $this->key =& $this->_argument; if ($this->isLong) { $this->_argument = substr($this->_raw, 2); } else if ($this->isShort) { $this->_argument = substr($this->_raw, 1); } else { $this->_argument = $this->_raw; } } /** * Returns the raw input as a string. * * @return string */ public function __toString() { return (string)$this->_raw; } /** * Returns the formatted argument string. * * @return string */ public function value() { return $this->_argument; } /** * Returns the raw input. * * @return mixed */ public function raw() { return $this->_raw; } /** * Returns true if the string matches the pattern for long arguments. * * @return bool */ public function isLong() { return (0 == strncmp($this->_raw, '--', 2)); } /** * Returns true if the string matches the pattern for short arguments. * * @return bool */ public function isShort() { return !$this->isLong && (0 == strncmp($this->_raw, '-', 1)); } /** * Returns true if the string matches the pattern for arguments. * * @return bool */ public function isArgument() { return $this->isShort() || $this->isLong(); } /** * Returns true if the string matches the pattern for values. * * @return bool */ public function isValue() { return !$this->isArgument; } /** * Returns true if the argument is short but contains several characters. Each * character is considered a separate argument. * * @return bool */ public function canExplode() { return $this->isShort && strlen($this->_argument) > 1; } /** * Returns all but the first character of the argument, removing them from the * objects representation at the same time. * * @return array */ public function exploded() { $exploded = array(); for ($i = strlen($this->_argument); $i > 0; $i--) { array_push($exploded, $this->_argument[$i - 1]); } $this->_argument = array_pop($exploded); $this->_raw = '-' . $this->_argument; return $exploded; } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\arguments; use cli\Arguments; /** * Arguments help screen renderer */ class HelpScreen { protected $_flags = array(); protected $_maxFlag = 0; protected $_options = array(); protected $_maxOption = 0; public function __construct(Arguments $arguments) { $this->setArguments($arguments); } public function __toString() { return $this->render(); } public function setArguments(Arguments $arguments) { $this->consumeArgumentFlags($arguments); $this->consumeArgumentOptions($arguments); } public function consumeArgumentFlags(Arguments $arguments) { $data = $this->_consume($arguments->getFlags()); $this->_flags = $data[0]; $this->_flagMax = $data[1]; } public function consumeArgumentOptions(Arguments $arguments) { $data = $this->_consume($arguments->getOptions()); $this->_options = $data[0]; $this->_optionMax = $data[1]; } public function render() { $help = array(); array_push($help, $this->_renderFlags()); array_push($help, $this->_renderOptions()); return join($help, "\n\n"); } private function _renderFlags() { if (empty($this->_flags)) { return null; } return "Flags\n" . $this->_renderScreen($this->_flags, $this->_flagMax); } private function _renderOptions() { if (empty($this->_options)) { return null; } return "Options\n" . $this->_renderScreen($this->_options, $this->_optionMax); } private function _renderScreen($options, $max) { $help = array(); foreach ($options as $option => $settings) { $formatted = ' ' . str_pad($option, $max); $dlen = 80 - 4 - $max; $description = str_split($settings['description'], $dlen); $formatted.= ' ' . array_shift($description); if ($settings['default']) { $formatted .= ' [default: ' . $settings['default'] . ']'; } $pad = str_repeat(' ', $max + 3); while ($desc = array_shift($description)) { $formatted .= "\n${pad}${desc}"; } array_push($help, $formatted); } return join($help, "\n"); } private function _consume($options) { $max = 0; $out = array(); foreach ($options as $option => $settings) { $names = array('--' . $option); foreach ($settings['aliases'] as $alias) { array_push($names, '-' . $alias); } $names = join($names, ', '); $max = max(strlen($names), $max); $out[$names] = $settings; } return array($out, $max); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\arguments; /** * Thrown when undefined arguments are detected in strict mode. */ class InvalidArguments extends \InvalidArgumentException { protected $arguments; /** * @param array $arguments A list of arguments that do not fit the profile. */ public function __construct(array $arguments) { $this->arguments = $arguments; $this->message = $this->_generateMessage(); } /** * Get the arguments that caused the exception. * * @return array */ public function getArguments() { return $this->arguments; } private function _generateMessage() { return 'unknown argument' . (count($this->arguments) > 1 ? 's' : '') . ': ' . join($this->arguments, ', '); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\arguments; use cli\Memoize; class Lexer extends Memoize implements \Iterator { private $_items = array(); private $_index = 0; private $_length = 0; private $_first = true; /** * @param array $items A list of strings to process as tokens. */ public function __construct(array $items) { $this->_items = $items; $this->_length = count($items); } /** * The current token. * * @return string */ public function current() { return $this->_item; } /** * Peek ahead to the next token without moving the cursor. * * @return Argument */ public function peek() { return new Argument($this->_items[0]); } /** * Move the cursor forward 1 element if it is valid. */ public function next() { if ($this->valid()) { $this->_shift(); } } /** * Return the current position of the cursor. * * @return int */ public function key() { return $this->_index; } /** * Move forward 1 element and, if the method hasn't been called before, reset * the cursor's position to 0. */ public function rewind() { $this->_shift(); if ($this->_first) { $this->_index = 0; $this->_first = false; } } /** * Returns true if the cursor has not reached the end of the list. * * @return bool */ public function valid() { return ($this->_index < $this->_length); } /** * Push an element to the front of the stack. * * @param mixed $item The value to set */ public function unshift($item) { array_unshift($this->_items, $item); $this->_length += 1; } /** * Returns true if the cursor is at the end of the list. * * @return bool */ public function end() { return ($this->_index + 1) == $this->_length; } private function _shift() { $this->_item = new Argument(array_shift($this->_items)); $this->_index += 1; $this->_explode(); $this->_unmemo('peek'); } private function _explode() { if (!$this->_item->canExplode) { return false; } foreach ($this->_item->exploded as $piece) { $this->unshift('-' . $piece); } } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; /** * Handles rendering strings. If extra scalar arguments are given after the `$msg` * the string will be rendered with `sprintf`. If the second argument is an `array` * then each key in the array will be the placeholder name. Placeholders are of the * format {:key}. * * @param string $msg The message to render. * @param mixed ... Either scalar arguments or a single array argument. * @return string The rendered string. */ function render( $msg ) { return Streams::_call( 'render', func_get_args() ); } /** * Shortcut for printing to `STDOUT`. The message and parameters are passed * through `sprintf` before output. * * @param string $msg The message to output in `printf` format. * @param mixed ... Either scalar arguments or a single array argument. * @return void * @see \cli\render() */ function out( $msg ) { Streams::_call( 'out', func_get_args() ); } /** * Pads `$msg` to the width of the shell before passing to `cli\out`. * * @param string $msg The message to pad and pass on. * @param mixed ... Either scalar arguments or a single array argument. * @return void * @see cli\out() */ function out_padded( $msg ) { Streams::_call( 'out_padded', func_get_args() ); } /** * Prints a message to `STDOUT` with a newline appended. See `\cli\out` for * more documentation. * * @see cli\out() */ function line( $msg = '' ) { Streams::_call( 'line', func_get_args() ); } /** * Shortcut for printing to `STDERR`. The message and parameters are passed * through `sprintf` before output. * * @param string $msg The message to output in `printf` format. With no string, * a newline is printed. * @param mixed ... Either scalar arguments or a single array argument. * @return void */ function err( $msg = '' ) { Streams::_call( 'err', func_get_args() ); } /** * Takes input from `STDIN` in the given format. If an end of transmission * character is sent (^D), an exception is thrown. * * @param string $format A valid input format. See `fscanf` for documentation. * If none is given, all input up to the first newline * is accepted. * @return string The input with whitespace trimmed. * @throws \Exception Thrown if ctrl-D (EOT) is sent as input. */ function input( $format = null ) { return Streams::input( $format ); } /** * Displays an input prompt. If no default value is provided the prompt will * continue displaying until input is received. * * @param string $question The question to ask the user. * @param string $default A default value if the user provides no input. * @param string $marker A string to append to the question and default value on display. * @param boolean $hide If the user input should be hidden * @return string The users input. * @see cli\input() */ function prompt( $question, $default = false, $marker = ': ', $hide = false ) { return Streams::prompt( $question, $default, $marker, $hide ); } /** * Presents a user with a multiple choice question, useful for 'yes/no' type * questions (which this function defaults too). * * @param string $question The question to ask the user. * @param string $choice * @param string|null $default The default choice. NULL if a default is not allowed. * @internal param string $valid A string of characters allowed as a response. Case * is ignored. * @return string The users choice. * @see cli\prompt() */ function choose( $question, $choice = 'yn', $default = 'n' ) { return Streams::choose( $question, $choice, $default ); } /** * Does the same as {@see choose()}, but always asks yes/no and returns a boolean * * @param string $question The question to ask the user. * @param bool|null $default The default choice, in a boolean format. * @return bool */ function confirm( $question, $default = false ) { if ( is_bool( $default ) ) { $default = $default? 'y' : 'n'; } $result = choose( $question, 'yn', $default ); return $result == 'y'; } /** * Displays an array of strings as a menu where a user can enter a number to * choose an option. The array must be a single dimension with either strings * or objects with a `__toString()` method. * * @param array $items The list of items the user can choose from. * @param string $default The index of the default item. * @param string $title The message displayed to the user when prompted. * @return string The index of the chosen item. * @see cli\line() * @see cli\input() * @see cli\err() */ function menu( $items, $default = null, $title = 'Choose an item' ) { return Streams::menu( $items, $default, $title ); } /** * Attempts an encoding-safe way of getting string length. If mb_string extensions aren't * installed, falls back to basic strlen if no encoding is present * * @param string The string to check * @return int Numeric value that represents the string's length */ function safe_strlen( $str ) { if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_detect_encoding' ) ) { $length = mb_strlen( $str, mb_detect_encoding( $str ) ); } else { // iconv will return PHP notice if non-ascii characters are present in input string $str = iconv( 'ASCII' , 'ASCII', $str ); $length = strlen( $str ); } return $length; } /** * Attempts an encoding-safe way of getting a substring. If mb_string extensions aren't * installed, falls back to ascii substring if no encoding is present * * @param string $str The input string * @param int $start The starting position of the substring * @param boolean $length Maximum length of the substring * @return string Substring of string specified by start and length parameters */ function safe_substr( $str, $start, $length = false ) { if ( function_exists( 'mb_substr' ) && function_exists( 'mb_detect_encoding' ) ) { $substr = mb_substr( $str, $start, $length, mb_detect_encoding( $str ) ); } else { // iconv will return PHP notice if non-ascii characters are present in input string $str = iconv( 'ASCII' , 'ASCII', $str ); $substr = substr( $str, $start, $length ); } return $substr; } /** * An encoding-safe way of padding string length for display * * @param string $string The string to pad * @param int $length The length to pad it to * @return string */ function safe_str_pad( $string, $length ) { // Hebrew vowel characters $cleaned_string = preg_replace( '#[\x{591}-\x{5C7}]+#u', '', Colors::decolorize( $string ) ); if ( function_exists( 'mb_strwidth' ) && function_exists( 'mb_detect_encoding' ) ) { $real_length = mb_strwidth( $cleaned_string, mb_detect_encoding( $string ) ); } else { $real_length = safe_strlen( $cleaned_string ); } $diff = strlen( $string ) - $real_length; $length += $diff; return str_pad( $string, $length ); } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\notify; use cli\Notify; use cli\Streams; /** * A Notifer that displays a string of periods. */ class Dots extends Notify { protected $_dots; protected $_format = '{:msg}{:dots} ({:elapsed}, {:speed}/s)'; protected $_iteration; /** * Instatiates a Notification object. * * @param string $msg The text to display next to the Notifier. * @param int $dots The number of dots to iterate through. * @param int $interval The interval in milliseconds between updates. * @throws \InvalidArgumentException */ public function __construct($msg, $dots = 3, $interval = 100) { parent::__construct($msg, $interval); $this->_dots = (int)$dots; if ($this->_dots <= 0) { throw new \InvalidArgumentException('Dot count out of range, must be positive.'); } } /** * Prints the correct number of dots to `STDOUT` with the time elapsed and * tick speed. * * @param boolean $finish `true` if this was called from * `cli\Notify::finish()`, `false` otherwise. * @see cli\out_padded() * @see cli\Notify::formatTime() * @see cli\Notify::speed() */ public function display($finish = false) { $repeat = $this->_dots; if (!$finish) { $repeat = $this->_iteration++ % $repeat; } $msg = $this->_message; $dots = str_pad(str_repeat('.', $repeat), $this->_dots); $speed = number_format(round($this->speed())); $elapsed = $this->formatTime($this->elapsed()); Streams::out_padded($this->_format, compact('msg', 'dots', 'speed', 'elapsed')); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\notify; use cli\Notify; use cli\Streams; /** * The `Spinner` Notifier displays an ASCII spinner. */ class Spinner extends Notify { protected $_chars = '-\|/'; protected $_format = '{:msg} {:char} ({:elapsed}, {:speed}/s)'; protected $_iteration = 0; /** * Prints the current spinner position to `STDOUT` with the time elapsed * and tick speed. * * @param boolean $finish `true` if this was called from * `cli\Notify::finish()`, `false` otherwise. * @see cli\out_padded() * @see cli\Notify::formatTime() * @see cli\Notify::speed() */ public function display($finish = false) { $msg = $this->_message; $idx = $this->_iteration++ % strlen($this->_chars); $char = $this->_chars[$idx]; $speed = number_format(round($this->speed())); $elapsed = $this->formatTime($this->elapsed()); Streams::out_padded($this->_format, compact('msg', 'char', 'elapsed', 'speed')); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\progress; use cli; use cli\Progress; use cli\Shell; use cli\Streams; /** * Displays a progress bar spanning the entire shell. * * Basic format: * * ^MSG PER% [======================= ] 00:00 / 00:00$ */ class Bar extends Progress { protected $_bars = '=>'; protected $_formatMessage = '{:msg} {:percent}% ['; protected $_formatTiming = '] {:elapsed} / {:estimated}'; protected $_format = '{:msg}{:bar}{:timing}'; /** * Prints the progress bar to the screen with percent complete, elapsed time * and estimated total time. * * @param boolean $finish `true` if this was called from * `cli\Notify::finish()`, `false` otherwise. * @see cli\out() * @see cli\Notify::formatTime() * @see cli\Notify::elapsed() * @see cli\Progress::estimated(); * @see cli\Progress::percent() * @see cli\Shell::columns() */ public function display($finish = false) { $_percent = $this->percent(); $percent = str_pad(floor($_percent * 100), 3);; $msg = $this->_message; $msg = Streams::render($this->_formatMessage, compact('msg', 'percent')); $estimated = $this->formatTime($this->estimated()); $elapsed = str_pad($this->formatTime($this->elapsed()), strlen($estimated)); $timing = Streams::render($this->_formatTiming, compact('elapsed', 'estimated')); $size = Shell::columns(); $size -= strlen($msg . $timing); if ( $size < 0 ) { $size = 0; } $bar = str_repeat($this->_bars[0], floor($_percent * $size)) . $this->_bars[1]; // substr is needed to trim off the bar cap at 100% $bar = substr(str_pad($bar, $size, ' '), 0, $size); Streams::out($this->_format, compact('msg', 'bar', 'timing')); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\table; use cli\Colors; use cli\Shell; /** * The ASCII renderer renders tables with ASCII borders. */ class Ascii extends Renderer { protected $_characters = array( 'corner' => '+', 'line' => '-', 'border' => '|', 'padding' => ' ', ); protected $_border = null; protected $_constraintWidth = null; /** * Set the widths of each column in the table. * * @param array $widths The widths of the columns. */ public function setWidths(array $widths) { if ( is_null( $this->_constraintWidth ) ) { $this->_constraintWidth = (int) Shell::columns(); } $col_count = count( $widths ); $col_borders_count = $col_count * strlen( $this->_characters['border'] ); $table_borders_count = strlen( $this->_characters['border'] ) * 1; $col_padding_count = $col_count * strlen( $this->_characters['padding'] ) * 2; $max_width = $this->_constraintWidth - $col_borders_count - $table_borders_count - $col_padding_count; if ( $widths && $max_width && array_sum( $widths ) > $max_width ) { $avg = floor( $max_width / count( $widths ) ); $resize_widths = array(); $extra_width = 0; foreach( $widths as $width ) { if ( $width > $avg ) { $resize_widths[] = $width; } else { $extra_width = $extra_width + ( $avg - $width ); } } if ( ! empty( $resize_widths ) && $extra_width ) { $avg_extra_width = floor( $extra_width / count( $resize_widths ) ); foreach( $widths as &$width ) { if ( in_array( $width, $resize_widths ) ) { $width = $avg + $avg_extra_width; $extra_width = $extra_width - $avg_extra_width; array_shift( $resize_widths ); // Last item gets the cake if ( empty( $resize_widths ) ) { $width = $width + $extra_width; } } } } } $this->_widths = $widths; } /** * Set the constraint width for the table * * @param int $constraintWidth */ public function setConstraintWidth( $constraintWidth ) { $this->_constraintWidth = $constraintWidth; } /** * Set the characters used for rendering the Ascii table. * * The keys `corner`, `line` and `border` are used in rendering. * * @param $characters array Characters used in rendering. */ public function setCharacters(array $characters) { $this->_characters = array_merge($this->_characters, $characters); } /** * Render a border for the top and bottom and separating the headers from the * table rows. * * @return string The table border. */ public function border() { if (!isset($this->_border)) { $this->_border = $this->_characters['corner']; foreach ($this->_widths as $width) { $this->_border .= str_repeat($this->_characters['line'], $width + 2); $this->_border .= $this->_characters['corner']; } } return $this->_border; } /** * Renders a row for output. * * @param array $row The table row. * @return string The formatted table row. */ public function row( array $row ) { $extra_row_count = 0; if ( count( $row ) > 0 ) { $extra_rows = array_fill( 0, count( $row ), array() ); foreach( $row as $col => $value ) { $value = str_replace( PHP_EOL, ' ', $value ); $col_width = $this->_widths[ $col ]; $original_val_width = Colors::length( $value ); if ( $original_val_width > $col_width ) { $row[ $col ] = \cli\safe_substr( $value, 0, $col_width ); $value = \cli\safe_substr( $value, $col_width, $original_val_width ); $i = 0; do { $extra_value = \cli\safe_substr( $value, 0, $col_width ); $val_width = \cli\safe_strlen( $extra_value ); if ( $val_width ) { $extra_rows[ $col ][] = $extra_value; $value = \cli\safe_substr( $value, $col_width, $original_val_width ); $i++; if ( $i > $extra_row_count ) { $extra_row_count = $i; } } } while( $value ); } } } $row = array_map(array($this, 'padColumn'), $row, array_keys($row)); array_unshift($row, ''); // First border array_push($row, ''); // Last border $ret = join($this->_characters['border'], $row); if ( $extra_row_count ) { foreach( $extra_rows as $col => $col_values ) { while( count( $col_values ) < $extra_row_count ) { $col_values[] = ''; } } do { $row_values = array(); $has_more = false; foreach( $extra_rows as $col => &$col_values ) { $row_values[ $col ] = array_shift( $col_values ); if ( count( $col_values ) ) { $has_more = true; } } $row_values = array_map(array($this, 'padColumn'), $row_values, array_keys($row_values)); array_unshift($row_values, ''); // First border array_push($row_values, ''); // Last border $ret .= PHP_EOL . join($this->_characters['border'], $row_values); } while( $has_more ); } return $ret; } private function padColumn($content, $column) { return $this->_characters['padding'] . Colors::pad($content, $this->_widths[$column]) . $this->_characters['padding']; } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\table; /** * Table renderers are used to change how a table is displayed. */ abstract class Renderer { protected $_widths = array(); public function __construct(array $widths = array()) { $this->setWidths($widths); } /** * Set the widths of each column in the table. * * @param array $widths The widths of the columns. */ public function setWidths(array $widths) { $this->_widths = $widths; } /** * Render a border for the top and bottom and separating the headers from the * table rows. * * @return string The table border. */ public function border() { return null; } /** * Renders a row for output. * * @param array $row The table row. * @return string The formatted table row. */ abstract public function row(array $row); } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\table; /** * The tabular renderer is used for displaying data in a tabular format. */ class Tabular extends Renderer { /** * Renders a row for output. * * @param array $row The table row. * @return string The formatted table row. */ public function row(array $row) { return implode("\t", array_values($row)); } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author Ryan Sullivan <rsullivan@connectstudios.com> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\tree; /** * The ASCII renderer renders trees with ASCII lines. */ class Ascii extends Renderer { /** * @param array $tree * @return string */ public function render(array $tree) { $output = ''; $treeIterator = new \RecursiveTreeIterator( new \RecursiveArrayIterator($tree), \RecursiveTreeIterator::SELF_FIRST ); foreach ($treeIterator as $val) { $output .= $val . "\n"; } return $output; } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author Ryan Sullivan <rsullivan@connectstudios.com> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\tree; /** * The ASCII renderer renders trees with ASCII lines. */ class Markdown extends Renderer { /** * How many spaces to indent by * @var int */ protected $_padding = 2; /** * @param int $padding Optional. Default 2. */ function __construct($padding = null) { if ($padding) { $this->_padding = $padding; } } /** * Renders the tree * * @param array $tree * @param int $level Optional * @return string */ public function render(array $tree, $level = 0) { $output = ''; foreach ($tree as $label => $next) { if (is_string($next)) { $label = $next; } // Output the label $output .= sprintf("%s- %s\n", str_repeat(' ', $level * $this->_padding), $label); // Next level if (is_array($next)) { $output .= $this->render($next, $level + 1); } } return $output; } } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author Ryan Sullivan <rsullivan@connectstudios.com> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli\tree; /** * Tree renderers are used to change how a tree is displayed. */ abstract class Renderer { /** * @param array $tree * @return string|null */ abstract public function render(array $tree); } <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; /** * The `Shell` class is a utility class for shell related tasks such as * information on width. */ class Shell { /** * Returns the number of columns the current shell has for display. * * @return int The number of columns. * @todo Test on more systems. */ static public function columns() { static $columns; if ( null === $columns ) { if (self::is_windows() ) { $output = array(); exec('mode CON', $output); foreach ($output as $line) { if (preg_match('/Columns:( )*([0-9]+)/', $line, $matches)) { $columns = (int)$matches[2]; break; } } } else if (!preg_match('/(^|,)(\s*)?exec(\s*)?(,|$)/', ini_get('disable_functions'))) { $columns = (int) exec('/usr/bin/env tput cols'); } if ( !$columns ) { $columns = 80; // default width of cmd window on Windows OS } } return $columns; } /** * Checks whether the output of the current script is a TTY or a pipe / redirect * * Returns true if STDOUT output is being redirected to a pipe or a file; false is * output is being sent directly to the terminal. * * If an env variable SHELL_PIPE exists, returned result depends it's * value. Strings like 1, 0, yes, no, that validate to booleans are accepted. * * To enable ASCII formatting even when shell is piped, use the * ENV variable SHELL_PIPE=0 * * @return bool */ static public function isPiped() { $shellPipe = getenv('SHELL_PIPE'); if ($shellPipe !== false) { return filter_var($shellPipe, FILTER_VALIDATE_BOOLEAN); } else { return (function_exists('posix_isatty') && !posix_isatty(STDOUT)); } } /** * Uses `stty` to hide input/output completely. * @param boolean $hidden Will hide/show the next data. Defaults to true. */ static public function hide($hidden = true) { system( 'stty ' . ( $hidden? '-echo' : 'echo' ) ); } /** * Is this shell in Windows? * * @return bool */ static private function is_windows() { return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; } } ?> <?php /** * PHP Command Line Tools * * This source file is subject to the MIT license that is bundled * with this package in the file LICENSE. * * @author James Logsdon <dwarf@girsbrain.org> * @copyright 2010 James Logsdom (http://girsbrain.org) * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ namespace cli; /** * Change the color of text. * * Reference: http://graphcomp.com/info/specs/ansi_col.html#colors */ class Colors { static protected $_colors = array( 'color' => array( 'black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'white' => 37 ), 'style' => array( 'bright' => 1, 'dim' => 2, 'underline' => 4, 'blink' => 5, 'reverse' => 7, 'hidden' => 8 ), 'background' => array( 'black' => 40, 'red' => 41, 'green' => 42, 'yellow' => 43, 'blue' => 44, 'magenta' => 45, 'cyan' => 46, 'white' => 47 ) ); static protected $_enabled = null; static protected $_string_cache = array(); static public function enable($force = true) { self::$_enabled = $force === true ? true : null; } static public function disable($force = true) { self::$_enabled = $force === true ? false : null; } /** * Check if we should colorize output based on local flags and shell type. * * Only check the shell type if `Colors::$_enabled` is null and `$colored` is null. */ static public function shouldColorize($colored = null) { return self::$_enabled === true || (self::$_enabled !== false && ($colored === true || ($colored !== false && Streams::isTty()))); } /** * Set the color. * * @param string $color The name of the color or style to set. * @return string */ static public function color($color) { if (!is_array($color)) { $color = compact('color'); } $color += array('color' => null, 'style' => null, 'background' => null); if ($color['color'] == 'reset') { return "\033[0m"; } $colors = array(); foreach (array('color', 'style', 'background') as $type) { $code = @$color[$type]; if (isset(self::$_colors[$type][$code])) { $colors[] = self::$_colors[$type][$code]; } } if (empty($colors)) { $colors[] = 0; } return "\033[" . join(';', $colors) . "m"; } /** * Colorize a string using helpful string formatters. If the `Streams::$out` points to a TTY coloring will be enabled, * otherwise disabled. You can control this check with the `$colored` parameter. * * @param string $string * @param boolean $colored Force enable or disable the colorized output. If left as `null` the TTY will control coloring. * @return string */ static public function colorize($string, $colored = null) { $passed = $string; if (isset(self::$_string_cache[md5($passed)]['colorized'])) { return self::$_string_cache[md5($passed)]['colorized']; } if (!self::shouldColorize($colored)) { $colors = self::getColors(); $search = array_keys( $colors ); $return = str_replace( $search, '', $string ); self::cacheString($passed, $return, $colored); return $return; } $string = str_replace('%%', '%¾', $string); foreach (self::getColors() as $key => $value) { $string = str_replace($key, self::color($value), $string); } $string = str_replace('%¾', '%', $string); self::cacheString($passed, $string, $colored); return $string; } /** * Remove color information from a string. * * @param string $string A string with color information. * @return string A string with color information removed. */ static public function decolorize($string) { // Get rid of color tokens if they exist $string = str_replace(array_keys(self::getColors()), '', $string); // Remove color encoding if it exists foreach (self::getColors() as $key => $value) { $string = str_replace(self::color($value), '', $string); } return $string; } /** * Cache the original, colorized, and decolorized versions of a string. * * @param string $passed The original string before colorization. * @param string $colorized The string after running through self::colorize. * @param string $colored The string without any color information. */ static public function cacheString($passed, $colorized, $colored) { self::$_string_cache[md5($passed)] = array( 'passed' => $passed, 'colorized' => $colorized, 'decolorized' => self::decolorize($passed) ); } /** * Return the length of the string without color codes. * * @param string $string the string to measure * @return string */ static public function length($string) { if (isset(self::$_string_cache[md5($string)]['decolorized'])) { $test_string = self::$_string_cache[md5($string)]['decolorized']; } else { $test_string = self::decolorize($string); } return safe_strlen($test_string); } /** * Pad the string to a certain display length. * * @param string $string the string to pad * @param integer $length the display length * @return string */ static public function pad($string, $length) { return safe_str_pad( $string, $length ); } /** * Get the color mapping array. * * @return array Array of color tokens mapped to colors and styles. */ static public function getColors() { return array( '%y' => array('color' => 'yellow'), '%g' => array('color' => 'green'), '%b' => array('color' => 'blue'), '%r' => array('color' => 'red'), '%p' => array('color' => 'magenta'), '%m' => array('color' => 'magenta'), '%c' => array('color' => 'cyan'), '%w' => array('color' => 'grey'), '%k' => array('color' => 'black'), '%n' => array('color' => 'reset'), '%Y' => array('color' => 'yellow', 'style' => 'bright'), '%G' => array('color' => 'green', 'style' => 'bright'), '%B' => array('color' => 'blue', 'style' => 'bright'), '%R' => array('color' => 'red', 'style' => 'bright'), '%P' => array('color' => 'magenta', 'style' => 'bright'), '%M' => array('color' => 'magenta', 'style' => 'bright'), '%C' => array('color' => 'cyan', 'style' => 'bright'), '%W' => array('color' => 'grey', 'style' => 'bright'), '%K' => array('color' => 'black', 'style' => 'bright'), '%N' => array('color' => 'reset', 'style' => 'bright'), '%3' => array('background' => 'yellow'), '%2' => array('background' => 'green'), '%4' => array('background' => 'blue'), '%1' => array('background' => 'red'), '%5' => array('background' => 'magenta'), '%6' => array('background' => 'cyan'), '%7' => array('background' => 'grey'), '%0' => array('background' => 'black'), '%F' => array('style' => 'blink'), '%U' => array('style' => 'underline'), '%8' => array('style' => 'inverse'), '%9' => array('style' => 'bright'), '%_' => array('style' => 'bright') ); } /** * Get the cached string values. * * @return array The cached string values. */ static public function getStringCache() { return self::$_string_cache; } /** * Clear the string cache. */ static public function clearStringCache() { self::$_string_cache = array(); } } <?php error_reporting(-1); require_once __DIR__ . '/vendor/autoload.php'; $args = new cli\Arguments(array( 'flags' => array( 'verbose' => array( 'description' => 'Turn on verbose mode', 'aliases' => array('v') ), 'c' => array( 'description' => 'A counter to test stackable', 'stackable' => true ) ), 'options' => array( 'user' => array( 'description' => 'Username for authentication', 'aliases' => array('u') ) ), 'strict' => true )); try { $args->parse(); } catch (cli\InvalidArguments $e) { echo $e->getMessage() . "\n\n"; } print_r($args->getArguments()); #!/usr/bin/env php <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A shell script to create a single-file class cache of the entire Mustache * library. * * $ bin/build_bootstrap.php * * ... will create a `mustache.php` bootstrap file in the project directory, * containing all Mustache library classes. This file can then be included in * your project, rather than requiring the Mustache Autoloader. */ $baseDir = realpath(dirname(__FILE__) . '/..'); require $baseDir . '/src/Mustache/Autoloader.php'; Mustache_Autoloader::register(); // delete the old file $file = $baseDir . '/mustache.php'; if (file_exists($file)) { unlink($file); } // and load the new one SymfonyClassCollectionLoader::load(array( 'Mustache_Engine', 'Mustache_Cache', 'Mustache_Cache_AbstractCache', 'Mustache_Cache_FilesystemCache', 'Mustache_Cache_NoopCache', 'Mustache_Compiler', 'Mustache_Context', 'Mustache_Exception', 'Mustache_Exception_InvalidArgumentException', 'Mustache_Exception_LogicException', 'Mustache_Exception_RuntimeException', 'Mustache_Exception_SyntaxException', 'Mustache_Exception_UnknownFilterException', 'Mustache_Exception_UnknownHelperException', 'Mustache_Exception_UnknownTemplateException', 'Mustache_HelperCollection', 'Mustache_LambdaHelper', 'Mustache_Loader', 'Mustache_Loader_ArrayLoader', 'Mustache_Loader_CascadingLoader', 'Mustache_Loader_FilesystemLoader', 'Mustache_Loader_InlineLoader', 'Mustache_Loader_MutableLoader', 'Mustache_Loader_StringLoader', 'Mustache_Logger', 'Mustache_Logger_AbstractLogger', 'Mustache_Logger_StreamLogger', 'Mustache_Parser', 'Mustache_Template', 'Mustache_Tokenizer', ), dirname($file), basename($file, '.php')); /** * SymfonyClassCollectionLoader. * * Based heavily on the Symfony ClassCollectionLoader component, with all * the unnecessary bits removed. * * @license http://www.opensource.org/licenses/MIT * @author Fabien Potencier <fabien@symfony.com> */ class SymfonyClassCollectionLoader { private static $loaded; const HEADER = <<<EOS <?php /* * This file is part of Mustache.php. * * (c) 2010-%d Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ EOS; /** * Loads a list of classes and caches them in one big file. * * @param array $classes An array of classes to load * @param string $cacheDir A cache directory * @param string $name The cache name prefix * @param string $extension File extension of the resulting file * * @throws InvalidArgumentException When class can't be loaded */ public static function load(array $classes, $cacheDir, $name, $extension = '.php') { // each $name can only be loaded once per PHP process if (isset(self::$loaded[$name])) { return; } self::$loaded[$name] = true; $content = ''; foreach ($classes as $class) { if (!class_exists($class) && !interface_exists($class) && (!function_exists('trait_exists') || !trait_exists($class))) { throw new InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); } $r = new ReflectionClass($class); $content .= preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName())); } $cache = $cacheDir . '/' . $name . $extension; $header = sprintf(self::HEADER, strftime('%Y')); self::writeCacheFile($cache, $header . substr(self::stripComments('<?php ' . $content), 5)); } /** * Writes a cache file. * * @param string $file Filename * @param string $content Temporary file content * * @throws RuntimeException when a cache file cannot be written */ private static function writeCacheFile($file, $content) { $tmpFile = tempnam(dirname($file), basename($file)); if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { chmod($file, 0666 & ~umask()); return; } throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file)); } /** * Removes comments from a PHP source string. * * We don't use the PHP php_strip_whitespace() function * as we want the content to be readable and well-formatted. * * @param string $source A PHP string * * @return string The PHP string with the comments removed */ private static function stripComments($source) { if (!function_exists('token_get_all')) { return $source; } $output = ''; foreach (token_get_all($source) as $token) { if (is_string($token)) { $output .= $token; } elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { $output .= $token[1]; } } // replace multiple new lines with a single newline $output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output); return $output; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Abstract Mustache Cache class. * * Provides logging support to child implementations. * * @abstract */ abstract class Mustache_Cache_AbstractCache implements Mustache_Cache { private $logger = null; /** * Get the current logger instance. * * @return Mustache_Logger|Psr\Log\LoggerInterface */ public function getLogger() { return $this->logger; } /** * Set a logger instance. * * @param Mustache_Logger|Psr\Log\LoggerInterface $logger */ public function setLogger($logger = null) { if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) { throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.'); } $this->logger = $logger; } /** * Add a log record if logging is enabled. * * @param int $level The logging level * @param string $message The log message * @param array $context The log context */ protected function log($level, $message, array $context = array()) { if (isset($this->logger)) { $this->logger->log($level, $message, $context); } } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Cache filesystem implementation. * * A FilesystemCache instance caches Mustache Template classes from the filesystem by name: * * $cache = new Mustache_Cache_FilesystemCache(dirname(__FILE__).'/cache'); * $cache->cache($className, $compiledSource); * * The FilesystemCache benefits from any opcode caching that may be setup in your environment. So do that, k? */ class Mustache_Cache_FilesystemCache extends Mustache_Cache_AbstractCache { private $baseDir; private $fileMode; /** * Filesystem cache constructor. * * @param string $baseDir Directory for compiled templates. * @param int $fileMode Override default permissions for cache files. Defaults to using the system-defined umask. */ public function __construct($baseDir, $fileMode = null) { $this->baseDir = $baseDir; $this->fileMode = $fileMode; } /** * Load the class from cache using `require_once`. * * @param string $key * * @return bool */ public function load($key) { $fileName = $this->getCacheFilename($key); if (!is_file($fileName)) { return false; } require_once $fileName; return true; } /** * Cache and load the compiled class. * * @param string $key * @param string $value */ public function cache($key, $value) { $fileName = $this->getCacheFilename($key); $this->log( Mustache_Logger::DEBUG, 'Writing to template cache: "{fileName}"', array('fileName' => $fileName) ); $this->writeFile($fileName, $value); $this->load($key); } /** * Build the cache filename. * Subclasses should override for custom cache directory structures. * * @param string $name * * @return string */ protected function getCacheFilename($name) { return sprintf('%s/%s.php', $this->baseDir, $name); } /** * Create cache directory. * * @throws Mustache_Exception_RuntimeException If unable to create directory * * @param string $fileName * * @return string */ private function buildDirectoryForFilename($fileName) { $dirName = dirname($fileName); if (!is_dir($dirName)) { $this->log( Mustache_Logger::INFO, 'Creating Mustache template cache directory: "{dirName}"', array('dirName' => $dirName) ); @mkdir($dirName, 0777, true); if (!is_dir($dirName)) { throw new Mustache_Exception_RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName)); } } return $dirName; } /** * Write cache file. * * @throws Mustache_Exception_RuntimeException If unable to write file * * @param string $fileName * @param string $value */ private function writeFile($fileName, $value) { $dirName = $this->buildDirectoryForFilename($fileName); $this->log( Mustache_Logger::DEBUG, 'Caching compiled template to "{fileName}"', array('fileName' => $fileName) ); $tempFile = tempnam($dirName, basename($fileName)); if (false !== @file_put_contents($tempFile, $value)) { if (@rename($tempFile, $fileName)) { $mode = isset($this->fileMode) ? $this->fileMode : (0666 & ~umask()); @chmod($fileName, $mode); return; } $this->log( Mustache_Logger::ERROR, 'Unable to rename Mustache temp cache file: "{tempName}" -> "{fileName}"', array('tempName' => $tempFile, 'fileName' => $fileName) ); } throw new Mustache_Exception_RuntimeException(sprintf('Failed to write cache file "%s".', $fileName)); } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Cache in-memory implementation. * * The in-memory cache is used for uncached lambda section templates. It's also useful during development, but is not * recommended for production use. */ class Mustache_Cache_NoopCache extends Mustache_Cache_AbstractCache { /** * Loads nothing. Move along. * * @param string $key * * @return bool */ public function load($key) { return false; } /** * Loads the compiled Mustache Template class without caching. * * @param string $key * @param string $value */ public function cache($key, $value) { $this->log( Mustache_Logger::WARNING, 'Template cache disabled, evaluating "{className}" class at runtime', array('className' => $key) ); eval('?>' . $value); } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Invalid argument exception. */ class Mustache_Exception_InvalidArgumentException extends InvalidArgumentException implements Mustache_Exception { // This space intentionally left blank. } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Logic exception. */ class Mustache_Exception_LogicException extends LogicException implements Mustache_Exception { // This space intentionally left blank. } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Runtime exception. */ class Mustache_Exception_RuntimeException extends RuntimeException implements Mustache_Exception { // This space intentionally left blank. } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache syntax exception. */ class Mustache_Exception_SyntaxException extends LogicException implements Mustache_Exception { protected $token; /** * @param string $msg * @param array $token */ public function __construct($msg, array $token) { $this->token = $token; parent::__construct($msg); } /** * @return array */ public function getToken() { return $this->token; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Unknown filter exception. */ class Mustache_Exception_UnknownFilterException extends UnexpectedValueException implements Mustache_Exception { protected $filterName; /** * @param string $filterName */ public function __construct($filterName) { $this->filterName = $filterName; parent::__construct(sprintf('Unknown filter: %s', $filterName)); } public function getFilterName() { return $this->filterName; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Unknown helper exception. */ class Mustache_Exception_UnknownHelperException extends InvalidArgumentException implements Mustache_Exception { protected $helperName; /** * @param string $helperName */ public function __construct($helperName) { $this->helperName = $helperName; parent::__construct(sprintf('Unknown helper: %s', $helperName)); } public function getHelperName() { return $this->helperName; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Unknown template exception. */ class Mustache_Exception_UnknownTemplateException extends InvalidArgumentException implements Mustache_Exception { protected $templateName; /** * @param string $templateName */ public function __construct($templateName) { $this->templateName = $templateName; parent::__construct(sprintf('Unknown template: %s', $templateName)); } public function getTemplateName() { return $this->templateName; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Template array Loader implementation. * * An ArrayLoader instance loads Mustache Template source by name from an initial array: * * $loader = new ArrayLoader( * 'foo' => '{{ bar }}', * 'baz' => 'Hey {{ qux }}!' * ); * * $tpl = $loader->load('foo'); // '{{ bar }}' * * The ArrayLoader is used internally as a partials loader by Mustache_Engine instance when an array of partials * is set. It can also be used as a quick-and-dirty Template loader. */ class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_MutableLoader { private $templates; /** * ArrayLoader constructor. * * @param array $templates Associative array of Template source (default: array()) */ public function __construct(array $templates = array()) { $this->templates = $templates; } /** * Load a Template. * * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. * * @param string $name * * @return string Mustache Template source */ public function load($name) { if (!isset($this->templates[$name])) { throw new Mustache_Exception_UnknownTemplateException($name); } return $this->templates[$name]; } /** * Set an associative array of Template sources for this loader. * * @param array $templates */ public function setTemplates(array $templates) { $this->templates = $templates; } /** * Set a Template source by name. * * @param string $name * @param string $template Mustache Template source */ public function setTemplate($name, $template) { $this->templates[$name] = $template; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A Mustache Template cascading loader implementation, which delegates to other * Loader instances. */ class Mustache_Loader_CascadingLoader implements Mustache_Loader { private $loaders; /** * Construct a CascadingLoader with an array of loaders. * * $loader = new Mustache_Loader_CascadingLoader(array( * new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__), * new Mustache_Loader_FilesystemLoader(__DIR__.'/templates') * )); * * @param Mustache_Loader[] $loaders */ public function __construct(array $loaders = array()) { $this->loaders = array(); foreach ($loaders as $loader) { $this->addLoader($loader); } } /** * Add a Loader instance. * * @param Mustache_Loader $loader */ public function addLoader(Mustache_Loader $loader) { $this->loaders[] = $loader; } /** * Load a Template by name. * * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. * * @param string $name * * @return string Mustache Template source */ public function load($name) { foreach ($this->loaders as $loader) { try { return $loader->load($name); } catch (Mustache_Exception_UnknownTemplateException $e) { // do nothing, check the next loader. } } throw new Mustache_Exception_UnknownTemplateException($name); } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Template filesystem Loader implementation. * * A FilesystemLoader instance loads Mustache Template source from the filesystem by name: * * $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'); * $tpl = $loader->load('foo'); // equivalent to `file_get_contents(dirname(__FILE__).'/views/foo.mustache'); * * This is probably the most useful Mustache Loader implementation. It can be used for partials and normal Templates: * * $m = new Mustache(array( * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), * )); */ class Mustache_Loader_FilesystemLoader implements Mustache_Loader { private $baseDir; private $extension = '.mustache'; private $templates = array(); /** * Mustache filesystem Loader constructor. * * Passing an $options array allows overriding certain Loader options during instantiation: * * $options = array( * // The filename extension used for Mustache templates. Defaults to '.mustache' * 'extension' => '.ms', * ); * * @throws Mustache_Exception_RuntimeException if $baseDir does not exist. * * @param string $baseDir Base directory containing Mustache template files. * @param array $options Array of Loader options (default: array()) */ public function __construct($baseDir, array $options = array()) { $this->baseDir = $baseDir; if (strpos($this->baseDir, '://') === false) { $this->baseDir = realpath($this->baseDir); } if (!is_dir($this->baseDir)) { throw new Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir)); } if (array_key_exists('extension', $options)) { if (empty($options['extension'])) { $this->extension = ''; } else { $this->extension = '.' . ltrim($options['extension'], '.'); } } } /** * Load a Template by name. * * $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'); * $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache"; * * @param string $name * * @return string Mustache Template source */ public function load($name) { if (!isset($this->templates[$name])) { $this->templates[$name] = $this->loadFile($name); } return $this->templates[$name]; } /** * Helper function for loading a Mustache file by name. * * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. * * @param string $name * * @return string Mustache Template source */ protected function loadFile($name) { $fileName = $this->getFileName($name); if (!file_exists($fileName)) { throw new Mustache_Exception_UnknownTemplateException($name); } return file_get_contents($fileName); } /** * Helper function for getting a Mustache template file name. * * @param string $name * * @return string Template file name */ protected function getFileName($name) { $fileName = $this->baseDir . '/' . $name; if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { $fileName .= $this->extension; } return $fileName; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A Mustache Template loader for inline templates. * * With the InlineLoader, templates can be defined at the end of any PHP source * file: * * $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__); * $hello = $loader->load('hello'); * $goodbye = $loader->load('goodbye'); * * __halt_compiler(); * * @@ hello * Hello, {{ planet }}! * * @@ goodbye * Goodbye, cruel {{ planet }} * * Templates are deliniated by lines containing only `@@ name`. * * The InlineLoader is well-suited to micro-frameworks such as Silex: * * $app->register(new MustacheServiceProvider, array( * 'mustache.loader' => new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__) * )); * * $app->get('/{name}', function ($name) use ($app) { * return $app['mustache']->render('hello', compact('name')); * }) * ->value('name', 'world'); * * // ... * * __halt_compiler(); * * @@ hello * Hello, {{ name }}! */ class Mustache_Loader_InlineLoader implements Mustache_Loader { protected $fileName; protected $offset; protected $templates; /** * The InlineLoader requires a filename and offset to process templates. * * The magic constants `__FILE__` and `__COMPILER_HALT_OFFSET__` are usually * perfectly suited to the job: * * $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__); * * Note that this only works if the loader is instantiated inside the same * file as the inline templates. If the templates are located in another * file, it would be necessary to manually specify the filename and offset. * * @param string $fileName The file to parse for inline templates * @param int $offset A string offset for the start of the templates. * This usually coincides with the `__halt_compiler` * call, and the `__COMPILER_HALT_OFFSET__`. */ public function __construct($fileName, $offset) { if (!is_file($fileName)) { throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid filename.'); } if (!is_int($offset) || $offset < 0) { throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid file offset.'); } $this->fileName = $fileName; $this->offset = $offset; } /** * Load a Template by name. * * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. * * @param string $name * * @return string Mustache Template source */ public function load($name) { $this->loadTemplates(); if (!array_key_exists($name, $this->templates)) { throw new Mustache_Exception_UnknownTemplateException($name); } return $this->templates[$name]; } /** * Parse and load templates from the end of a source file. */ protected function loadTemplates() { if ($this->templates === null) { $this->templates = array(); $data = file_get_contents($this->fileName, false, null, $this->offset); foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) { if (trim($chunk)) { list($name, $content) = explode("\n", $chunk, 2); $this->templates[trim($name)] = trim($content); } } } } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Template mutable Loader interface. */ interface Mustache_Loader_MutableLoader { /** * Set an associative array of Template sources for this loader. * * @param array $templates */ public function setTemplates(array $templates); /** * Set a Template source by name. * * @param string $name * @param string $template Mustache Template source */ public function setTemplate($name, $template); } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Template string Loader implementation. * * A StringLoader instance is essentially a noop. It simply passes the 'name' argument straight through: * * $loader = new StringLoader; * $tpl = $loader->load('{{ foo }}'); // '{{ foo }}' * * This is the default Template Loader instance used by Mustache: * * $m = new Mustache; * $tpl = $m->loadTemplate('{{ foo }}'); * echo $tpl->render(array('foo' => 'bar')); // "bar" */ class Mustache_Loader_StringLoader implements Mustache_Loader { /** * Load a Template by source. * * @param string $name Mustache Template source * * @return string Mustache Template source */ public function load($name) { return $name; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * This is a simple Logger implementation that other Loggers can inherit from. * * This is identical to the Psr\Log\AbstractLogger. * * It simply delegates all log-level-specific methods to the `log` method to * reduce boilerplate code that a simple Logger that does the same thing with * messages regardless of the error level has to implement. */ abstract class Mustache_Logger_AbstractLogger implements Mustache_Logger { /** * System is unusable. * * @param string $message * @param array $context */ public function emergency($message, array $context = array()) { $this->log(Mustache_Logger::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 $message * @param array $context */ public function alert($message, array $context = array()) { $this->log(Mustache_Logger::ALERT, $message, $context); } /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string $message * @param array $context */ public function critical($message, array $context = array()) { $this->log(Mustache_Logger::CRITICAL, $message, $context); } /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param array $context */ public function error($message, array $context = array()) { $this->log(Mustache_Logger::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 $message * @param array $context */ public function warning($message, array $context = array()) { $this->log(Mustache_Logger::WARNING, $message, $context); } /** * Normal but significant events. * * @param string $message * @param array $context */ public function notice($message, array $context = array()) { $this->log(Mustache_Logger::NOTICE, $message, $context); } /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string $message * @param array $context */ public function info($message, array $context = array()) { $this->log(Mustache_Logger::INFO, $message, $context); } /** * Detailed debug information. * * @param string $message * @param array $context */ public function debug($message, array $context = array()) { $this->log(Mustache_Logger::DEBUG, $message, $context); } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A Mustache Stream Logger. * * The Stream Logger wraps a file resource instance (such as a stream) or a * stream URL. All log messages over the threshold level will be appended to * this stream. * * Hint: Try `php://stderr` for your stream URL. */ class Mustache_Logger_StreamLogger extends Mustache_Logger_AbstractLogger { protected static $levels = array( self::DEBUG => 100, self::INFO => 200, self::NOTICE => 250, self::WARNING => 300, self::ERROR => 400, self::CRITICAL => 500, self::ALERT => 550, self::EMERGENCY => 600, ); protected $level; protected $stream = null; protected $url = null; /** * @throws InvalidArgumentException if the logging level is unknown. * * @param resource|string $stream Resource instance or URL * @param int $level The minimum logging level at which this handler will be triggered */ public function __construct($stream, $level = Mustache_Logger::ERROR) { $this->setLevel($level); if (is_resource($stream)) { $this->stream = $stream; } else { $this->url = $stream; } } /** * Close stream resources. */ public function __destruct() { if (is_resource($this->stream)) { fclose($this->stream); } } /** * Set the minimum logging level. * * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown. * * @param int $level The minimum logging level which will be written */ public function setLevel($level) { if (!array_key_exists($level, self::$levels)) { throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level)); } $this->level = $level; } /** * Get the current minimum logging level. * * @return int */ public function getLevel() { return $this->level; } /** * Logs with an arbitrary level. * * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown. * * @param mixed $level * @param string $message * @param array $context */ public function log($level, $message, array $context = array()) { if (!array_key_exists($level, self::$levels)) { throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level)); } if (self::$levels[$level] >= self::$levels[$this->level]) { $this->writeLog($level, $message, $context); } } /** * Write a record to the log. * * @throws Mustache_Exception_LogicException If neither a stream resource nor url is present. * @throws Mustache_Exception_RuntimeException If the stream url cannot be opened. * * @param int $level The logging level * @param string $message The log message * @param array $context The log context */ protected function writeLog($level, $message, array $context = array()) { if (!is_resource($this->stream)) { if (!isset($this->url)) { throw new Mustache_Exception_LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); } $this->stream = fopen($this->url, 'a'); if (!is_resource($this->stream)) { // @codeCoverageIgnoreStart throw new Mustache_Exception_RuntimeException(sprintf('The stream or file "%s" could not be opened.', $this->url)); // @codeCoverageIgnoreEnd } } fwrite($this->stream, self::formatLine($level, $message, $context)); } /** * Gets the name of the logging level. * * @throws InvalidArgumentException if the logging level is unknown. * * @param int $level * * @return string */ protected static function getLevelName($level) { return strtoupper($level); } /** * Format a log line for output. * * @param int $level The logging level * @param string $message The log message * @param array $context The log context * * @return string */ protected static function formatLine($level, $message, array $context = array()) { return sprintf( "%s: %s\n", self::getLevelName($level), self::interpolateMessage($message, $context) ); } /** * Interpolate context values into the message placeholders. * * @param string $message * @param array $context * * @return string */ protected static function interpolateMessage($message, array $context = array()) { if (strpos($message, '{') === false) { return $message; } // build a replacement array with braces around the context keys $replace = array(); foreach ($context as $key => $val) { $replace['{' . $key . '}'] = $val; } // interpolate replacement values into the the message and return return strtr($message, $replace); } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache class autoloader. */ class Mustache_Autoloader { private $baseDir; /** * Autoloader constructor. * * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..') */ public function __construct($baseDir = null) { if ($baseDir === null) { $baseDir = dirname(__FILE__) . '/..'; } // realpath doesn't always work, for example, with stream URIs $realDir = realpath($baseDir); if (is_dir($realDir)) { $this->baseDir = $realDir; } else { $this->baseDir = $baseDir; } } /** * Register a new instance as an SPL autoloader. * * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..') * * @return Mustache_Autoloader Registered Autoloader instance */ public static function register($baseDir = null) { $loader = new self($baseDir); spl_autoload_register(array($loader, 'autoload')); return $loader; } /** * Autoload Mustache classes. * * @param string $class */ public function autoload($class) { if ($class[0] === '\\') { $class = substr($class, 1); } if (strpos($class, 'Mustache') !== 0) { return; } $file = sprintf('%s/%s.php', $this->baseDir, str_replace('_', '/', $class)); if (is_file($file)) { require $file; } } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Cache interface. * * Interface for caching and loading Mustache_Template classes * generated by the Mustache_Compiler. */ interface Mustache_Cache { /** * Load a compiled Mustache_Template class from cache. * * @param string $key * * @return bool indicates successfully class load */ public function load($key); /** * Cache and load a compiled Mustache_Template class. * * @param string $key * @param string $value */ public function cache($key, $value); } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Compiler class. * * This class is responsible for turning a Mustache token parse tree into normal PHP source code. */ class Mustache_Compiler { private $pragmas; private $defaultPragmas = array(); private $sections; private $blocks; private $source; private $indentNextLine; private $customEscape; private $entityFlags; private $charset; private $strictCallables; /** * Compile a Mustache token parse tree into PHP source code. * * @param string $source Mustache Template source code * @param string $tree Parse tree of Mustache tokens * @param string $name Mustache Template class name * @param bool $customEscape (default: false) * @param string $charset (default: 'UTF-8') * @param bool $strictCallables (default: false) * @param int $entityFlags (default: ENT_COMPAT) * * @return string Generated PHP source code */ public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT) { $this->pragmas = $this->defaultPragmas; $this->sections = array(); $this->blocks = array(); $this->source = $source; $this->indentNextLine = true; $this->customEscape = $customEscape; $this->entityFlags = $entityFlags; $this->charset = $charset; $this->strictCallables = $strictCallables; return $this->writeCode($tree, $name); } /** * Enable pragmas across all templates, regardless of the presence of pragma * tags in the individual templates. * * @internal Users should set global pragmas in Mustache_Engine, not here :) * * @param string[] $pragmas */ public function setPragmas(array $pragmas) { $this->pragmas = array(); foreach ($pragmas as $pragma) { $this->pragmas[$pragma] = true; } $this->defaultPragmas = $this->pragmas; } /** * Helper function for walking the Mustache token parse tree. * * @throws Mustache_Exception_SyntaxException upon encountering unknown token types. * * @param array $tree Parse tree of Mustache tokens * @param int $level (default: 0) * * @return string Generated PHP source code */ private function walk(array $tree, $level = 0) { $code = ''; $level++; foreach ($tree as $node) { switch ($node[Mustache_Tokenizer::TYPE]) { case Mustache_Tokenizer::T_PRAGMA: $this->pragmas[$node[Mustache_Tokenizer::NAME]] = true; break; case Mustache_Tokenizer::T_SECTION: $code .= $this->section( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), $node[Mustache_Tokenizer::INDEX], $node[Mustache_Tokenizer::END], $node[Mustache_Tokenizer::OTAG], $node[Mustache_Tokenizer::CTAG], $level ); break; case Mustache_Tokenizer::T_INVERTED: $code .= $this->invertedSection( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), $level ); break; case Mustache_Tokenizer::T_PARTIAL: $code .= $this->partial( $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', $level ); break; case Mustache_Tokenizer::T_PARENT: $code .= $this->parent( $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', $node[Mustache_Tokenizer::NODES], $level ); break; case Mustache_Tokenizer::T_BLOCK_ARG: $code .= $this->blockArg( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::INDEX], $node[Mustache_Tokenizer::END], $node[Mustache_Tokenizer::OTAG], $node[Mustache_Tokenizer::CTAG], $level ); break; case Mustache_Tokenizer::T_BLOCK_VAR: $code .= $this->blockVar( $node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::INDEX], $node[Mustache_Tokenizer::END], $node[Mustache_Tokenizer::OTAG], $node[Mustache_Tokenizer::CTAG], $level ); break; case Mustache_Tokenizer::T_COMMENT: break; case Mustache_Tokenizer::T_ESCAPED: case Mustache_Tokenizer::T_UNESCAPED: case Mustache_Tokenizer::T_UNESCAPED_2: $code .= $this->variable( $node[Mustache_Tokenizer::NAME], isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_ESCAPED, $level ); break; case Mustache_Tokenizer::T_TEXT: $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level); break; default: throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node); } } return $code; } const KLASS = '<?php class %s extends Mustache_Template { private $lambdaHelper;%s public function renderInternal(Mustache_Context $context, $indent = \'\') { $this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context); $buffer = \'\'; $newContext = array(); %s return $buffer; } %s %s }'; const KLASS_NO_LAMBDAS = '<?php class %s extends Mustache_Template {%s public function renderInternal(Mustache_Context $context, $indent = \'\') { $buffer = \'\'; $newContext = array(); %s return $buffer; } }'; const STRICT_CALLABLE = 'protected $strictCallables = true;'; /** * Generate Mustache Template class PHP source. * * @param array $tree Parse tree of Mustache tokens * @param string $name Mustache Template class name * * @return string Generated PHP source code */ private function writeCode($tree, $name) { $code = $this->walk($tree); $sections = implode("\n", $this->sections); $blocks = implode("\n", $this->blocks); $klass = empty($this->sections) && empty($this->blocks) ? self::KLASS_NO_LAMBDAS : self::KLASS; $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : ''; return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections, $blocks); } const BLOCK_VAR = ' $blockFunction = $context->findInBlock(%s); if (is_callable($blockFunction)) { $buffer .= call_user_func($blockFunction, $context); } else {%s } '; /** * Generate Mustache Template inheritance block variable PHP source. * * @param array $nodes Array of child tokens * @param string $id Section name * @param int $start Section start offset * @param int $end Section end offset * @param string $otag Current Mustache opening tag * @param string $ctag Current Mustache closing tag * @param int $level * * @return string Generated PHP source code */ private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level) { $id = var_export($id, true); return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $this->walk($nodes, $level)); } const BLOCK_ARG = '$newContext[%s] = array($this, \'block%s\');'; /** * Generate Mustache Template inheritance block argument PHP source. * * @param array $nodes Array of child tokens * @param string $id Section name * @param int $start Section start offset * @param int $end Section end offset * @param string $otag Current Mustache opening tag * @param string $ctag Current Mustache closing tag * @param int $level * * @return string Generated PHP source code */ private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) { $key = $this->block($nodes); $keystr = var_export($key, true); $id = var_export($id, true); return sprintf($this->prepare(self::BLOCK_ARG, 1), $id, $key); } const BLOCK_FUNCTION = ' public function block%s($context) { $indent = $buffer = \'\';%s return $buffer; } '; /** * Generate Mustache Template inheritance block function PHP source. * * @param array $nodes Array of child tokens * * @return string key of new block function */ private function block($nodes) { $code = $this->walk($nodes, 0); $key = ucfirst(md5($code)); if (!isset($this->blocks[$key])) { $this->blocks[$key] = sprintf($this->prepare(self::BLOCK_FUNCTION, 0), $key, $code); } return $key; } const SECTION_CALL = ' // %s section $value = $context->%s(%s);%s $buffer .= $this->section%s($context, $indent, $value); '; const SECTION = ' private function section%s(Mustache_Context $context, $indent, $value) { $buffer = \'\'; if (%s) { $source = %s; $result = call_user_func($value, $source, $this->lambdaHelper); if (strpos($result, \'{{\') === false) { $buffer .= $result; } else { $buffer .= $this->mustache ->loadLambda((string) $result%s) ->renderInternal($context); } } elseif (!empty($value)) { $values = $this->isIterable($value) ? $value : array($value); foreach ($values as $value) { $context->push($value); %s $context->pop(); } } return $buffer; } '; /** * Generate Mustache Template section PHP source. * * @param array $nodes Array of child tokens * @param string $id Section name * @param string[] $filters Array of filters * @param int $start Section start offset * @param int $end Section end offset * @param string $otag Current Mustache opening tag * @param string $ctag Current Mustache closing tag * @param int $level * @param bool $arg (default: false) * * @return string Generated section PHP source code */ private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $level, $arg = false) { $source = var_export(substr($this->source, $start, $end - $start), true); $callable = $this->getCallable(); if ($otag !== '{{' || $ctag !== '}}') { $delims = ', ' . var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true); } else { $delims = ''; } $key = ucfirst(md5($delims . "\n" . $source)); if (!isset($this->sections[$key])) { $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2)); } if ($arg === true) { return $key; } else { $method = $this->getFindMethod($id); $id = var_export($id, true); $filters = $this->getFilters($filters, $level); return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); } } const INVERTED_SECTION = ' // %s inverted section $value = $context->%s(%s);%s if (empty($value)) { %s } '; /** * Generate Mustache Template inverted section PHP source. * * @param array $nodes Array of child tokens * @param string $id Section name * @param string[] $filters Array of filters * @param int $level * * @return string Generated inverted section PHP source code */ private function invertedSection($nodes, $id, $filters, $level) { $method = $this->getFindMethod($id); $id = var_export($id, true); $filters = $this->getFilters($filters, $level); return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $filters, $this->walk($nodes, $level)); } const PARTIAL_INDENT = ', $indent . %s'; const PARTIAL = ' if ($partial = $this->mustache->loadPartial(%s)) { $buffer .= $partial->renderInternal($context%s); } '; /** * Generate Mustache Template partial call PHP source. * * @param string $id Partial name * @param string $indent Whitespace indent to apply to partial * @param int $level * * @return string Generated partial call PHP source code */ private function partial($id, $indent, $level) { if ($indent !== '') { $indentParam = sprintf(self::PARTIAL_INDENT, var_export($indent, true)); } else { $indentParam = ''; } return sprintf( $this->prepare(self::PARTIAL, $level), var_export($id, true), $indentParam ); } const PARENT = ' %s if ($parent = $this->mustache->LoadPartial(%s)) { $context->pushBlockContext($newContext); $buffer .= $parent->renderInternal($context, $indent); $context->popBlockContext(); } '; /** * Generate Mustache Template inheritance parent call PHP source. * * @param string $id Parent tag name * @param string $indent Whitespace indent to apply to parent * @param array $children Child nodes * @param int $level * * @return string Generated PHP source code */ private function parent($id, $indent, array $children, $level) { $realChildren = array_filter($children, array(__CLASS__, 'onlyBlockArgs')); return sprintf( $this->prepare(self::PARENT, $level), $this->walk($realChildren, $level), var_export($id, true), var_export($indent, true) ); } /** * Helper method for filtering out non-block-arg tokens. * * @param array $node * * @return bool True if $node is a block arg token. */ private static function onlyBlockArgs(array $node) { return $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_BLOCK_ARG; } const VARIABLE = ' $value = $this->resolveValue($context->%s(%s), $context, $indent);%s $buffer .= %s%s; '; /** * Generate Mustache Template variable interpolation PHP source. * * @param string $id Variable name * @param string[] $filters Array of filters * @param bool $escape Escape the variable value for output? * @param int $level * * @return string Generated variable interpolation PHP source */ private function variable($id, $filters, $escape, $level) { $method = $this->getFindMethod($id); $id = ($method !== 'last') ? var_export($id, true) : ''; $filters = $this->getFilters($filters, $level); $value = $escape ? $this->getEscape() : '$value'; return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value); } const FILTER = ' $filter = $context->%s(%s); if (!(%s)) { throw new Mustache_Exception_UnknownFilterException(%s); } $value = call_user_func($filter, $value);%s '; /** * Generate Mustache Template variable filtering PHP source. * * @param string[] $filters Array of filters * @param int $level * * @return string Generated filter PHP source */ private function getFilters(array $filters, $level) { if (empty($filters)) { return ''; } $name = array_shift($filters); $method = $this->getFindMethod($name); $filter = ($method !== 'last') ? var_export($name, true) : ''; $callable = $this->getCallable('$filter'); $msg = var_export($name, true); return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilters($filters, $level)); } const LINE = '$buffer .= "\n";'; const TEXT = '$buffer .= %s%s;'; /** * Generate Mustache Template output Buffer call PHP source. * * @param string $text * @param int $level * * @return string Generated output Buffer call PHP source */ private function text($text, $level) { $indentNextLine = (substr($text, -1) === "\n"); $code = sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true)); $this->indentNextLine = $indentNextLine; return $code; } /** * Prepare PHP source code snippet for output. * * @param string $text * @param int $bonus Additional indent level (default: 0) * @param bool $prependNewline Prepend a newline to the snippet? (default: true) * @param bool $appendNewline Append a newline to the snippet? (default: false) * * @return string PHP source code snippet */ private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false) { $text = ($prependNewline ? "\n" : '') . trim($text); if ($prependNewline) { $bonus++; } if ($appendNewline) { $text .= "\n"; } return preg_replace("/\n( {8})?/", "\n" . str_repeat(' ', $bonus * 4), $text); } const DEFAULT_ESCAPE = 'htmlspecialchars(%s, %s, %s)'; const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)'; /** * Get the current escaper. * * @param string $value (default: '$value') * * @return string Either a custom callback, or an inline call to `htmlspecialchars` */ private function getEscape($value = '$value') { if ($this->customEscape) { return sprintf(self::CUSTOM_ESCAPE, $value); } return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true)); } /** * Select the appropriate Context `find` method for a given $id. * * The return value will be one of `find`, `findDot` or `last`. * * @see Mustache_Context::find * @see Mustache_Context::findDot * @see Mustache_Context::last * * @param string $id Variable name * * @return string `find` method name */ private function getFindMethod($id) { if ($id === '.') { return 'last'; } if (isset($this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) && $this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) { if (substr($id, 0, 1) === '.') { return 'findAnchoredDot'; } } if (strpos($id, '.') === false) { return 'find'; } return 'findDot'; } const IS_CALLABLE = '!is_string(%s) && is_callable(%s)'; const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)'; /** * Helper function to compile strict vs lax "is callable" logic. * * @param string $variable (default: '$value') * * @return string "is callable" logic */ private function getCallable($variable = '$value') { $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE; return sprintf($tpl, $variable, $variable); } const LINE_INDENT = '$indent . '; /** * Get the current $indent prefix to write to the buffer. * * @return string "$indent . " or "" */ private function flushIndent() { if (!$this->indentNextLine) { return ''; } $this->indentNextLine = false; return self::LINE_INDENT; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Template rendering Context. */ class Mustache_Context { private $stack = array(); private $blockStack = array(); /** * Mustache rendering Context constructor. * * @param mixed $context Default rendering context (default: null) */ public function __construct($context = null) { if ($context !== null) { $this->stack = array($context); } } /** * Push a new Context frame onto the stack. * * @param mixed $value Object or array to use for context */ public function push($value) { array_push($this->stack, $value); } /** * Push a new Context frame onto the block context stack. * * @param mixed $value Object or array to use for block context */ public function pushBlockContext($value) { array_push($this->blockStack, $value); } /** * Pop the last Context frame from the stack. * * @return mixed Last Context frame (object or array) */ public function pop() { return array_pop($this->stack); } /** * Pop the last block Context frame from the stack. * * @return mixed Last block Context frame (object or array) */ public function popBlockContext() { return array_pop($this->blockStack); } /** * Get the last Context frame. * * @return mixed Last Context frame (object or array) */ public function last() { return end($this->stack); } /** * Find a variable in the Context stack. * * Starting with the last Context frame (the context of the innermost section), and working back to the top-level * rendering context, look for a variable with the given name: * * * If the Context frame is an associative array which contains the key $id, returns the value of that element. * * If the Context frame is an object, this will check first for a public method, then a public property named * $id. Failing both of these, it will try `__isset` and `__get` magic methods. * * If a value named $id is not found in any Context frame, returns an empty string. * * @param string $id Variable name * * @return mixed Variable value, or '' if not found */ public function find($id) { return $this->findVariableInStack($id, $this->stack); } /** * Find a 'dot notation' variable in the Context stack. * * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous * result. For example, given the following context stack: * * $data = array( * 'name' => 'Fred', * 'child' => array( * 'name' => 'Bob' * ), * ); * * ... and the Mustache following template: * * {{ child.name }} * * ... the `name` value is only searched for within the `child` value of the global Context, not within parent * Context frames. * * @param string $id Dotted variable selector * * @return mixed Variable value, or '' if not found */ public function findDot($id) { $chunks = explode('.', $id); $first = array_shift($chunks); $value = $this->findVariableInStack($first, $this->stack); foreach ($chunks as $chunk) { if ($value === '') { return $value; } $value = $this->findVariableInStack($chunk, array($value)); } return $value; } /** * Find an 'anchored dot notation' variable in the Context stack. * * This is the same as findDot(), except it looks in the top of the context * stack for the first value, rather than searching the whole context stack * and starting from there. * * @see Mustache_Context::findDot * * @throws Mustache_Exception_InvalidArgumentException if given an invalid anchored dot $id. * * @param string $id Dotted variable selector * * @return mixed Variable value, or '' if not found */ public function findAnchoredDot($id) { $chunks = explode('.', $id); $first = array_shift($chunks); if ($first !== '') { throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected id for findAnchoredDot: %s', $id)); } $value = $this->last(); foreach ($chunks as $chunk) { if ($value === '') { return $value; } $value = $this->findVariableInStack($chunk, array($value)); } return $value; } /** * Find an argument in the block context stack. * * @param string $id * * @return mixed Variable value, or '' if not found. */ public function findInBlock($id) { foreach ($this->blockStack as $context) { if (array_key_exists($id, $context)) { return $context[$id]; } } return ''; } /** * Helper function to find a variable in the Context stack. * * @see Mustache_Context::find * * @param string $id Variable name * @param array $stack Context stack * * @return mixed Variable value, or '' if not found */ private function findVariableInStack($id, array $stack) { for ($i = count($stack) - 1; $i >= 0; $i--) { $frame = &$stack[$i]; switch (gettype($frame)) { case 'object': if (!($frame instanceof Closure)) { // Note that is_callable() *will not work here* // See https://github.com/bobthecow/mustache.php/wiki/Magic-Methods if (method_exists($frame, $id)) { return $frame->$id(); } if (isset($frame->$id)) { return $frame->$id; } if ($frame instanceof ArrayAccess && isset($frame[$id])) { return $frame[$id]; } } break; case 'array': if (array_key_exists($id, $frame)) { return $frame[$id]; } break; } } return ''; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A Mustache implementation in PHP. * * {@link http://defunkt.github.com/mustache} * * Mustache is a framework-agnostic logic-less templating language. It enforces separation of view * logic from template files. In fact, it is not even possible to embed logic in the template. * * This is very, very rad. * * @author Justin Hileman {@link http://justinhileman.com} */ class Mustache_Engine { const VERSION = '2.9.0'; const SPEC_VERSION = '1.1.2'; const PRAGMA_FILTERS = 'FILTERS'; const PRAGMA_BLOCKS = 'BLOCKS'; const PRAGMA_ANCHORED_DOT = 'ANCHORED-DOT'; // Known pragmas private static $knownPragmas = array( self::PRAGMA_FILTERS => true, self::PRAGMA_BLOCKS => true, self::PRAGMA_ANCHORED_DOT => true, ); // Template cache private $templates = array(); // Environment private $templateClassPrefix = '__Mustache_'; private $cache; private $lambdaCache; private $cacheLambdaTemplates = false; private $loader; private $partialsLoader; private $helpers; private $escape; private $entityFlags = ENT_COMPAT; private $charset = 'UTF-8'; private $logger; private $strictCallables = false; private $pragmas = array(); // Services private $tokenizer; private $parser; private $compiler; /** * Mustache class constructor. * * Passing an $options array allows overriding certain Mustache options during instantiation: * * $options = array( * // The class prefix for compiled templates. Defaults to '__Mustache_'. * 'template_class_prefix' => '__MyTemplates_', * * // A Mustache cache instance or a cache directory string for compiled templates. * // Mustache will not cache templates unless this is set. * 'cache' => dirname(__FILE__).'/tmp/cache/mustache', * * // Override default permissions for cache files. Defaults to using the system-defined umask. It is * // *strongly* recommended that you configure your umask properly rather than overriding permissions here. * 'cache_file_mode' => 0666, * * // Optionally, enable caching for lambda section templates. This is generally not recommended, as lambda * // sections are often too dynamic to benefit from caching. * 'cache_lambda_templates' => true, * * // A Mustache template loader instance. Uses a StringLoader if not specified. * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), * * // A Mustache loader instance for partials. * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), * * // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as * // efficient or lazy as a Filesystem (or database) loader. * 'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')), * * // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order * // sections), or any other valid Mustache context value. They will be prepended to the context stack, * // so they will be available in any template loaded by this Mustache instance. * 'helpers' => array('i18n' => function ($text) { * // do something translatey here... * }), * * // An 'escape' callback, responsible for escaping double-mustache variables. * 'escape' => function ($value) { * return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8'); * }, * * // Type argument for `htmlspecialchars`. Defaults to ENT_COMPAT. You may prefer ENT_QUOTES. * 'entity_flags' => ENT_QUOTES, * * // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'. * 'charset' => 'ISO-8859-1', * * // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible * // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is * // available as well: * 'logger' => new Mustache_Logger_StreamLogger('php://stderr'), * * // Only treat Closure instances and invokable classes as callable. If true, values like * // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally * // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This * // helps protect against arbitrary code execution when user input is passed directly into the template. * // This currently defaults to false, but will default to true in v3.0. * 'strict_callables' => true, * * // Enable pragmas across all templates, regardless of the presence of pragma tags in the individual * // templates. * 'pragmas' => [Mustache_Engine::PRAGMA_FILTERS], * ); * * @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable. * * @param array $options (default: array()) */ public function __construct(array $options = array()) { if (isset($options['template_class_prefix'])) { $this->templateClassPrefix = $options['template_class_prefix']; } if (isset($options['cache'])) { $cache = $options['cache']; if (is_string($cache)) { $mode = isset($options['cache_file_mode']) ? $options['cache_file_mode'] : null; $cache = new Mustache_Cache_FilesystemCache($cache, $mode); } $this->setCache($cache); } if (isset($options['cache_lambda_templates'])) { $this->cacheLambdaTemplates = (bool) $options['cache_lambda_templates']; } if (isset($options['loader'])) { $this->setLoader($options['loader']); } if (isset($options['partials_loader'])) { $this->setPartialsLoader($options['partials_loader']); } if (isset($options['partials'])) { $this->setPartials($options['partials']); } if (isset($options['helpers'])) { $this->setHelpers($options['helpers']); } if (isset($options['escape'])) { if (!is_callable($options['escape'])) { throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable'); } $this->escape = $options['escape']; } if (isset($options['entity_flags'])) { $this->entityFlags = $options['entity_flags']; } if (isset($options['charset'])) { $this->charset = $options['charset']; } if (isset($options['logger'])) { $this->setLogger($options['logger']); } if (isset($options['strict_callables'])) { $this->strictCallables = $options['strict_callables']; } if (isset($options['pragmas'])) { foreach ($options['pragmas'] as $pragma) { if (!isset(self::$knownPragmas[$pragma])) { throw new Mustache_Exception_InvalidArgumentException(sprintf('Unknown pragma: "%s".', $pragma)); } $this->pragmas[$pragma] = true; } } } /** * Shortcut 'render' invocation. * * Equivalent to calling `$mustache->loadTemplate($template)->render($context);` * * @see Mustache_Engine::loadTemplate * @see Mustache_Template::render * * @param string $template * @param mixed $context (default: array()) * * @return string Rendered template */ public function render($template, $context = array()) { return $this->loadTemplate($template)->render($context); } /** * Get the current Mustache escape callback. * * @return callable|null */ public function getEscape() { return $this->escape; } /** * Get the current Mustache entitity type to escape. * * @return int */ public function getEntityFlags() { return $this->entityFlags; } /** * Get the current Mustache character set. * * @return string */ public function getCharset() { return $this->charset; } /** * Get the current globally enabled pragmas. * * @return array */ public function getPragmas() { return array_keys($this->pragmas); } /** * Set the Mustache template Loader instance. * * @param Mustache_Loader $loader */ public function setLoader(Mustache_Loader $loader) { $this->loader = $loader; } /** * Get the current Mustache template Loader instance. * * If no Loader instance has been explicitly specified, this method will instantiate and return * a StringLoader instance. * * @return Mustache_Loader */ public function getLoader() { if (!isset($this->loader)) { $this->loader = new Mustache_Loader_StringLoader(); } return $this->loader; } /** * Set the Mustache partials Loader instance. * * @param Mustache_Loader $partialsLoader */ public function setPartialsLoader(Mustache_Loader $partialsLoader) { $this->partialsLoader = $partialsLoader; } /** * Get the current Mustache partials Loader instance. * * If no Loader instance has been explicitly specified, this method will instantiate and return * an ArrayLoader instance. * * @return Mustache_Loader */ public function getPartialsLoader() { if (!isset($this->partialsLoader)) { $this->partialsLoader = new Mustache_Loader_ArrayLoader(); } return $this->partialsLoader; } /** * Set partials for the current partials Loader instance. * * @throws Mustache_Exception_RuntimeException If the current Loader instance is immutable * * @param array $partials (default: array()) */ public function setPartials(array $partials = array()) { if (!isset($this->partialsLoader)) { $this->partialsLoader = new Mustache_Loader_ArrayLoader(); } if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) { throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance'); } $this->partialsLoader->setTemplates($partials); } /** * Set an array of Mustache helpers. * * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in * any template loaded by this Mustache instance. * * @throws Mustache_Exception_InvalidArgumentException if $helpers is not an array or Traversable * * @param array|Traversable $helpers */ public function setHelpers($helpers) { if (!is_array($helpers) && !$helpers instanceof Traversable) { throw new Mustache_Exception_InvalidArgumentException('setHelpers expects an array of helpers'); } $this->getHelpers()->clear(); foreach ($helpers as $name => $helper) { $this->addHelper($name, $helper); } } /** * Get the current set of Mustache helpers. * * @see Mustache_Engine::setHelpers * * @return Mustache_HelperCollection */ public function getHelpers() { if (!isset($this->helpers)) { $this->helpers = new Mustache_HelperCollection(); } return $this->helpers; } /** * Add a new Mustache helper. * * @see Mustache_Engine::setHelpers * * @param string $name * @param mixed $helper */ public function addHelper($name, $helper) { $this->getHelpers()->add($name, $helper); } /** * Get a Mustache helper by name. * * @see Mustache_Engine::setHelpers * * @param string $name * * @return mixed Helper */ public function getHelper($name) { return $this->getHelpers()->get($name); } /** * Check whether this Mustache instance has a helper. * * @see Mustache_Engine::setHelpers * * @param string $name * * @return bool True if the helper is present */ public function hasHelper($name) { return $this->getHelpers()->has($name); } /** * Remove a helper by name. * * @see Mustache_Engine::setHelpers * * @param string $name */ public function removeHelper($name) { $this->getHelpers()->remove($name); } /** * Set the Mustache Logger instance. * * @throws Mustache_Exception_InvalidArgumentException If logger is not an instance of Mustache_Logger or Psr\Log\LoggerInterface. * * @param Mustache_Logger|Psr\Log\LoggerInterface $logger */ public function setLogger($logger = null) { if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) { throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.'); } if ($this->getCache()->getLogger() === null) { $this->getCache()->setLogger($logger); } $this->logger = $logger; } /** * Get the current Mustache Logger instance. * * @return Mustache_Logger|Psr\Log\LoggerInterface */ public function getLogger() { return $this->logger; } /** * Set the Mustache Tokenizer instance. * * @param Mustache_Tokenizer $tokenizer */ public function setTokenizer(Mustache_Tokenizer $tokenizer) { $this->tokenizer = $tokenizer; } /** * Get the current Mustache Tokenizer instance. * * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one. * * @return Mustache_Tokenizer */ public function getTokenizer() { if (!isset($this->tokenizer)) { $this->tokenizer = new Mustache_Tokenizer(); } return $this->tokenizer; } /** * Set the Mustache Parser instance. * * @param Mustache_Parser $parser */ public function setParser(Mustache_Parser $parser) { $this->parser = $parser; } /** * Get the current Mustache Parser instance. * * If no Parser instance has been explicitly specified, this method will instantiate and return a new one. * * @return Mustache_Parser */ public function getParser() { if (!isset($this->parser)) { $this->parser = new Mustache_Parser(); } return $this->parser; } /** * Set the Mustache Compiler instance. * * @param Mustache_Compiler $compiler */ public function setCompiler(Mustache_Compiler $compiler) { $this->compiler = $compiler; } /** * Get the current Mustache Compiler instance. * * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one. * * @return Mustache_Compiler */ public function getCompiler() { if (!isset($this->compiler)) { $this->compiler = new Mustache_Compiler(); } return $this->compiler; } /** * Set the Mustache Cache instance. * * @param Mustache_Cache $cache */ public function setCache(Mustache_Cache $cache) { if (isset($this->logger) && $cache->getLogger() === null) { $cache->setLogger($this->getLogger()); } $this->cache = $cache; } /** * Get the current Mustache Cache instance. * * If no Cache instance has been explicitly specified, this method will instantiate and return a new one. * * @return Mustache_Cache */ public function getCache() { if (!isset($this->cache)) { $this->setCache(new Mustache_Cache_NoopCache()); } return $this->cache; } /** * Get the current Lambda Cache instance. * * If 'cache_lambda_templates' is enabled, this is the default cache instance. Otherwise, it is a NoopCache. * * @see Mustache_Engine::getCache * * @return Mustache_Cache */ protected function getLambdaCache() { if ($this->cacheLambdaTemplates) { return $this->getCache(); } if (!isset($this->lambdaCache)) { $this->lambdaCache = new Mustache_Cache_NoopCache(); } return $this->lambdaCache; } /** * Helper method to generate a Mustache template class. * * @param string $source * * @return string Mustache Template class name */ public function getTemplateClassName($source) { return $this->templateClassPrefix . md5(sprintf( 'version:%s,escape:%s,entity_flags:%i,charset:%s,strict_callables:%s,pragmas:%s,source:%s', self::VERSION, isset($this->escape) ? 'custom' : 'default', $this->entityFlags, $this->charset, $this->strictCallables ? 'true' : 'false', implode(' ', $this->getPragmas()), $source )); } /** * Load a Mustache Template by name. * * @param string $name * * @return Mustache_Template */ public function loadTemplate($name) { return $this->loadSource($this->getLoader()->load($name)); } /** * Load a Mustache partial Template by name. * * This is a helper method used internally by Template instances for loading partial templates. You can most likely * ignore it completely. * * @param string $name * * @return Mustache_Template */ public function loadPartial($name) { try { if (isset($this->partialsLoader)) { $loader = $this->partialsLoader; } elseif (isset($this->loader) && !$this->loader instanceof Mustache_Loader_StringLoader) { $loader = $this->loader; } else { throw new Mustache_Exception_UnknownTemplateException($name); } return $this->loadSource($loader->load($name)); } catch (Mustache_Exception_UnknownTemplateException $e) { // If the named partial cannot be found, log then return null. $this->log( Mustache_Logger::WARNING, 'Partial not found: "{name}"', array('name' => $e->getTemplateName()) ); } } /** * Load a Mustache lambda Template by source. * * This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most * likely ignore it completely. * * @param string $source * @param string $delims (default: null) * * @return Mustache_Template */ public function loadLambda($source, $delims = null) { if ($delims !== null) { $source = $delims . "\n" . $source; } return $this->loadSource($source, $this->getLambdaCache()); } /** * Instantiate and return a Mustache Template instance by source. * * Optionally provide a Mustache_Cache instance. This is used internally by Mustache_Engine::loadLambda to respect * the 'cache_lambda_templates' configuration option. * * @see Mustache_Engine::loadTemplate * @see Mustache_Engine::loadPartial * @see Mustache_Engine::loadLambda * * @param string $source * @param Mustache_Cache $cache (default: null) * * @return Mustache_Template */ private function loadSource($source, Mustache_Cache $cache = null) { $className = $this->getTemplateClassName($source); if (!isset($this->templates[$className])) { if ($cache === null) { $cache = $this->getCache(); } if (!class_exists($className, false)) { if (!$cache->load($className)) { $compiled = $this->compile($source); $cache->cache($className, $compiled); } } $this->log( Mustache_Logger::DEBUG, 'Instantiating template: "{className}"', array('className' => $className) ); $this->templates[$className] = new $className($this); } return $this->templates[$className]; } /** * Helper method to tokenize a Mustache template. * * @see Mustache_Tokenizer::scan * * @param string $source * * @return array Tokens */ private function tokenize($source) { return $this->getTokenizer()->scan($source); } /** * Helper method to parse a Mustache template. * * @see Mustache_Parser::parse * * @param string $source * * @return array Token tree */ private function parse($source) { $parser = $this->getParser(); $parser->setPragmas($this->getPragmas()); return $parser->parse($this->tokenize($source)); } /** * Helper method to compile a Mustache template. * * @see Mustache_Compiler::compile * * @param string $source * * @return string generated Mustache template class code */ private function compile($source) { $tree = $this->parse($source); $name = $this->getTemplateClassName($source); $this->log( Mustache_Logger::INFO, 'Compiling template to "{className}" class', array('className' => $name) ); $compiler = $this->getCompiler(); $compiler->setPragmas($this->getPragmas()); return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags); } /** * Add a log record if logging is enabled. * * @param int $level The logging level * @param string $message The log message * @param array $context The log context */ private function log($level, $message, array $context = array()) { if (isset($this->logger)) { $this->logger->log($level, $message, $context); } } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A Mustache Exception interface. */ interface Mustache_Exception { // This space intentionally left blank. } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A collection of helpers for a Mustache instance. */ class Mustache_HelperCollection { private $helpers = array(); /** * Helper Collection constructor. * * Optionally accepts an array (or Traversable) of `$name => $helper` pairs. * * @throws Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable * * @param array|Traversable $helpers (default: null) */ public function __construct($helpers = null) { if ($helpers === null) { return; } if (!is_array($helpers) && !$helpers instanceof Traversable) { throw new Mustache_Exception_InvalidArgumentException('HelperCollection constructor expects an array of helpers'); } foreach ($helpers as $name => $helper) { $this->add($name, $helper); } } /** * Magic mutator. * * @see Mustache_HelperCollection::add * * @param string $name * @param mixed $helper */ public function __set($name, $helper) { $this->add($name, $helper); } /** * Add a helper to this collection. * * @param string $name * @param mixed $helper */ public function add($name, $helper) { $this->helpers[$name] = $helper; } /** * Magic accessor. * * @see Mustache_HelperCollection::get * * @param string $name * * @return mixed Helper */ public function __get($name) { return $this->get($name); } /** * Get a helper by name. * * @throws Mustache_Exception_UnknownHelperException If helper does not exist. * * @param string $name * * @return mixed Helper */ public function get($name) { if (!$this->has($name)) { throw new Mustache_Exception_UnknownHelperException($name); } return $this->helpers[$name]; } /** * Magic isset(). * * @see Mustache_HelperCollection::has * * @param string $name * * @return bool True if helper is present */ public function __isset($name) { return $this->has($name); } /** * Check whether a given helper is present in the collection. * * @param string $name * * @return bool True if helper is present */ public function has($name) { return array_key_exists($name, $this->helpers); } /** * Magic unset(). * * @see Mustache_HelperCollection::remove * * @param string $name */ public function __unset($name) { $this->remove($name); } /** * Check whether a given helper is present in the collection. * * @throws Mustache_Exception_UnknownHelperException if the requested helper is not present. * * @param string $name */ public function remove($name) { if (!$this->has($name)) { throw new Mustache_Exception_UnknownHelperException($name); } unset($this->helpers[$name]); } /** * Clear the helper collection. * * Removes all helpers from this collection */ public function clear() { $this->helpers = array(); } /** * Check whether the helper collection is empty. * * @return bool True if the collection is empty */ public function isEmpty() { return empty($this->helpers); } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Lambda Helper. * * Passed as the second argument to section lambdas (higher order sections), * giving them access to a `render` method for rendering a string with the * current context. */ class Mustache_LambdaHelper { private $mustache; private $context; /** * Mustache Lambda Helper constructor. * * @param Mustache_Engine $mustache Mustache engine instance. * @param Mustache_Context $context Rendering context. */ public function __construct(Mustache_Engine $mustache, Mustache_Context $context) { $this->mustache = $mustache; $this->context = $context; } /** * Render a string as a Mustache template with the current rendering context. * * @param string $string * * @return string Rendered template. */ public function render($string) { return $this->mustache ->loadLambda((string) $string) ->renderInternal($this->context); } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Template Loader interface. */ interface Mustache_Loader { /** * Load a Template by name. * * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. * * @param string $name * * @return string Mustache Template source */ public function load($name); } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Describes a Mustache logger instance. * * This is identical to the Psr\Log\LoggerInterface. * * The message MUST be a string or object implementing __toString(). * * The message MAY contain placeholders in the form: {foo} where foo * will be replaced by the context data in key "foo". * * The context array can contain arbitrary data, the only assumption that * can be made by implementors is that if an Exception instance is given * to produce a stack trace, it MUST be in a key named "exception". * * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md * for the full interface specification. */ interface Mustache_Logger { /** * Psr\Log compatible log levels. */ const EMERGENCY = 'emergency'; const ALERT = 'alert'; const CRITICAL = 'critical'; const ERROR = 'error'; const WARNING = 'warning'; const NOTICE = 'notice'; const INFO = 'info'; const DEBUG = 'debug'; /** * System is unusable. * * @param string $message * @param array $context */ public function emergency($message, array $context = array()); /** * Action must be taken immediately. * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * * @param string $message * @param array $context */ public function alert($message, array $context = array()); /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string $message * @param array $context */ public function critical($message, array $context = array()); /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param array $context */ public function error($message, array $context = array()); /** * 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 $message * @param array $context */ public function warning($message, array $context = array()); /** * Normal but significant events. * * @param string $message * @param array $context */ public function notice($message, array $context = array()); /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string $message * @param array $context */ public function info($message, array $context = array()); /** * Detailed debug information. * * @param string $message * @param array $context */ public function debug($message, array $context = array()); /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context */ public function log($level, $message, array $context = array()); } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Parser class. * * This class is responsible for turning a set of Mustache tokens into a parse tree. */ class Mustache_Parser { private $lineNum; private $lineTokens; private $pragmas; private $defaultPragmas = array(); private $pragmaFilters; private $pragmaBlocks; /** * Process an array of Mustache tokens and convert them into a parse tree. * * @param array $tokens Set of Mustache tokens * * @return array Mustache token parse tree */ public function parse(array $tokens = array()) { $this->lineNum = -1; $this->lineTokens = 0; $this->pragmas = $this->defaultPragmas; $this->pragmaFilters = isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS]); $this->pragmaBlocks = isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS]); return $this->buildTree($tokens); } /** * Enable pragmas across all templates, regardless of the presence of pragma * tags in the individual templates. * * @internal Users should set global pragmas in Mustache_Engine, not here :) * * @param string[] $pragmas */ public function setPragmas(array $pragmas) { $this->pragmas = array(); foreach ($pragmas as $pragma) { $this->enablePragma($pragma); } $this->defaultPragmas = $this->pragmas; } /** * Helper method for recursively building a parse tree. * * @throws Mustache_Exception_SyntaxException when nesting errors or mismatched section tags are encountered. * * @param array &$tokens Set of Mustache tokens * @param array $parent Parent token (default: null) * * @return array Mustache Token parse tree */ private function buildTree(array &$tokens, array $parent = null) { $nodes = array(); while (!empty($tokens)) { $token = array_shift($tokens); if ($token[Mustache_Tokenizer::LINE] === $this->lineNum) { $this->lineTokens++; } else { $this->lineNum = $token[Mustache_Tokenizer::LINE]; $this->lineTokens = 0; } if ($this->pragmaFilters && isset($token[Mustache_Tokenizer::NAME])) { list($name, $filters) = $this->getNameAndFilters($token[Mustache_Tokenizer::NAME]); if (!empty($filters)) { $token[Mustache_Tokenizer::NAME] = $name; $token[Mustache_Tokenizer::FILTERS] = $filters; } } switch ($token[Mustache_Tokenizer::TYPE]) { case Mustache_Tokenizer::T_DELIM_CHANGE: $this->checkIfTokenIsAllowedInParent($parent, $token); $this->clearStandaloneLines($nodes, $tokens); break; case Mustache_Tokenizer::T_SECTION: case Mustache_Tokenizer::T_INVERTED: $this->checkIfTokenIsAllowedInParent($parent, $token); $this->clearStandaloneLines($nodes, $tokens); $nodes[] = $this->buildTree($tokens, $token); break; case Mustache_Tokenizer::T_END_SECTION: if (!isset($parent)) { $msg = sprintf( 'Unexpected closing tag: /%s on line %d', $token[Mustache_Tokenizer::NAME], $token[Mustache_Tokenizer::LINE] ); throw new Mustache_Exception_SyntaxException($msg, $token); } if ($token[Mustache_Tokenizer::NAME] !== $parent[Mustache_Tokenizer::NAME]) { $msg = sprintf( 'Nesting error: %s (on line %d) vs. %s (on line %d)', $parent[Mustache_Tokenizer::NAME], $parent[Mustache_Tokenizer::LINE], $token[Mustache_Tokenizer::NAME], $token[Mustache_Tokenizer::LINE] ); throw new Mustache_Exception_SyntaxException($msg, $token); } $this->clearStandaloneLines($nodes, $tokens); $parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX]; $parent[Mustache_Tokenizer::NODES] = $nodes; return $parent; case Mustache_Tokenizer::T_PARTIAL: $this->checkIfTokenIsAllowedInParent($parent, $token); //store the whitespace prefix for laters! if ($indent = $this->clearStandaloneLines($nodes, $tokens)) { $token[Mustache_Tokenizer::INDENT] = $indent[Mustache_Tokenizer::VALUE]; } $nodes[] = $token; break; case Mustache_Tokenizer::T_PARENT: $this->checkIfTokenIsAllowedInParent($parent, $token); $nodes[] = $this->buildTree($tokens, $token); break; case Mustache_Tokenizer::T_BLOCK_VAR: if ($this->pragmaBlocks) { // BLOCKS pragma is enabled, let's do this! if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; } $this->clearStandaloneLines($nodes, $tokens); $nodes[] = $this->buildTree($tokens, $token); } else { // pretend this was just a normal "escaped" token... $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_ESCAPED; // TODO: figure out how to figure out if there was a space after this dollar: $token[Mustache_Tokenizer::NAME] = '$' . $token[Mustache_Tokenizer::NAME]; $nodes[] = $token; } break; case Mustache_Tokenizer::T_PRAGMA: $this->enablePragma($token[Mustache_Tokenizer::NAME]); // no break case Mustache_Tokenizer::T_COMMENT: $this->clearStandaloneLines($nodes, $tokens); $nodes[] = $token; break; default: $nodes[] = $token; break; } } if (isset($parent)) { $msg = sprintf( 'Missing closing tag: %s opened on line %d', $parent[Mustache_Tokenizer::NAME], $parent[Mustache_Tokenizer::LINE] ); throw new Mustache_Exception_SyntaxException($msg, $parent); } return $nodes; } /** * Clear standalone line tokens. * * Returns a whitespace token for indenting partials, if applicable. * * @param array $nodes Parsed nodes. * @param array $tokens Tokens to be parsed. * * @return array|null Resulting indent token, if any. */ private function clearStandaloneLines(array &$nodes, array &$tokens) { if ($this->lineTokens > 1) { // this is the third or later node on this line, so it can't be standalone return; } $prev = null; if ($this->lineTokens === 1) { // this is the second node on this line, so it can't be standalone // unless the previous node is whitespace. if ($prev = end($nodes)) { if (!$this->tokenIsWhitespace($prev)) { return; } } } if ($next = reset($tokens)) { // If we're on a new line, bail. if ($next[Mustache_Tokenizer::LINE] !== $this->lineNum) { return; } // If the next token isn't whitespace, bail. if (!$this->tokenIsWhitespace($next)) { return; } if (count($tokens) !== 1) { // Unless it's the last token in the template, the next token // must end in newline for this to be standalone. if (substr($next[Mustache_Tokenizer::VALUE], -1) !== "\n") { return; } } // Discard the whitespace suffix array_shift($tokens); } if ($prev) { // Return the whitespace prefix, if any return array_pop($nodes); } } /** * Check whether token is a whitespace token. * * True if token type is T_TEXT and value is all whitespace characters. * * @param array $token * * @return bool True if token is a whitespace token */ private function tokenIsWhitespace(array $token) { if ($token[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_TEXT) { return preg_match('/^\s*$/', $token[Mustache_Tokenizer::VALUE]); } return false; } /** * Check whether a token is allowed inside a parent tag. * * @throws Mustache_Exception_SyntaxException if an invalid token is found inside a parent tag. * * @param array|null $parent * @param array $token */ private function checkIfTokenIsAllowedInParent($parent, array $token) { if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token); } } /** * Split a tag name into name and filters. * * @param string $name * * @return array [Tag name, Array of filters] */ private function getNameAndFilters($name) { $filters = array_map('trim', explode('|', $name)); $name = array_shift($filters); return array($name, $filters); } /** * Enable a pragma. * * @param string $name */ private function enablePragma($name) { $this->pragmas[$name] = true; switch ($name) { case Mustache_Engine::PRAGMA_BLOCKS: $this->pragmaBlocks = true; break; case Mustache_Engine::PRAGMA_FILTERS: $this->pragmaFilters = true; break; } } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Abstract Mustache Template class. * * @abstract */ abstract class Mustache_Template { /** * @var Mustache_Engine */ protected $mustache; /** * @var bool */ protected $strictCallables = false; /** * Mustache Template constructor. * * @param Mustache_Engine $mustache */ public function __construct(Mustache_Engine $mustache) { $this->mustache = $mustache; } /** * Mustache Template instances can be treated as a function and rendered by simply calling them. * * $m = new Mustache_Engine; * $tpl = $m->loadTemplate('Hello, {{ name }}!'); * echo $tpl(array('name' => 'World')); // "Hello, World!" * * @see Mustache_Template::render * * @param mixed $context Array or object rendering context (default: array()) * * @return string Rendered template */ public function __invoke($context = array()) { return $this->render($context); } /** * Render this template given the rendering context. * * @param mixed $context Array or object rendering context (default: array()) * * @return string Rendered template */ public function render($context = array()) { return $this->renderInternal( $this->prepareContextStack($context) ); } /** * Internal rendering method implemented by Mustache Template concrete subclasses. * * This is where the magic happens :) * * NOTE: This method is not part of the Mustache.php public API. * * @param Mustache_Context $context * @param string $indent (default: '') * * @return string Rendered template */ abstract public function renderInternal(Mustache_Context $context, $indent = ''); /** * Tests whether a value should be iterated over (e.g. in a section context). * * In most languages there are two distinct array types: list and hash (or whatever you want to call them). Lists * should be iterated, hashes should be treated as objects. Mustache follows this paradigm for Ruby, Javascript, * Java, Python, etc. * * PHP, however, treats lists and hashes as one primitive type: array. So Mustache.php needs a way to distinguish * between between a list of things (numeric, normalized array) and a set of variables to be used as section context * (associative array). In other words, this will be iterated over: * * $items = array( * array('name' => 'foo'), * array('name' => 'bar'), * array('name' => 'baz'), * ); * * ... but this will be used as a section context block: * * $items = array( * 1 => array('name' => 'foo'), * 'banana' => array('name' => 'bar'), * 42 => array('name' => 'baz'), * ); * * @param mixed $value * * @return bool True if the value is 'iterable' */ protected function isIterable($value) { switch (gettype($value)) { case 'object': return $value instanceof Traversable; case 'array': $i = 0; foreach ($value as $k => $v) { if ($k !== $i++) { return false; } } return true; default: return false; } } /** * Helper method to prepare the Context stack. * * Adds the Mustache HelperCollection to the stack's top context frame if helpers are present. * * @param mixed $context Optional first context frame (default: null) * * @return Mustache_Context */ protected function prepareContextStack($context = null) { $stack = new Mustache_Context(); $helpers = $this->mustache->getHelpers(); if (!$helpers->isEmpty()) { $stack->push($helpers); } if (!empty($context)) { $stack->push($context); } return $stack; } /** * Resolve a context value. * * Invoke the value if it is callable, otherwise return the value. * * @param mixed $value * @param Mustache_Context $context * @param string $indent * * @return string */ protected function resolveValue($value, Mustache_Context $context, $indent = '') { if (($this->strictCallables ? is_object($value) : !is_string($value)) && is_callable($value)) { return $this->mustache ->loadLambda((string) call_user_func($value)) ->renderInternal($context, $indent); } return $value; } } <?php /* * This file is part of Mustache.php. * * (c) 2010-2015 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * Mustache Tokenizer class. * * This class is responsible for turning raw template source into a set of Mustache tokens. */ class Mustache_Tokenizer { // Finite state machine states const IN_TEXT = 0; const IN_TAG_TYPE = 1; const IN_TAG = 2; // Token types const T_SECTION = '#'; const T_INVERTED = '^'; const T_END_SECTION = '/'; const T_COMMENT = '!'; const T_PARTIAL = '>'; const T_PARENT = '<'; const T_DELIM_CHANGE = '='; const T_ESCAPED = '_v'; const T_UNESCAPED = '{'; const T_UNESCAPED_2 = '&'; const T_TEXT = '_t'; const T_PRAGMA = '%'; const T_BLOCK_VAR = '$'; const T_BLOCK_ARG = '$arg'; // Valid token types private static $tagTypes = array( self::T_SECTION => true, self::T_INVERTED => true, self::T_END_SECTION => true, self::T_COMMENT => true, self::T_PARTIAL => true, self::T_PARENT => true, self::T_DELIM_CHANGE => true, self::T_ESCAPED => true, self::T_UNESCAPED => true, self::T_UNESCAPED_2 => true, self::T_PRAGMA => true, self::T_BLOCK_VAR => true, ); // Token properties const TYPE = 'type'; const NAME = 'name'; const OTAG = 'otag'; const CTAG = 'ctag'; const LINE = 'line'; const INDEX = 'index'; const END = 'end'; const INDENT = 'indent'; const NODES = 'nodes'; const VALUE = 'value'; const FILTERS = 'filters'; private $state; private $tagType; private $buffer; private $tokens; private $seenTag; private $line; private $otag; private $ctag; private $otagLen; private $ctagLen; /** * Scan and tokenize template source. * * @throws Mustache_Exception_SyntaxException when mismatched section tags are encountered. * * @param string $text Mustache template source to tokenize * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null) * * @return array Set of Mustache tokens */ public function scan($text, $delimiters = null) { // Setting mbstring.func_overload makes things *really* slow. // Let's do everyone a favor and scan this string as ASCII instead. $encoding = null; if (function_exists('mb_internal_encoding') && ini_get('mbstring.func_overload') & 2) { $encoding = mb_internal_encoding(); mb_internal_encoding('ASCII'); } $this->reset(); if ($delimiters = trim($delimiters)) { $this->setDelimiters($delimiters); } $len = strlen($text); for ($i = 0; $i < $len; $i++) { switch ($this->state) { case self::IN_TEXT: if ($this->tagChange($this->otag, $this->otagLen, $text, $i)) { $i--; $this->flushBuffer(); $this->state = self::IN_TAG_TYPE; } else { $char = $text[$i]; $this->buffer .= $char; if ($char === "\n") { $this->flushBuffer(); $this->line++; } } break; case self::IN_TAG_TYPE: $i += $this->otagLen - 1; $char = $text[$i + 1]; if (isset(self::$tagTypes[$char])) { $tag = $char; $this->tagType = $tag; } else { $tag = null; $this->tagType = self::T_ESCAPED; } if ($this->tagType === self::T_DELIM_CHANGE) { $i = $this->changeDelimiters($text, $i); $this->state = self::IN_TEXT; } elseif ($this->tagType === self::T_PRAGMA) { $i = $this->addPragma($text, $i); $this->state = self::IN_TEXT; } else { if ($tag !== null) { $i++; } $this->state = self::IN_TAG; } $this->seenTag = $i; break; default: if ($this->tagChange($this->ctag, $this->ctagLen, $text, $i)) { $token = array( self::TYPE => $this->tagType, self::NAME => trim($this->buffer), self::OTAG => $this->otag, self::CTAG => $this->ctag, self::LINE => $this->line, self::INDEX => ($this->tagType === self::T_END_SECTION) ? $this->seenTag - $this->otagLen : $i + $this->ctagLen, ); if ($this->tagType === self::T_UNESCAPED) { // Clean up `{{{ tripleStache }}}` style tokens. if ($this->ctag === '}}') { if (($i + 2 < $len) && $text[$i + 2] === '}') { $i++; } else { $msg = sprintf( 'Mismatched tag delimiters: %s on line %d', $token[self::NAME], $token[self::LINE] ); throw new Mustache_Exception_SyntaxException($msg, $token); } } else { $lastName = $token[self::NAME]; if (substr($lastName, -1) === '}') { $token[self::NAME] = trim(substr($lastName, 0, -1)); } else { $msg = sprintf( 'Mismatched tag delimiters: %s on line %d', $token[self::NAME], $token[self::LINE] ); throw new Mustache_Exception_SyntaxException($msg, $token); } } } $this->buffer = ''; $i += $this->ctagLen - 1; $this->state = self::IN_TEXT; $this->tokens[] = $token; } else { $this->buffer .= $text[$i]; } break; } } $this->flushBuffer(); // Restore the user's encoding... if ($encoding) { mb_internal_encoding($encoding); } return $this->tokens; } /** * Helper function to reset tokenizer internal state. */ private function reset() { $this->state = self::IN_TEXT; $this->tagType = null; $this->buffer = ''; $this->tokens = array(); $this->seenTag = false; $this->line = 0; $this->otag = '{{'; $this->ctag = '}}'; $this->otagLen = 2; $this->ctagLen = 2; } /** * Flush the current buffer to a token. */ private function flushBuffer() { if (strlen($this->buffer) > 0) { $this->tokens[] = array( self::TYPE => self::T_TEXT, self::LINE => $this->line, self::VALUE => $this->buffer, ); $this->buffer = ''; } } /** * Change the current Mustache delimiters. Set new `otag` and `ctag` values. * * @param string $text Mustache template source * @param int $index Current tokenizer index * * @return int New index value */ private function changeDelimiters($text, $index) { $startIndex = strpos($text, '=', $index) + 1; $close = '=' . $this->ctag; $closeIndex = strpos($text, $close, $index); $this->setDelimiters(trim(substr($text, $startIndex, $closeIndex - $startIndex))); $this->tokens[] = array( self::TYPE => self::T_DELIM_CHANGE, self::LINE => $this->line, ); return $closeIndex + strlen($close) - 1; } /** * Set the current Mustache `otag` and `ctag` delimiters. * * @param string $delimiters */ private function setDelimiters($delimiters) { list($otag, $ctag) = explode(' ', $delimiters); $this->otag = $otag; $this->ctag = $ctag; $this->otagLen = strlen($otag); $this->ctagLen = strlen($ctag); } /** * Add pragma token. * * Pragmas are hoisted to the front of the template, so all pragma tokens * will appear at the front of the token list. * * @param string $text * @param int $index * * @return int New index value */ private function addPragma($text, $index) { $end = strpos($text, $this->ctag, $index); $pragma = trim(substr($text, $index + 2, $end - $index - 2)); // Pragmas are hoisted to the front of the template. array_unshift($this->tokens, array( self::TYPE => self::T_PRAGMA, self::NAME => $pragma, self::LINE => 0, )); return $end + $this->ctagLen - 1; } /** * Test whether it's time to change tags. * * @param string $tag Current tag name * @param int $tagLen Current tag name length * @param string $text Mustache template source * @param int $index Current tokenizer index * * @return bool True if this is a closing section tag */ private function tagChange($tag, $tagLen, $text, $index) { return substr($text, $index, $tagLen) === $tag; } } <?php /** * PEAR package builder * * Inspired by Twig's create_pear_package.php. * @link https://raw.github.com/fabpot/Twig/master/bin/create_pear_package.php * @author Twig Team * @license BSD license */ if (!isset($argv[1]) || $argv[1] === '-h' || $argv[1] === '--help') { echo 'usage: php ' . $argv[0] . ' <version> <stability>' . PHP_EOL; echo PHP_EOL; echo ' version:' . PHP_EOL; echo ' Version of the package, in the form of major.minor.bug' . PHP_EOL; echo PHP_EOL; echo ' stability:' . PHP_EOL; echo ' One of alpha, beta, stable' . PHP_EOL; die(); } if (!isset($argv[2])) { die('You must provide the stability (alpha, beta, or stable)'); } $context = array( 'date' => gmdate('Y-m-d'), 'time' => gmdate('H:m:00'), 'version' => $argv[1], 'api_version' => $argv[1], 'stability' => $argv[2], 'api_stability' => $argv[2], ); $context['files'] = ''; $path = realpath(dirname(__FILE__).'/../library/Requests'); foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { if (preg_match('/\.php$/', $file)) { $name = str_replace($path . DIRECTORY_SEPARATOR, '', $file); $name = str_replace(DIRECTORY_SEPARATOR, '/', $name); $context['files'][] = "\t\t\t\t\t" . '<file install-as="Requests/' . $name . '" name="' . $name . '" role="php" />'; } } $context['files'] = implode("\n", $context['files']); $template = file_get_contents(dirname(__FILE__).'/../package.xml.tpl'); $content = preg_replace_callback('/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/', 'replace_parameters', $template); file_put_contents(dirname(__FILE__).'/../package.xml', $content); function replace_parameters($matches) { global $context; return isset($context[$matches[1]]) ? $context[$matches[1]] : null; } <?php // First, include Requests include('../library/Requests.php'); // Next, make sure Requests can load internal classes Requests::register_autoloader(); // Now let's make a request! $options = array( 'auth' => array('someuser', 'password') ); $request = Requests::get('http://httpbin.org/basic-auth/someuser/password', array(), $options); // Check what we received var_dump($request);<?php // First, include Requests include('../library/Requests.php'); // Next, make sure Requests can load internal classes Requests::register_autoloader(); // Now let's make a request! $request = Requests::get('http://httpbin.org/get', array('Accept' => 'application/json')); // Check what we received var_dump($request);<?php // First, include Requests include('../library/Requests.php'); // Next, make sure Requests can load internal classes Requests::register_autoloader(); // Setup what we want to request $requests = array( array( 'url' => 'http://httpbin.org/get', 'headers' => array('Accept' => 'application/javascript'), ), 'post' => array( 'url' => 'http://httpbin.org/post', 'data' => array('mydata' => 'something'), ), 'delayed' => array( 'url' => 'http://httpbin.org/delay/10', 'options' => array( 'timeout' => 20, ), ), ); // Setup a callback function my_callback(&$request, $id) { var_dump($id, $request); } // Tell Requests to use the callback $options = array( 'complete' => 'my_callback', ); // Send the request! $responses = Requests::request_multiple($requests, $options); // Note: the response from the above call will be an associative array matching // $requests with the response data, however we've already handled it in // my_callback() anyway! // // If you don't believe me, uncomment this: # var_dump($responses);<?php // First, include Requests include('../library/Requests.php'); // Next, make sure Requests can load internal classes Requests::register_autoloader(); // Now let's make a request! $request = Requests::post('http://httpbin.org/post', array(), array('mydata' => 'something')); // Check what we received var_dump($request);<?php // First, include Requests include('../library/Requests.php'); // Next, make sure Requests can load internal classes Requests::register_autoloader(); // Now let's make a request via a proxy. $options = array( 'proxy' => '127.0.0.1:8080', // syntax: host:port, eg 12.13.14.14:8080 or someproxy.com:3128 // If you need to authenticate, use the following syntax: // 'proxy' => array( '127.0.0.1:8080', 'username', 'password' ), ); $request = Requests::get('http://httpbin.org/ip', array(), $options ); // See result var_dump($request->body); <?php // First, include Requests include('../library/Requests.php'); // Next, make sure Requests can load internal classes Requests::register_autoloader(); // Set up our session $session = new Requests_Session('http://httpbin.org/'); $session->headers['Accept'] = 'application/json'; $session->useragent = 'Awesomesauce'; // Now let's make a request! $request = $session->get('/get'); // Check what we received var_dump($request); // Let's check our user agent! $request = $session->get('/user-agent'); // And check again var_dump($request); <?php /** * Requests for PHP * * Inspired by Requests for Python. * * Based on concepts from SimplePie_File, RequestCore and WP_Http. * * @package Requests */ /** * Requests for PHP * * Inspired by Requests for Python. * * Based on concepts from SimplePie_File, RequestCore and WP_Http. * * @package Requests */ class Requests { /** * POST method * * @var string */ const POST = 'POST'; /** * PUT method * * @var string */ const PUT = 'PUT'; /** * GET method * * @var string */ const GET = 'GET'; /** * HEAD method * * @var string */ const HEAD = 'HEAD'; /** * DELETE method * * @var string */ const DELETE = 'DELETE'; /** * PATCH method * * @link http://tools.ietf.org/html/rfc5789 * @var string */ const PATCH = 'PATCH'; /** * Current version of Requests * * @var string */ const VERSION = '1.6.1'; /** * Registered transport classes * * @var array */ protected static $transports = array(); /** * Selected transport name * * Use {@see get_transport()} instead * * @var string|null */ public static $transport = null; /** * This is a static class, do not instantiate it * * @codeCoverageIgnore */ private function __construct() {} /** * Autoloader for Requests * * Register this with {@see register_autoloader()} if you'd like to avoid * having to create your own. * * (You can also use `spl_autoload_register` directly if you'd prefer.) * * @codeCoverageIgnore * * @param string $class Class name to load */ public static function autoloader($class) { // Check that the class starts with "Requests" if (strpos($class, 'Requests') !== 0) { return; } $file = str_replace('_', '/', $class); if (file_exists(dirname(__FILE__) . '/' . $file . '.php')) { require_once(dirname(__FILE__) . '/' . $file . '.php'); } } /** * Register the built-in autoloader * * @codeCoverageIgnore */ public static function register_autoloader() { spl_autoload_register(array('Requests', 'autoloader')); } /** * Register a transport * * @param string $transport Transport class to add, must support the Requests_Transport interface */ public static function add_transport($transport) { if (empty(self::$transports)) { self::$transports = array( 'Requests_Transport_cURL', 'Requests_Transport_fsockopen', ); } self::$transports = array_merge(self::$transports, array($transport)); } /** * Get a working transport * * @throws Requests_Exception If no valid transport is found (`notransport`) * @return Requests_Transport */ protected static function get_transport() { // Caching code, don't bother testing coverage // @codeCoverageIgnoreStart if (self::$transport !== null) { return new self::$transport(); } // @codeCoverageIgnoreEnd if (empty(self::$transports)) { self::$transports = array( 'Requests_Transport_cURL', 'Requests_Transport_fsockopen', ); } // Find us a working transport foreach (self::$transports as $class) { if (!class_exists($class)) continue; $result = call_user_func(array($class, 'test')); if ($result) { self::$transport = $class; break; } } if (self::$transport === null) { throw new Requests_Exception('No working transports found', 'notransport', self::$transports); } return new self::$transport(); } /**#@+ * @see request() * @param string $url * @param array $headers * @param array $options * @return Requests_Response */ /** * Send a GET request */ public static function get($url, $headers = array(), $options = array()) { return self::request($url, $headers, null, self::GET, $options); } /** * Send a HEAD request */ public static function head($url, $headers = array(), $options = array()) { return self::request($url, $headers, null, self::HEAD, $options); } /** * Send a DELETE request */ public static function delete($url, $headers = array(), $options = array()) { return self::request($url, $headers, null, self::DELETE, $options); } /**#@-*/ /**#@+ * @see request() * @param string $url * @param array $headers * @param array $data * @param array $options * @return Requests_Response */ /** * Send a POST request */ public static function post($url, $headers = array(), $data = array(), $options = array()) { return self::request($url, $headers, $data, self::POST, $options); } /** * Send a PUT request */ public static function put($url, $headers = array(), $data = array(), $options = array()) { return self::request($url, $headers, $data, self::PUT, $options); } /** * Send a PATCH request * * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the * specification recommends that should send an ETag * * @link http://tools.ietf.org/html/rfc5789 */ public static function patch($url, $headers, $data = array(), $options = array()) { return self::request($url, $headers, $data, self::PATCH, $options); } /**#@-*/ /** * Main interface for HTTP requests * * This method initiates a request and sends it via a transport before * parsing. * * The `$options` parameter takes an associative array with the following * options: * * - `timeout`: How long should we wait for a response? * (integer, seconds, default: 10) * - `useragent`: Useragent to send to the server * (string, default: php-requests/$version) * - `follow_redirects`: Should we follow 3xx redirects? * (boolean, default: true) * - `redirects`: How many times should we redirect before erroring? * (integer, default: 10) * - `blocking`: Should we block processing on this request? * (boolean, default: true) * - `filename`: File to stream the body to instead. * (string|boolean, default: false) * - `auth`: Authentication handler or array of user/password details to use * for Basic authentication * (Requests_Auth|array|boolean, default: false) * - `proxy`: Proxy details to use for proxy by-passing and authentication * (Requests_Proxy|array|boolean, default: false) * - `idn`: Enable IDN parsing * (boolean, default: true) * - `transport`: Custom transport. Either a class name, or a * transport object. Defaults to the first working transport from * {@see getTransport()} * (string|Requests_Transport, default: {@see getTransport()}) * - `hooks`: Hooks handler. * (Requests_Hooker, default: new Requests_Hooks()) * - `verify`: Should we verify SSL certificates? Allows passing in a custom * certificate file as a string. (Using true uses the system-wide root * certificate store instead, but this may have different behaviour * across transports.) * (string|boolean, default: library/Requests/Transport/cacert.pem) * - `verifyname`: Should we verify the common name in the SSL certificate? * (boolean: default, true) * * @throws Requests_Exception On invalid URLs (`nonhttp`) * * @param string $url URL to request * @param array $headers Extra headers to send with the request * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests * @param string $type HTTP request type (use Requests constants) * @param array $options Options for the request (see description for more information) * @return Requests_Response */ public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array()) { if (empty($options['type'])) { $options['type'] = $type; } $options = array_merge(self::get_default_options(), $options); self::set_defaults($url, $headers, $data, $type, $options); $options['hooks']->dispatch('requests.before_request', array(&$url, &$headers, &$data, &$type, &$options)); if (!empty($options['transport'])) { $transport = $options['transport']; if (is_string($options['transport'])) { $transport = new $transport(); } } else { $transport = self::get_transport(); } $response = $transport->request($url, $headers, $data, $options); $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options)); return self::parse_response($response, $url, $headers, $data, $options); } /** * Send multiple HTTP requests simultaneously * * The `$requests` parameter takes an associative or indexed array of * request fields. The key of each request can be used to match up the * request with the returned data, or with the request passed into your * `multiple.request.complete` callback. * * The request fields value is an associative array with the following keys: * * - `url`: Request URL Same as the `$url` parameter to * {@see Requests::request} * (string, required) * - `headers`: Associative array of header fields. Same as the `$headers` * parameter to {@see Requests::request} * (array, default: `array()`) * - `data`: Associative array of data fields or a string. Same as the * `$data` parameter to {@see Requests::request} * (array|string, default: `array()`) * - `type`: HTTP request type (use Requests constants). Same as the `$type` * parameter to {@see Requests::request} * (string, default: `Requests::GET`) * - `data`: Associative array of options. Same as the `$options` parameter * to {@see Requests::request} * (array, default: see {@see Requests::request}) * - `cookies`: Associative array of cookie name to value, or cookie jar. * (array|Requests_Cookie_Jar) * * If the `$options` parameter is specified, individual requests will * inherit options from it. This can be used to use a single hooking system, * or set all the types to `Requests::POST`, for example. * * In addition, the `$options` parameter takes the following global options: * * - `complete`: A callback for when a request is complete. Takes two * parameters, a Requests_Response/Requests_Exception reference, and the * ID from the request array (Note: this can also be overridden on a * per-request basis, although that's a little silly) * (callback) * * @param array $requests Requests data (see description for more information) * @param array $options Global and default options (see {@see Requests::request}) * @return array Responses (either Requests_Response or a Requests_Exception object) */ public static function request_multiple($requests, $options = array()) { $options = array_merge(self::get_default_options(true), $options); if (!empty($options['hooks'])) { $options['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple')); if (!empty($options['complete'])) { $options['hooks']->register('multiple.request.complete', $options['complete']); } } foreach ($requests as $id => &$request) { if (!isset($request['headers'])) { $request['headers'] = array(); } if (!isset($request['data'])) { $request['data'] = array(); } if (!isset($request['type'])) { $request['type'] = self::GET; } if (!isset($request['options'])) { $request['options'] = $options; $request['options']['type'] = $request['type']; } else { if (empty($request['options']['type'])) { $request['options']['type'] = $request['type']; } $request['options'] = array_merge($options, $request['options']); } self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']); // Ensure we only hook in once if ($request['options']['hooks'] !== $options['hooks']) { $request['options']['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple')); if (!empty($request['options']['complete'])) { $request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']); } } } unset($request); if (!empty($options['transport'])) { $transport = $options['transport']; if (is_string($options['transport'])) { $transport = new $transport(); } } else { $transport = self::get_transport(); } $responses = $transport->request_multiple($requests, $options); foreach ($responses as $id => &$response) { // If our hook got messed with somehow, ensure we end up with the // correct response if (is_string($response)) { $request = $requests[$id]; self::parse_multiple($response, $request); $request['options']['hooks']->dispatch('multiple.request.complete', array(&$response, $id)); } } return $responses; } /** * Get the default options * * @see Requests::request() for values returned by this method * @param boolean $multirequest Is this a multirequest? * @return array Default option values */ protected static function get_default_options($multirequest = false) { $defaults = array( 'timeout' => 10, 'useragent' => 'php-requests/' . self::VERSION, 'redirected' => 0, 'redirects' => 10, 'follow_redirects' => true, 'blocking' => true, 'type' => self::GET, 'filename' => false, 'auth' => false, 'proxy' => false, 'cookies' => false, 'idn' => true, 'hooks' => null, 'transport' => null, 'verify' => dirname( __FILE__ ) . '/Requests/Transport/cacert.pem', 'verifyname' => true, ); if ($multirequest !== false) { $defaults['complete'] = null; } return $defaults; } /** * Set the default values * * @param string $url URL to request * @param array $headers Extra headers to send with the request * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests * @param string $type HTTP request type * @param array $options Options for the request * @return array $options */ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) { if (!preg_match('/^http(s)?:\/\//i', $url)) { throw new Requests_Exception('Only HTTP requests are handled.', 'nonhttp', $url); } if (empty($options['hooks'])) { $options['hooks'] = new Requests_Hooks(); } if (is_array($options['auth'])) { $options['auth'] = new Requests_Auth_Basic($options['auth']); } if ($options['auth'] !== false) { $options['auth']->register($options['hooks']); } if (!empty($options['proxy'])) { $options['proxy'] = new Requests_Proxy_HTTP($options['proxy']); } if ($options['proxy'] !== false) { $options['proxy']->register($options['hooks']); } if (is_array($options['cookies'])) { $options['cookies'] = new Requests_Cookie_Jar($options['cookies']); } elseif (empty($options['cookies'])) { $options['cookies'] = new Requests_Cookie_Jar(); } if ($options['cookies'] !== false) { $options['cookies']->register($options['hooks']); } if ($options['idn'] !== false) { $iri = new Requests_IRI($url); $iri->host = Requests_IDNAEncoder::encode($iri->ihost); $url = $iri->uri; } } /** * HTTP response parser * * @throws Requests_Exception On missing head/body separator (`requests.no_crlf_separator`) * @throws Requests_Exception On missing head/body separator (`noversion`) * @throws Requests_Exception On missing head/body separator (`toomanyredirects`) * * @param string $headers Full response text including headers and body * @param string $url Original request URL * @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects * @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects * @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects * @return Requests_Response */ protected static function parse_response($headers, $url, $req_headers, $req_data, $options) { $return = new Requests_Response(); if (!$options['blocking']) { return $return; } $return->raw = $headers; $return->url = $url; if (!$options['filename']) { if (($pos = strpos($headers, "\r\n\r\n")) === false) { // Crap! throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator'); } $headers = substr($return->raw, 0, $pos); $return->body = substr($return->raw, $pos + strlen("\n\r\n\r")); } else { $return->body = ''; } // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3) $headers = str_replace("\r\n", "\n", $headers); // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2) $headers = preg_replace('/\n[ \t]/', ' ', $headers); $headers = explode("\n", $headers); preg_match('#^HTTP/1\.\d[ \t]+(\d+)#i', array_shift($headers), $matches); if (empty($matches)) { throw new Requests_Exception('Response could not be parsed', 'noversion', $headers); } $return->status_code = (int) $matches[1]; if ($return->status_code >= 200 && $return->status_code < 300) { $return->success = true; } foreach ($headers as $header) { list($key, $value) = explode(':', $header, 2); $value = trim($value); preg_replace('#(\s+)#i', ' ', $value); $return->headers[$key] = $value; } if (isset($return->headers['transfer-encoding'])) { $return->body = self::decode_chunked($return->body); unset($return->headers['transfer-encoding']); } if (isset($return->headers['content-encoding'])) { $return->body = self::decompress($return->body); } //fsockopen and cURL compatibility if (isset($return->headers['connection'])) { unset($return->headers['connection']); } $options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options)); if ((in_array($return->status_code, array(300, 301, 302, 303, 307)) || $return->status_code > 307 && $return->status_code < 400) && $options['follow_redirects'] === true) { if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) { if ($return->status_code === 303) { $options['type'] = Requests::GET; } $options['redirected']++; $location = $return->headers['location']; if (strpos ($location, '/') === 0) { // relative redirect, for compatibility make it absolute $location = Requests_IRI::absolutize($url, $location); $location = $location->uri; } $redirected = self::request($location, $req_headers, $req_data, false, $options); $redirected->history[] = $return; return $redirected; } elseif ($options['redirected'] >= $options['redirects']) { throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return); } } $return->redirects = $options['redirected']; $options['hooks']->dispatch('requests.after_request', array(&$return, $req_headers, $req_data, $options)); return $return; } /** * Callback for `transport.internal.parse_response` * * Internal use only. Converts a raw HTTP response to a Requests_Response * while still executing a multiple request. * * @param string $headers Full response text including headers and body * @param array $request Request data as passed into {@see Requests::request_multiple()} * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object */ public static function parse_multiple(&$response, $request) { try { $response = self::parse_response($response, $request['url'], $request['headers'], $request['data'], $request['options']); } catch (Requests_Exception $e) { $response = $e; } } /** * Decoded a chunked body as per RFC 2616 * * @see http://tools.ietf.org/html/rfc2616#section-3.6.1 * @param string $data Chunked body * @return string Decoded body */ protected static function decode_chunked($data) { if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($data))) { return $data; } $decoded = ''; $encoded = $data; while (true) { $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches ); if (!$is_chunked) { // Looks like it's not chunked after all return $data; } $length = hexdec(trim($matches[1])); if ($length === 0) { // Ignore trailer headers return $decoded; } $chunk_length = strlen($matches[0]); $decoded .= $part = substr($encoded, $chunk_length, $length); $encoded = substr($encoded, $chunk_length + $length + 2); if (trim($encoded) === '0' || empty($encoded)) { return $decoded; } } // We'll never actually get down here // @codeCoverageIgnoreStart } // @codeCoverageIgnoreEnd /** * Convert a key => value array to a 'key: value' array for headers * * @param array $array Dictionary of header values * @return array List of headers */ public static function flatten($array) { $return = array(); foreach ($array as $key => $value) { $return[] = "$key: $value"; } return $return; } /** * Convert a key => value array to a 'key: value' array for headers * * @deprecated Misspelling of {@see Requests::flatten} * @param array $array Dictionary of header values * @return array List of headers */ public static function flattern($array) { return self::flatten($array); } /** * Decompress an encoded body * * Implements gzip, compress and deflate. Guesses which it is by attempting * to decode. * * @todo Make this smarter by defaulting to whatever the headers say first * @param string $data Compressed data in one of the above formats * @return string Decompressed string */ public static function decompress($data) { if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") { // Not actually compressed. Probably cURL ruining this for us. return $data; } if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) { return $decoded; } elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) { return $decoded; } elseif (($decoded = self::compatible_gzinflate($data)) !== false) { return $decoded; } elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) { return $decoded; } return $data; } /** * Decompression of deflated string while staying compatible with the majority of servers. * * Certain Servers will return deflated data with headers which PHP's gzinflate() * function cannot handle out of the box. The following function has been created from * various snippets on the gzinflate() PHP documentation. * * Warning: Magic numbers within. Due to the potential different formats that the compressed * data may be returned in, some "magic offsets" are needed to ensure proper decompression * takes place. For a simple progmatic way to determine the magic offset in use, see: * http://core.trac.wordpress.org/ticket/18273 * * @since 2.8.1 * @link http://core.trac.wordpress.org/ticket/18273 * @link http://au2.php.net/manual/en/function.gzinflate.php#70875 * @link http://au2.php.net/manual/en/function.gzinflate.php#77336 * * @param string $gzData String to decompress. * @return string|bool False on failure. */ public static function compatible_gzinflate($gzData) { // Compressed data might contain a full zlib header, if so strip it for // gzinflate() if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) { $i = 10; $flg = ord( substr($gzData, 3, 1) ); if ( $flg > 0 ) { if ( $flg & 4 ) { list($xlen) = unpack('v', substr($gzData, $i, 2) ); $i = $i + 2 + $xlen; } if ( $flg & 8 ) $i = strpos($gzData, "\0", $i) + 1; if ( $flg & 16 ) $i = strpos($gzData, "\0", $i) + 1; if ( $flg & 2 ) $i = $i + 2; } $decompressed = self::compatible_gzinflate( substr( $gzData, $i ) ); if ( false !== $decompressed ) { return $decompressed; } } // If the data is Huffman Encoded, we must first strip the leading 2 // byte Huffman marker for gzinflate() // The response is Huffman coded by many compressors such as // java.util.zip.Deflater, Ruby’s Zlib::Deflate, and .NET's // System.IO.Compression.DeflateStream. // // See http://decompres.blogspot.com/ for a quick explanation of this // data type $huffman_encoded = false; // low nibble of first byte should be 0x08 list( , $first_nibble ) = unpack( 'h', $gzData ); // First 2 bytes should be divisible by 0x1F list( , $first_two_bytes ) = unpack( 'n', $gzData ); if ( 0x08 == $first_nibble && 0 == ( $first_two_bytes % 0x1F ) ) $huffman_encoded = true; if ( $huffman_encoded ) { if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) ) return $decompressed; } if ( "\x50\x4b\x03\x04" == substr( $gzData, 0, 4 ) ) { // ZIP file format header // Offset 6: 2 bytes, General-purpose field // Offset 26: 2 bytes, filename length // Offset 28: 2 bytes, optional field length // Offset 30: Filename field, followed by optional field, followed // immediately by data list( , $general_purpose_flag ) = unpack( 'v', substr( $gzData, 6, 2 ) ); // If the file has been compressed on the fly, 0x08 bit is set of // the general purpose field. We can use this to differentiate // between a compressed document, and a ZIP file $zip_compressed_on_the_fly = ( 0x08 == (0x08 & $general_purpose_flag ) ); if ( ! $zip_compressed_on_the_fly ) { // Don't attempt to decode a compressed zip file return $gzData; } // Determine the first byte of data, based on the above ZIP header // offsets: $first_file_start = array_sum( unpack( 'v2', substr( $gzData, 26, 4 ) ) ); if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 30 + $first_file_start ) ) ) ) { return $decompressed; } return false; } // Finally fall back to straight gzinflate if ( false !== ( $decompressed = @gzinflate( $gzData ) ) ) { return $decompressed; } // Fallback for all above failing, not expected, but included for // debugging and preventing regressions and to track stats if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) ) { return $decompressed; } return false; } public static function match_domain($host, $reference) { // Check for a direct match if ($host === $reference) { return true; } // Calculate the valid wildcard match if the host is not an IP address // Also validates that the host has 3 parts or more, as per Firefox's // ruleset. $parts = explode('.', $host); if (ip2long($host) === false && count($parts) >= 3) { $parts[0] = '*'; $wildcard = implode('.', $parts); if ($wildcard === $reference) { return true; } } return false; } } <?php /** * Authentication provider interface * * @package Requests * @subpackage Authentication */ /** * Authentication provider interface * * Implement this interface to act as an authentication provider. * * Parameters should be passed via the constructor where possible, as this * makes it much easier for users to use your provider. * * @see Requests_Hooks * @package Requests * @subpackage Authentication */ interface Requests_Auth { /** * Register hooks as needed * * This method is called in {@see Requests::request} when the user has set * an instance as the 'auth' option. Use this callback to register all the * hooks you'll need. * * @see Requests_Hooks::register * @param Requests_Hooks $hooks Hook system */ public function register(Requests_Hooks &$hooks); }<?php /** * Basic Authentication provider * * @package Requests * @subpackage Authentication */ /** * Basic Authentication provider * * Provides a handler for Basic HTTP authentication via the Authorization * header. * * @package Requests * @subpackage Authentication */ class Requests_Auth_Basic implements Requests_Auth { /** * Username * * @var string */ public $user; /** * Password * * @var string */ public $pass; /** * Constructor * * @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`) * @param array|null $args Array of user and password. Must have exactly two elements */ public function __construct($args = null) { if (is_array($args)) { if (count($args) !== 2) { throw new Requests_Exception('Invalid number of arguments', 'authbasicbadargs'); } list($this->user, $this->pass) = $args; } } /** * Register the necessary callbacks * * @see curl_before_send * @see fsockopen_header * @param Requests_Hooks $hooks Hook system */ public function register(Requests_Hooks &$hooks) { $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); } /** * Set cURL parameters before the data is sent * * @param resource $handle cURL resource */ public function curl_before_send(&$handle) { curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString()); } /** * Add extra headers to the request before sending * * @param string $out HTTP header string */ public function fsockopen_header(&$out) { $out .= "Authorization: Basic " . base64_encode($this->getAuthString()) . "\r\n"; } /** * Get the authentication string (user:pass) * * @return string */ public function getAuthString() { return $this->user . ':' . $this->pass; } }<?php /** * Cookie storage object * * @package Requests * @subpackage Cookies */ /** * Cookie storage object * * @package Requests * @subpackage Cookies */ class Requests_Cookie { /** * * @var string */ public $name; /** * @var string */ public $value; /** * Cookie attributes * * Valid keys are (currently) path, domain, expires, max-age, secure and * httponly. * * @var array */ public $attributes = array(); /** * Create a new cookie object * * @param string $name * @param string $value * @param array $attributes Associative array of attribute data */ public function __construct($name, $value, $attributes = array()) { $this->name = $name; $this->value = $value; $this->attributes = $attributes; } /** * Format a cookie for a Cookie header * * This is used when sending cookies to a server. * * @return string Cookie formatted for Cookie header */ public function formatForHeader() { return sprintf('%s=%s', $this->name, $this->value); } /** * Format a cookie for a Set-Cookie header * * This is used when sending cookies to clients. This isn't really * applicable to client-side usage, but might be handy for debugging. * * @return string Cookie formatted for Set-Cookie header */ public function formatForSetCookie() { $header_value = $this->formatForHeader(); if (!empty($this->attributes)) { $parts = array(); foreach ($this->attributes as $key => $value) { // Ignore non-associative attributes if (is_numeric($key)) { $parts[] = $value; } else { $parts[] = sprintf('%s=%s', $key, $value); } } $header_value .= '; ' . implode('; ', $parts); } return $header_value; } /** * Get the cookie value * * Attributes and other data can be accessed via methods. */ public function __toString() { return $this->value; } /** * Parse a cookie string into a cookie object * * Based on Mozilla's parsing code in Firefox and related projects, which * is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265 * specifies some of this handling, but not in a thorough manner. * * @param string Cookie header value (from a Set-Cookie header) * @return Requests_Cookie Parsed cookie object */ public static function parse($string, $name = '') { $parts = explode(';', $string); $kvparts = array_shift($parts); if (!empty($name)) { $value = $string; } elseif (strpos($kvparts, '=') === false) { // Some sites might only have a value without the equals separator. // Deviate from RFC 6265 and pretend it was actually a blank name // (`=foo`) // // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 $name = ''; $value = $kvparts; } else { list($name, $value) = explode('=', $kvparts, 2); } $name = trim($name); $value = trim($value); // Attribute key are handled case-insensitively $attributes = new Requests_Utility_CaseInsensitiveDictionary(); if (!empty($parts)) { foreach ($parts as $part) { if (strpos($part, '=') === false) { $part_key = $part; $part_value = true; } else { list($part_key, $part_value) = explode('=', $part, 2); $part_value = trim($part_value); } $part_key = trim($part_key); $attributes[$part_key] = $part_value; } } return new Requests_Cookie($name, $value, $attributes); } /** * Parse all Set-Cookie headers from request headers * * @param Requests_Response_Headers $headers * @return array */ public static function parseFromHeaders(Requests_Response_Headers $headers) { $cookie_headers = $headers->getValues('Set-Cookie'); if (empty($cookie_headers)) { return array(); } $cookies = array(); foreach ($cookie_headers as $header) { $parsed = self::parse($header); $cookies[$parsed->name] = $parsed; } return $cookies; } } <?php /** * Cookie holder object * * @package Requests * @subpackage Cookies */ /** * Cookie holder object * * @package Requests * @subpackage Cookies */ class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate { /** * Actual item data * * @var array */ protected $cookies = array(); /** * Create a new jar * * @param array $cookies Existing cookie values */ public function __construct($cookies = array()) { $this->cookies = $cookies; } /** * Normalise cookie data into a Requests_Cookie * * @param string|Requests_Cookie $cookie * @return Requests_Cookie */ public function normalizeCookie($cookie, $key = null) { if ($cookie instanceof Requests_Cookie) { return $cookie; } return Requests_Cookie::parse($cookie, $key); } /** * Check if the given item exists * * @param string $key Item key * @return boolean Does the item exist? */ public function offsetExists($key) { return isset($this->cookies[$key]); } /** * Get the value for the item * * @param string $key Item key * @return string Item value */ public function offsetGet($key) { if (!isset($this->cookies[$key])) return null; return $this->cookies[$key]; } /** * Set the given item * * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) * * @param string $key Item name * @param string $value Item value */ public function offsetSet($key, $value) { if ($key === null) { throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); } $this->cookies[$key] = $value; } /** * Unset the given header * * @param string $key */ public function offsetUnset($key) { unset($this->cookies[$key]); } /** * Get an iterator for the data * * @return ArrayIterator */ public function getIterator() { return new ArrayIterator($this->cookies); } /** * Register the cookie handler with the request's hooking system * * @param Requests_Hooker $hooks Hooking system */ public function register(Requests_Hooker $hooks) { $hooks->register('requests.before_request', array($this, 'before_request')); $hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check')); } /** * Add Cookie header to a request if we have any * * As per RFC 6265, cookies are separated by '; ' * * @param string $url * @param array $headers * @param array $data * @param string $type * @param array $options */ public function before_request(&$url, &$headers, &$data, &$type, &$options) { if (!empty($this->cookies)) { $cookies = array(); foreach ($this->cookies as $key => $cookie) { $cookie = $this->normalizeCookie($cookie, $key); $cookies[] = $cookie->formatForHeader(); } $headers['Cookie'] = implode('; ', $cookies); } } /** * Parse all cookies from a response and attach them to the response * * @var Requests_Response $response */ public function before_redirect_check(Requests_Response &$return) { $cookies = Requests_Cookie::parseFromHeaders($return->headers); $this->cookies = array_merge($this->cookies, $cookies); $return->cookies = $this; } }<?php /** * Exception for HTTP requests * * @package Requests */ /** * Exception for HTTP requests * * @package Requests */ class Requests_Exception extends Exception { /** * Type of exception * * @var string */ protected $type; /** * Data associated with the exception * * @var mixed */ protected $data; /** * Create a new exception * * @param string $message Exception message * @param string $type Exception type * @param mixed $data Associated data * @param integer $code Exception numerical code, if applicable */ public function __construct($message, $type, $data = null, $code = 0) { parent::__construct($message, $code); $this->type = $type; $this->data = $data; } /** * Like {@see getCode()}, but a string code. * * @codeCoverageIgnore * @return string */ public function getType() { return $this->type; } /** * Gives any relevant data * * @codeCoverageIgnore * @return mixed */ public function getData() { return $this->data; } }<?php /** * Exception based on HTTP response * * @package Requests */ /** * Exception based on HTTP response * * @package Requests */ class Requests_Exception_HTTP extends Requests_Exception { /** * HTTP status code * * @var integer */ protected $code = 0; /** * Reason phrase * * @var string */ protected $reason = 'Unknown'; /** * Create a new exception * * There is no mechanism to pass in the status code, as this is set by the * subclass used. Reason phrases can vary, however. * * @param string $reason Reason phrase * @param mixed $data Associated data */ public function __construct($reason = null, $data = null) { if ($reason !== null) { $this->reason = $reason; } $message = sprintf('%d %s', $this->code, $this->reason); parent::__construct($message, 'httpresponse', $data, $this->code); } /** * Get the status message */ public function getReason() { return $this->reason; } /** * Get the correct exception class for a given error code * * @param int $code HTTP status code * @return string Exception class name to use */ public static function get_class($code) { $class = sprintf('Requests_Exception_HTTP_%d', $code); if (class_exists($class)) { return $class; } return 'Requests_Exception_HTTP_Unknown'; } }<?php /** * Exception for 400 Bad Request responses * * @package Requests */ /** * Exception for 400 Bad Request responses * * @package Requests */ class Requests_Exception_HTTP_400 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 400; /** * Reason phrase * * @var string */ protected $reason = 'Bad Request'; }<?php /** * Exception for 401 Unauthorized responses * * @package Requests */ /** * Exception for 401 Unauthorized responses * * @package Requests */ class Requests_Exception_HTTP_401 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 401; /** * Reason phrase * * @var string */ protected $reason = 'Unauthorized'; }<?php /** * Exception for 402 Payment Required responses * * @package Requests */ /** * Exception for 402 Payment Required responses * * @package Requests */ class Requests_Exception_HTTP_402 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 402; /** * Reason phrase * * @var string */ protected $reason = 'Payment Required'; }<?php /** * Exception for 403 Forbidden responses * * @package Requests */ /** * Exception for 403 Forbidden responses * * @package Requests */ class Requests_Exception_HTTP_403 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 403; /** * Reason phrase * * @var string */ protected $reason = 'Forbidden'; }<?php /** * Exception for 404 Not Found responses * * @package Requests */ /** * Exception for 404 Not Found responses * * @package Requests */ class Requests_Exception_HTTP_404 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 404; /** * Reason phrase * * @var string */ protected $reason = 'Not Found'; }<?php /** * Exception for 405 Method Not Allowed responses * * @package Requests */ /** * Exception for 405 Method Not Allowed responses * * @package Requests */ class Requests_Exception_HTTP_405 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 405; /** * Reason phrase * * @var string */ protected $reason = 'Method Not Allowed'; }<?php /** * Exception for 406 Not Acceptable responses * * @package Requests */ /** * Exception for 406 Not Acceptable responses * * @package Requests */ class Requests_Exception_HTTP_406 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 406; /** * Reason phrase * * @var string */ protected $reason = 'Not Acceptable'; }<?php /** * Exception for 407 Proxy Authentication Required responses * * @package Requests */ /** * Exception for 407 Proxy Authentication Required responses * * @package Requests */ class Requests_Exception_HTTP_407 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 407; /** * Reason phrase * * @var string */ protected $reason = 'Proxy Authentication Required'; }<?php /** * Exception for 408 Request Timeout responses * * @package Requests */ /** * Exception for 408 Request Timeout responses * * @package Requests */ class Requests_Exception_HTTP_408 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 408; /** * Reason phrase * * @var string */ protected $reason = 'Request Timeout'; }<?php /** * Exception for 409 Conflict responses * * @package Requests */ /** * Exception for 409 Conflict responses * * @package Requests */ class Requests_Exception_HTTP_409 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 409; /** * Reason phrase * * @var string */ protected $reason = 'Conflict'; }<?php /** * Exception for 410 Gone responses * * @package Requests */ /** * Exception for 410 Gone responses * * @package Requests */ class Requests_Exception_HTTP_410 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 410; /** * Reason phrase * * @var string */ protected $reason = 'Gone'; }<?php /** * Exception for 411 Length Required responses * * @package Requests */ /** * Exception for 411 Length Required responses * * @package Requests */ class Requests_Exception_HTTP_411 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 411; /** * Reason phrase * * @var string */ protected $reason = 'Length Required'; }<?php /** * Exception for 412 Precondition Failed responses * * @package Requests */ /** * Exception for 412 Precondition Failed responses * * @package Requests */ class Requests_Exception_HTTP_412 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 412; /** * Reason phrase * * @var string */ protected $reason = 'Precondition Failed'; }<?php /** * Exception for 413 Request Entity Too Large responses * * @package Requests */ /** * Exception for 413 Request Entity Too Large responses * * @package Requests */ class Requests_Exception_HTTP_413 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 413; /** * Reason phrase * * @var string */ protected $reason = 'Request Entity Too Large'; }<?php /** * Exception for 414 Request-URI Too Large responses * * @package Requests */ /** * Exception for 414 Request-URI Too Large responses * * @package Requests */ class Requests_Exception_HTTP_414 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 414; /** * Reason phrase * * @var string */ protected $reason = 'Request-URI Too Large'; }<?php /** * Exception for 415 Unsupported Media Type responses * * @package Requests */ /** * Exception for 415 Unsupported Media Type responses * * @package Requests */ class Requests_Exception_HTTP_415 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 415; /** * Reason phrase * * @var string */ protected $reason = 'Unsupported Media Type'; }<?php /** * Exception for 416 Requested Range Not Satisfiable responses * * @package Requests */ /** * Exception for 416 Requested Range Not Satisfiable responses * * @package Requests */ class Requests_Exception_HTTP_416 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 416; /** * Reason phrase * * @var string */ protected $reason = 'Requested Range Not Satisfiable'; }<?php /** * Exception for 417 Expectation Failed responses * * @package Requests */ /** * Exception for 417 Expectation Failed responses * * @package Requests */ class Requests_Exception_HTTP_417 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 417; /** * Reason phrase * * @var string */ protected $reason = 'Expectation Failed'; }<?php /** * Exception for 418 I'm A Teapot responses * * @see http://tools.ietf.org/html/rfc2324 * @package Requests */ /** * Exception for 418 I'm A Teapot responses * * @see http://tools.ietf.org/html/rfc2324 * @package Requests */ class Requests_Exception_HTTP_418 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 418; /** * Reason phrase * * @var string */ protected $reason = "I'm A Teapot"; }<?php /** * Exception for 428 Precondition Required responses * * @see http://tools.ietf.org/html/rfc6585 * @package Requests */ /** * Exception for 428 Precondition Required responses * * @see http://tools.ietf.org/html/rfc6585 * @package Requests */ class Requests_Exception_HTTP_428 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 428; /** * Reason phrase * * @var string */ protected $reason = 'Precondition Required'; }<?php /** * Exception for 429 Too Many Requests responses * * @see http://tools.ietf.org/html/draft-nottingham-http-new-status-04 * @package Requests */ /** * Exception for 429 Too Many Requests responses * * @see http://tools.ietf.org/html/draft-nottingham-http-new-status-04 * @package Requests */ class Requests_Exception_HTTP_429 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 429; /** * Reason phrase * * @var string */ protected $reason = 'Too Many Requests'; }<?php /** * Exception for 431 Request Header Fields Too Large responses * * @see http://tools.ietf.org/html/rfc6585 * @package Requests */ /** * Exception for 431 Request Header Fields Too Large responses * * @see http://tools.ietf.org/html/rfc6585 * @package Requests */ class Requests_Exception_HTTP_431 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 431; /** * Reason phrase * * @var string */ protected $reason = 'Request Header Fields Too Large'; }<?php /** * Exception for 500 Internal Server Error responses * * @package Requests */ /** * Exception for 500 Internal Server Error responses * * @package Requests */ class Requests_Exception_HTTP_500 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 500; /** * Reason phrase * * @var string */ protected $reason = 'Internal Server Error'; }<?php /** * Exception for 501 Not Implemented responses * * @package Requests */ /** * Exception for 501 Not Implemented responses * * @package Requests */ class Requests_Exception_HTTP_501 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 501; /** * Reason phrase * * @var string */ protected $reason = 'Not Implemented'; }<?php /** * Exception for 502 Bad Gateway responses * * @package Requests */ /** * Exception for 502 Bad Gateway responses * * @package Requests */ class Requests_Exception_HTTP_502 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 502; /** * Reason phrase * * @var string */ protected $reason = 'Bad Gateway'; }<?php /** * Exception for 503 Service Unavailable responses * * @package Requests */ /** * Exception for 503 Service Unavailable responses * * @package Requests */ class Requests_Exception_HTTP_503 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 503; /** * Reason phrase * * @var string */ protected $reason = 'Service Unavailable'; }<?php /** * Exception for 504 Gateway Timeout responses * * @package Requests */ /** * Exception for 504 Gateway Timeout responses * * @package Requests */ class Requests_Exception_HTTP_504 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 504; /** * Reason phrase * * @var string */ protected $reason = 'Gateway Timeout'; }<?php /** * Exception for 505 HTTP Version Not Supported responses * * @package Requests */ /** * Exception for 505 HTTP Version Not Supported responses * * @package Requests */ class Requests_Exception_HTTP_505 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 505; /** * Reason phrase * * @var string */ protected $reason = 'HTTP Version Not Supported'; }<?php /** * Exception for 511 Network Authentication Required responses * * @see http://tools.ietf.org/html/rfc6585 * @package Requests */ /** * Exception for 511 Network Authentication Required responses * * @see http://tools.ietf.org/html/rfc6585 * @package Requests */ class Requests_Exception_HTTP_511 extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 511; /** * Reason phrase * * @var string */ protected $reason = 'Network Authentication Required'; }<?php /** * Exception for unknown status responses * * @package Requests */ /** * Exception for unknown status responses * * @package Requests */ class Requests_Exception_HTTP_Unknown extends Requests_Exception_HTTP { /** * HTTP status code * * @var integer */ protected $code = 0; /** * Reason phrase * * @var string */ protected $reason = 'Unknown'; /** * Create a new exception * * If `$data` is an instance of {@see Requests_Response}, uses the status * code from it. Otherwise, sets as 0 * * @param string $reason Reason phrase * @param mixed $data Associated data */ public function __construct($reason = null, $data = null) { if ($data instanceof Requests_Response) { $this->code = $data->status_code; } parent::__construct($reason, $data); } }<?php /** * Event dispatcher * * @package Requests * @subpackage Utilities */ /** * Event dispatcher * * @package Requests * @subpackage Utilities */ interface Requests_Hooker { /** * Register a callback for a hook * * @param string $hook Hook name * @param callback $callback Function/method to call on event * @param int $priority Priority number. <0 is executed earlier, >0 is executed later */ public function register($hook, $callback, $priority = 0); /** * Dispatch a message * * @param string $hook Hook name * @param array $parameters Parameters to pass to callbacks * @return boolean Successfulness */ public function dispatch($hook, $parameters = array()); }<?php /** * Handles adding and dispatching events * * @package Requests * @subpackage Utilities */ /** * Handles adding and dispatching events * * @package Requests * @subpackage Utilities */ class Requests_Hooks implements Requests_Hooker { /** * Constructor */ public function __construct() { // pass } /** * Register a callback for a hook * * @param string $hook Hook name * @param callback $callback Function/method to call on event * @param int $priority Priority number. <0 is executed earlier, >0 is executed later */ public function register($hook, $callback, $priority = 0) { if (!isset($this->hooks[$hook])) { $this->hooks[$hook] = array(); } if (!isset($this->hooks[$hook][$priority])) { $this->hooks[$hook][$priority] = array(); } $this->hooks[$hook][$priority][] = $callback; } /** * Dispatch a message * * @param string $hook Hook name * @param array $parameters Parameters to pass to callbacks * @return boolean Successfulness */ public function dispatch($hook, $parameters = array()) { if (empty($this->hooks[$hook])) { return false; } foreach ($this->hooks[$hook] as $priority => $hooked) { foreach ($hooked as $callback) { call_user_func_array($callback, $parameters); } } return true; } }<?php /** * IDNA URL encoder * * Note: Not fully compliant, as nameprep does nothing yet. * * @package Requests * @subpackage Utilities * @see http://tools.ietf.org/html/rfc3490 IDNA specification * @see http://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification */ class Requests_IDNAEncoder { /** * ACE prefix used for IDNA * * @see http://tools.ietf.org/html/rfc3490#section-5 * @var string */ const ACE_PREFIX = 'xn--'; /**#@+ * Bootstrap constant for Punycode * * @see http://tools.ietf.org/html/rfc3492#section-5 * @var int */ const BOOTSTRAP_BASE = 36; const BOOTSTRAP_TMIN = 1; const BOOTSTRAP_TMAX = 26; const BOOTSTRAP_SKEW = 38; const BOOTSTRAP_DAMP = 700; const BOOTSTRAP_INITIAL_BIAS = 72; const BOOTSTRAP_INITIAL_N = 128; /**#@-*/ /** * Encode a hostname using Punycode * * @param string $string Hostname * @return string Punycode-encoded hostname */ public static function encode($string) { $parts = explode('.', $string); foreach ($parts as &$part) { $part = self::to_ascii($part); } return implode('.', $parts); } /** * Convert a UTF-8 string to an ASCII string using Punycode * * @throws Requests_Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`) * @throws Requests_Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`) * @throws Requests_Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`) * @throws Requests_Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`) * * @param string $string ASCII or UTF-8 string (max length 64 characters) * @return string ASCII string */ public static function to_ascii($string) { // Step 1: Check if the string is already ASCII if (self::is_ascii($string)) { // Skip to step 7 if (strlen($string) < 64) { return $string; } throw new Requests_Exception('Provided string is too long', 'idna.provided_too_long', $string); } // Step 2: nameprep $string = self::nameprep($string); // Step 3: UseSTD3ASCIIRules is false, continue // Step 4: Check if it's ASCII now if (self::is_ascii($string)) { // Skip to step 7 if (strlen($string) < 64) { return $string; } throw new Requests_Exception('Prepared string is too long', 'idna.prepared_too_long', $string); } // Step 5: Check ACE prefix if (strpos($string, self::ACE_PREFIX) === 0) { throw new Requests_Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $string); } // Step 6: Encode with Punycode $string = self::punycode_encode($string); // Step 7: Prepend ACE prefix $string = self::ACE_PREFIX . $string; // Step 8: Check size if (strlen($string) < 64) { return $string; } throw new Requests_Exception('Encoded string is too long', 'idna.encoded_too_long', $string); } /** * Check whether a given string contains only ASCII characters * * @internal (Testing found regex was the fastest implementation) * * @param string $string * @return bool Is the string ASCII-only? */ protected static function is_ascii($string) { return (preg_match('/(?:[^\x00-\x7F])/', $string) !== 1); } /** * Prepare a string for use as an IDNA name * * @todo Implement this based on RFC 3491 and the newer 5891 * @param string $string * @return string Prepared string */ protected static function nameprep($string) { return $string; } /** * Convert a UTF-8 string to a UCS-4 codepoint array * * Based on Requests_IRI::replace_invalid_with_pct_encoding() * * @throws Requests_Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`) * @param string $input * @return array Unicode code points */ protected static function utf8_to_codepoints($input) { $codepoints = array(); // Get number of bytes $strlen = strlen($input); for ($position = 0; $position < $strlen; $position++) { $value = ord($input[$position]); // One byte sequence: if ((~$value & 0x80) === 0x80) { $character = $value; $length = 1; $remaining = 0; } // Two byte sequence: elseif (($value & 0xE0) === 0xC0) { $character = ($value & 0x1F) << 6; $length = 2; $remaining = 1; } // Three byte sequence: elseif (($value & 0xF0) === 0xE0) { $character = ($value & 0x0F) << 12; $length = 3; $remaining = 2; } // Four byte sequence: elseif (($value & 0xF8) === 0xF0) { $character = ($value & 0x07) << 18; $length = 4; $remaining = 3; } // Invalid byte: else { throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value); } if ($remaining > 0) { if ($position + $length > $strlen) { throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); } for ($position++; $remaining > 0; $position++) { $value = ord($input[$position]); // If it is invalid, count the sequence as invalid and reprocess the current byte: if (($value & 0xC0) !== 0x80) { throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); } $character |= ($value & 0x3F) << (--$remaining * 6); } $position--; } if ( // Non-shortest form sequences are invalid $length > 1 && $character <= 0x7F || $length > 2 && $character <= 0x7FF || $length > 3 && $character <= 0xFFFF // Outside of range of ucschar codepoints // Noncharacters || ($character & 0xFFFE) === 0xFFFE || $character >= 0xFDD0 && $character <= 0xFDEF || ( // Everything else not in ucschar $character > 0xD7FF && $character < 0xF900 || $character < 0x20 || $character > 0x7E && $character < 0xA0 || $character > 0xEFFFD ) ) { throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); } $codepoints[] = $character; } return $codepoints; } /** * RFC3492-compliant encoder * * @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code * @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`) * * @param string $input UTF-8 encoded string to encode * @return string Punycode-encoded string */ public static function punycode_encode($input) { $output = ''; # let n = initial_n $n = self::BOOTSTRAP_INITIAL_N; # let delta = 0 $delta = 0; # let bias = initial_bias $bias = self::BOOTSTRAP_INITIAL_BIAS; # let h = b = the number of basic code points in the input $h = $b = 0; // see loop # copy them to the output in order $codepoints = self::utf8_to_codepoints($input); foreach ($codepoints as $char) { if ($char < 128) { // Character is valid ASCII // TODO: this should also check if it's valid for a URL $output .= chr($char); $h++; } // Check if the character is non-ASCII, but below initial n // This never occurs for Punycode, so ignore in coverage // @codeCoverageIgnoreStart elseif ($char < $n) { throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char); } // @codeCoverageIgnoreEnd else { $extended[$char] = true; } } $extended = array_keys($extended); sort($extended); $b = $h; # [copy them] followed by a delimiter if b > 0 if (strlen($output) > 0) { $output .= '-'; } # {if the input contains a non-basic code point < n then fail} # while h < length(input) do begin while ($h < count($codepoints)) { # let m = the minimum code point >= n in the input $m = array_shift($extended); //printf('next code point to insert is %s' . PHP_EOL, dechex($m)); # let delta = delta + (m - n) * (h + 1), fail on overflow $delta += ($m - $n) * ($h + 1); # let n = m $n = $m; # for each code point c in the input (in order) do begin for ($num = 0; $num < count($codepoints); $num++) { $c = $codepoints[$num]; # if c < n then increment delta, fail on overflow if ($c < $n) { $delta++; } # if c == n then begin elseif ($c === $n) { # let q = delta $q = $delta; # for k = base to infinity in steps of base do begin for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) { # let t = tmin if k <= bias {+ tmin}, or # tmax if k >= bias + tmax, or k - bias otherwise if ($k <= ($bias + self::BOOTSTRAP_TMIN)) { $t = self::BOOTSTRAP_TMIN; } elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) { $t = self::BOOTSTRAP_TMAX; } else { $t = $k - $bias; } # if q < t then break if ($q < $t) { break; } # output the code point for digit t + ((q - t) mod (base - t)) $digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)); //printf('needed delta is %d, encodes as "%s"' . PHP_EOL, $delta, self::digit_to_char($digit)); $output .= self::digit_to_char($digit); # let q = (q - t) div (base - t) $q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t)); # end } # output the code point for digit q $output .= self::digit_to_char($q); //printf('needed delta is %d, encodes as "%s"' . PHP_EOL, $delta, self::digit_to_char($q)); # let bias = adapt(delta, h + 1, test h equals b?) $bias = self::adapt($delta, $h + 1, $h === $b); //printf('bias becomes %d' . PHP_EOL, $bias); # let delta = 0 $delta = 0; # increment h $h++; # end } # end } # increment delta and n $delta++; $n++; # end } return $output; } /** * Convert a digit to its respective character * * @see http://tools.ietf.org/html/rfc3492#section-5 * @throws Requests_Exception On invalid digit (`idna.invalid_digit`) * * @param int $digit Digit in the range 0-35 * @return string Single character corresponding to digit */ protected static function digit_to_char($digit) { // @codeCoverageIgnoreStart // As far as I know, this never happens, but still good to be sure. if ($digit < 0 || $digit > 35) { throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit); } // @codeCoverageIgnoreEnd $digits = 'abcdefghijklmnopqrstuvwxyz0123456789'; return substr($digits, $digit, 1); } /** * Adapt the bias * * @see http://tools.ietf.org/html/rfc3492#section-6.1 * @param int $delta * @param int $numpoints * @param bool $firsttime * @return int New bias */ protected static function adapt($delta, $numpoints, $firsttime) { # function adapt(delta,numpoints,firsttime): # if firsttime then let delta = delta div damp if ($firsttime) { $delta = floor($delta / self::BOOTSTRAP_DAMP); } # else let delta = delta div 2 else { $delta = floor($delta / 2); } # let delta = delta + (delta div numpoints) $delta += floor($delta / $numpoints); # let k = 0 $k = 0; # while delta > ((base - tmin) * tmax) div 2 do begin $max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2); while ($delta > $max) { # let delta = delta div (base - tmin) $delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN)); # let k = k + base $k += self::BOOTSTRAP_BASE; # end } # return k + (((base - tmin + 1) * delta) div (delta + skew)) return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW)); } }<?php /** * Class to validate and to work with IPv6 addresses * * @package Requests * @subpackage Utilities */ /** * Class to validate and to work with IPv6 addresses * * This was originally based on the PEAR class of the same name, but has been * entirely rewritten. * * @package Requests * @subpackage Utilities */ class Requests_IPv6 { /** * Uncompresses an IPv6 address * * RFC 4291 allows you to compress consecutive zero pieces in an address to * '::'. This method expects a valid IPv6 address and expands the '::' to * the required number of zero pieces. * * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 * ::1 -> 0:0:0:0:0:0:0:1 * * @author Alexander Merz <alexander.merz@web.de> * @author elfrink at introweb dot nl * @author Josh Peck <jmp at joshpeck dot org> * @copyright 2003-2005 The PHP Group * @license http://www.opensource.org/licenses/bsd-license.php * @param string $ip An IPv6 address * @return string The uncompressed IPv6 address */ public static function uncompress($ip) { $c1 = -1; $c2 = -1; if (substr_count($ip, '::') === 1) { list($ip1, $ip2) = explode('::', $ip); if ($ip1 === '') { $c1 = -1; } else { $c1 = substr_count($ip1, ':'); } if ($ip2 === '') { $c2 = -1; } else { $c2 = substr_count($ip2, ':'); } if (strpos($ip2, '.') !== false) { $c2++; } // :: if ($c1 === -1 && $c2 === -1) { $ip = '0:0:0:0:0:0:0:0'; } // ::xxx else if ($c1 === -1) { $fill = str_repeat('0:', 7 - $c2); $ip = str_replace('::', $fill, $ip); } // xxx:: else if ($c2 === -1) { $fill = str_repeat(':0', 7 - $c1); $ip = str_replace('::', $fill, $ip); } // xxx::xxx else { $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); $ip = str_replace('::', $fill, $ip); } } return $ip; } /** * Compresses an IPv6 address * * RFC 4291 allows you to compress consecutive zero pieces in an address to * '::'. This method expects a valid IPv6 address and compresses consecutive * zero pieces to '::'. * * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 * 0:0:0:0:0:0:0:1 -> ::1 * * @see uncompress() * @param string $ip An IPv6 address * @return string The compressed IPv6 address */ public static function compress($ip) { // Prepare the IP to be compressed $ip = self::uncompress($ip); $ip_parts = self::split_v6_v4($ip); // Replace all leading zeros $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); // Find bunches of zeros if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) { $max = 0; $pos = null; foreach ($matches[0] as $match) { if (strlen($match[0]) > $max) { $max = strlen($match[0]); $pos = $match[1]; } } $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); } if ($ip_parts[1] !== '') { return implode(':', $ip_parts); } else { return $ip_parts[0]; } } /** * Splits an IPv6 address into the IPv6 and IPv4 representation parts * * RFC 4291 allows you to represent the last two parts of an IPv6 address * using the standard IPv4 representation * * Example: 0:0:0:0:0:0:13.1.68.3 * 0:0:0:0:0:FFFF:129.144.52.38 * * @param string $ip An IPv6 address * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part */ private static function split_v6_v4($ip) { if (strpos($ip, '.') !== false) { $pos = strrpos($ip, ':'); $ipv6_part = substr($ip, 0, $pos); $ipv4_part = substr($ip, $pos + 1); return array($ipv6_part, $ipv4_part); } else { return array($ip, ''); } } /** * Checks an IPv6 address * * Checks if the given IP is a valid IPv6 address * * @param string $ip An IPv6 address * @return bool true if $ip is a valid IPv6 address */ public static function check_ipv6($ip) { $ip = self::uncompress($ip); list($ipv6, $ipv4) = self::split_v6_v4($ip); $ipv6 = explode(':', $ipv6); $ipv4 = explode('.', $ipv4); if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) { foreach ($ipv6 as $ipv6_part) { // The section can't be empty if ($ipv6_part === '') return false; // Nor can it be over four characters if (strlen($ipv6_part) > 4) return false; // Remove leading zeros (this is safe because of the above) $ipv6_part = ltrim($ipv6_part, '0'); if ($ipv6_part === '') $ipv6_part = '0'; // Check the value is valid $value = hexdec($ipv6_part); if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) return false; } if (count($ipv4) === 4) { foreach ($ipv4 as $ipv4_part) { $value = (int) $ipv4_part; if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) return false; } } return true; } else { return false; } } } <?php /** * IRI parser/serialiser/normaliser * * @package Requests * @subpackage Utilities */ /** * IRI parser/serialiser/normaliser * * Copyright (c) 2007-2010, Geoffrey Sneddon and Steve Minutillo. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package Requests * @subpackage Utilities * @author Geoffrey Sneddon * @author Steve Minutillo * @copyright 2007-2009 Geoffrey Sneddon and Steve Minutillo * @license http://www.opensource.org/licenses/bsd-license.php * @link http://hg.gsnedders.com/iri/ */ class Requests_IRI { /** * Scheme * * @var string */ protected $scheme = null; /** * User Information * * @var string */ protected $iuserinfo = null; /** * ihost * * @var string */ protected $ihost = null; /** * Port * * @var string */ protected $port = null; /** * ipath * * @var string */ protected $ipath = ''; /** * iquery * * @var string */ protected $iquery = null; /** * ifragment * * @var string */ protected $ifragment = null; /** * Normalization database * * Each key is the scheme, each value is an array with each key as the IRI * part and value as the default value for that part. */ protected $normalization = array( 'acap' => array( 'port' => 674 ), 'dict' => array( 'port' => 2628 ), 'file' => array( 'ihost' => 'localhost' ), 'http' => array( 'port' => 80, 'ipath' => '/' ), 'https' => array( 'port' => 443, 'ipath' => '/' ), ); /** * Return the entire IRI when you try and read the object as a string * * @return string */ public function __toString() { return $this->get_iri(); } /** * Overload __set() to provide access via properties * * @param string $name Property name * @param mixed $value Property value */ public function __set($name, $value) { if (method_exists($this, 'set_' . $name)) { call_user_func(array($this, 'set_' . $name), $value); } elseif ( $name === 'iauthority' || $name === 'iuserinfo' || $name === 'ihost' || $name === 'ipath' || $name === 'iquery' || $name === 'ifragment' ) { call_user_func(array($this, 'set_' . substr($name, 1)), $value); } } /** * Overload __get() to provide access via properties * * @param string $name Property name * @return mixed */ public function __get($name) { // isset() returns false for null, we don't want to do that // Also why we use array_key_exists below instead of isset() $props = get_object_vars($this); if ( $name === 'iri' || $name === 'uri' || $name === 'iauthority' || $name === 'authority' ) { $return = $this->{"get_$name"}(); } elseif (array_key_exists($name, $props)) { $return = $this->$name; } // host -> ihost elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) { $name = $prop; $return = $this->$prop; } // ischeme -> scheme elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) { $name = $prop; $return = $this->$prop; } else { trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); $return = null; } if ($return === null && isset($this->normalization[$this->scheme][$name])) { return $this->normalization[$this->scheme][$name]; } else { return $return; } } /** * Overload __isset() to provide access via properties * * @param string $name Property name * @return bool */ public function __isset($name) { if (method_exists($this, 'get_' . $name) || isset($this->$name)) { return true; } else { return false; } } /** * Overload __unset() to provide access via properties * * @param string $name Property name */ public function __unset($name) { if (method_exists($this, 'set_' . $name)) { call_user_func(array($this, 'set_' . $name), ''); } } /** * Create a new IRI object, from a specified string * * @param string $iri */ public function __construct($iri = null) { $this->set_iri($iri); } /** * Create a new IRI object by resolving a relative IRI * * Returns false if $base is not absolute, otherwise an IRI. * * @param IRI|string $base (Absolute) Base IRI * @param IRI|string $relative Relative IRI * @return IRI|false */ public static function absolutize($base, $relative) { if (!($relative instanceof Requests_IRI)) { $relative = new Requests_IRI($relative); } if (!$relative->is_valid()) { return false; } elseif ($relative->scheme !== null) { return clone $relative; } else { if (!($base instanceof Requests_IRI)) { $base = new Requests_IRI($base); } if ($base->scheme !== null && $base->is_valid()) { if ($relative->get_iri() !== '') { if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) { $target = clone $relative; $target->scheme = $base->scheme; } else { $target = new Requests_IRI; $target->scheme = $base->scheme; $target->iuserinfo = $base->iuserinfo; $target->ihost = $base->ihost; $target->port = $base->port; if ($relative->ipath !== '') { if ($relative->ipath[0] === '/') { $target->ipath = $relative->ipath; } elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') { $target->ipath = '/' . $relative->ipath; } elseif (($last_segment = strrpos($base->ipath, '/')) !== false) { $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; } else { $target->ipath = $relative->ipath; } $target->ipath = $target->remove_dot_segments($target->ipath); $target->iquery = $relative->iquery; } else { $target->ipath = $base->ipath; if ($relative->iquery !== null) { $target->iquery = $relative->iquery; } elseif ($base->iquery !== null) { $target->iquery = $base->iquery; } } $target->ifragment = $relative->ifragment; } } else { $target = clone $base; $target->ifragment = null; } $target->scheme_normalization(); return $target; } else { return false; } } } /** * Parse an IRI into scheme/authority/path/query/fragment segments * * @param string $iri * @return array */ protected function parse_iri($iri) { $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match)) { if ($match[1] === '') { $match['scheme'] = null; } if (!isset($match[3]) || $match[3] === '') { $match['authority'] = null; } if (!isset($match[5])) { $match['path'] = ''; } if (!isset($match[6]) || $match[6] === '') { $match['query'] = null; } if (!isset($match[8]) || $match[8] === '') { $match['fragment'] = null; } return $match; } else { trigger_error('This should never happen', E_USER_ERROR); die; } } /** * Remove dot segments from a path * * @param string $input * @return string */ protected function remove_dot_segments($input) { $output = ''; while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') { // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, if (strpos($input, '../') === 0) { $input = substr($input, 3); } elseif (strpos($input, './') === 0) { $input = substr($input, 2); } // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, elseif (strpos($input, '/./') === 0) { $input = substr($input, 2); } elseif ($input === '/.') { $input = '/'; } // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, elseif (strpos($input, '/../') === 0) { $input = substr($input, 3); $output = substr_replace($output, '', strrpos($output, '/')); } elseif ($input === '/..') { $input = '/'; $output = substr_replace($output, '', strrpos($output, '/')); } // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, elseif ($input === '.' || $input === '..') { $input = ''; } // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer elseif (($pos = strpos($input, '/', 1)) !== false) { $output .= substr($input, 0, $pos); $input = substr_replace($input, '', 0, $pos); } else { $output .= $input; $input = ''; } } return $output . $input; } /** * Replace invalid character with percent encoding * * @param string $string Input string * @param string $extra_chars Valid characters not in iunreserved or * iprivate (this is ASCII-only) * @param bool $iprivate Allow iprivate * @return string */ protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) { // Normalize as many pct-encoded sections as possible $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string); // Replace invalid percent characters $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); // Add unreserved and % to $extra_chars (the latter is safe because all // pct-encoded sections are now valid). $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; // Now replace any bytes that aren't allowed with their pct-encoded versions $position = 0; $strlen = strlen($string); while (($position += strspn($string, $extra_chars, $position)) < $strlen) { $value = ord($string[$position]); // Start position $start = $position; // By default we are valid $valid = true; // No one byte sequences are valid due to the while. // Two byte sequence: if (($value & 0xE0) === 0xC0) { $character = ($value & 0x1F) << 6; $length = 2; $remaining = 1; } // Three byte sequence: elseif (($value & 0xF0) === 0xE0) { $character = ($value & 0x0F) << 12; $length = 3; $remaining = 2; } // Four byte sequence: elseif (($value & 0xF8) === 0xF0) { $character = ($value & 0x07) << 18; $length = 4; $remaining = 3; } // Invalid byte: else { $valid = false; $length = 1; $remaining = 0; } if ($remaining) { if ($position + $length <= $strlen) { for ($position++; $remaining; $position++) { $value = ord($string[$position]); // Check that the byte is valid, then add it to the character: if (($value & 0xC0) === 0x80) { $character |= ($value & 0x3F) << (--$remaining * 6); } // If it is invalid, count the sequence as invalid and reprocess the current byte: else { $valid = false; $position--; break; } } } else { $position = $strlen - 1; $valid = false; } } // Percent encode anything invalid or not in ucschar if ( // Invalid sequences !$valid // Non-shortest form sequences are invalid || $length > 1 && $character <= 0x7F || $length > 2 && $character <= 0x7FF || $length > 3 && $character <= 0xFFFF // Outside of range of ucschar codepoints // Noncharacters || ($character & 0xFFFE) === 0xFFFE || $character >= 0xFDD0 && $character <= 0xFDEF || ( // Everything else not in ucschar $character > 0xD7FF && $character < 0xF900 || $character < 0xA0 || $character > 0xEFFFD ) && ( // Everything not in iprivate, if it applies !$iprivate || $character < 0xE000 || $character > 0x10FFFD ) ) { // If we were a character, pretend we weren't, but rather an error. if ($valid) $position--; for ($j = $start; $j <= $position; $j++) { $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); $j += 2; $position += 2; $strlen += 2; } } } return $string; } /** * Callback function for preg_replace_callback. * * Removes sequences of percent encoded bytes that represent UTF-8 * encoded characters in iunreserved * * @param array $match PCRE match * @return string Replacement */ protected function remove_iunreserved_percent_encoded($match) { // As we just have valid percent encoded sequences we can just explode // and ignore the first member of the returned array (an empty string). $bytes = explode('%', $match[0]); // Initialize the new string (this is what will be returned) and that // there are no bytes remaining in the current sequence (unsurprising // at the first byte!). $string = ''; $remaining = 0; // Loop over each and every byte, and set $value to its value for ($i = 1, $len = count($bytes); $i < $len; $i++) { $value = hexdec($bytes[$i]); // If we're the first byte of sequence: if (!$remaining) { // Start position $start = $i; // By default we are valid $valid = true; // One byte sequence: if ($value <= 0x7F) { $character = $value; $length = 1; } // Two byte sequence: elseif (($value & 0xE0) === 0xC0) { $character = ($value & 0x1F) << 6; $length = 2; $remaining = 1; } // Three byte sequence: elseif (($value & 0xF0) === 0xE0) { $character = ($value & 0x0F) << 12; $length = 3; $remaining = 2; } // Four byte sequence: elseif (($value & 0xF8) === 0xF0) { $character = ($value & 0x07) << 18; $length = 4; $remaining = 3; } // Invalid byte: else { $valid = false; $remaining = 0; } } // Continuation byte: else { // Check that the byte is valid, then add it to the character: if (($value & 0xC0) === 0x80) { $remaining--; $character |= ($value & 0x3F) << ($remaining * 6); } // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: else { $valid = false; $remaining = 0; $i--; } } // If we've reached the end of the current byte sequence, append it to Unicode::$data if (!$remaining) { // Percent encode anything invalid or not in iunreserved if ( // Invalid sequences !$valid // Non-shortest form sequences are invalid || $length > 1 && $character <= 0x7F || $length > 2 && $character <= 0x7FF || $length > 3 && $character <= 0xFFFF // Outside of range of iunreserved codepoints || $character < 0x2D || $character > 0xEFFFD // Noncharacters || ($character & 0xFFFE) === 0xFFFE || $character >= 0xFDD0 && $character <= 0xFDEF // Everything else not in iunreserved (this is all BMP) || $character === 0x2F || $character > 0x39 && $character < 0x41 || $character > 0x5A && $character < 0x61 || $character > 0x7A && $character < 0x7E || $character > 0x7E && $character < 0xA0 || $character > 0xD7FF && $character < 0xF900 ) { for ($j = $start; $j <= $i; $j++) { $string .= '%' . strtoupper($bytes[$j]); } } else { for ($j = $start; $j <= $i; $j++) { $string .= chr(hexdec($bytes[$j])); } } } } // If we have any bytes left over they are invalid (i.e., we are // mid-way through a multi-byte sequence) if ($remaining) { for ($j = $start; $j < $len; $j++) { $string .= '%' . strtoupper($bytes[$j]); } } return $string; } protected function scheme_normalization() { if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) { $this->iuserinfo = null; } if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) { $this->ihost = null; } if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) { $this->port = null; } if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) { $this->ipath = ''; } if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) { $this->iquery = null; } if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) { $this->ifragment = null; } } /** * Check if the object represents a valid IRI. This needs to be done on each * call as some things change depending on another part of the IRI. * * @return bool */ public function is_valid() { $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; if ($this->ipath !== '' && ( $isauthority && ( $this->ipath[0] !== '/' || substr($this->ipath, 0, 2) === '//' ) || ( $this->scheme === null && !$isauthority && strpos($this->ipath, ':') !== false && (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) ) ) ) { return false; } return true; } /** * Set the entire IRI. Returns true on success, false on failure (if there * are any invalid characters). * * @param string $iri * @return bool */ protected function set_iri($iri) { static $cache; if (!$cache) { $cache = array(); } if ($iri === null) { return true; } elseif (isset($cache[$iri])) { list($this->scheme, $this->iuserinfo, $this->ihost, $this->port, $this->ipath, $this->iquery, $this->ifragment, $return) = $cache[$iri]; return $return; } else { $parsed = $this->parse_iri((string) $iri); $return = $this->set_scheme($parsed['scheme']) && $this->set_authority($parsed['authority']) && $this->set_path($parsed['path']) && $this->set_query($parsed['query']) && $this->set_fragment($parsed['fragment']); $cache[$iri] = array($this->scheme, $this->iuserinfo, $this->ihost, $this->port, $this->ipath, $this->iquery, $this->ifragment, $return); return $return; } } /** * Set the scheme. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $scheme * @return bool */ protected function set_scheme($scheme) { if ($scheme === null) { $this->scheme = null; } elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) { $this->scheme = null; return false; } else { $this->scheme = strtolower($scheme); } return true; } /** * Set the authority. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $authority * @return bool */ protected function set_authority($authority) { static $cache; if (!$cache) $cache = array(); if ($authority === null) { $this->iuserinfo = null; $this->ihost = null; $this->port = null; return true; } elseif (isset($cache[$authority])) { list($this->iuserinfo, $this->ihost, $this->port, $return) = $cache[$authority]; return $return; } else { $remaining = $authority; if (($iuserinfo_end = strrpos($remaining, '@')) !== false) { $iuserinfo = substr($remaining, 0, $iuserinfo_end); $remaining = substr($remaining, $iuserinfo_end + 1); } else { $iuserinfo = null; } if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) { if (($port = substr($remaining, $port_start + 1)) === false) { $port = null; } $remaining = substr($remaining, 0, $port_start); } else { $port = null; } $return = $this->set_userinfo($iuserinfo) && $this->set_host($remaining) && $this->set_port($port); $cache[$authority] = array($this->iuserinfo, $this->ihost, $this->port, $return); return $return; } } /** * Set the iuserinfo. * * @param string $iuserinfo * @return bool */ protected function set_userinfo($iuserinfo) { if ($iuserinfo === null) { $this->iuserinfo = null; } else { $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); $this->scheme_normalization(); } return true; } /** * Set the ihost. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $ihost * @return bool */ protected function set_host($ihost) { if ($ihost === null) { $this->ihost = null; return true; } elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') { if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) { $this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']'; } else { $this->ihost = null; return false; } } else { $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); // Lowercase, but ignore pct-encoded sections (as they should // remain uppercase). This must be done after the previous step // as that can add unescaped characters. $position = 0; $strlen = strlen($ihost); while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) { if ($ihost[$position] === '%') { $position += 3; } else { $ihost[$position] = strtolower($ihost[$position]); $position++; } } $this->ihost = $ihost; } $this->scheme_normalization(); return true; } /** * Set the port. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $port * @return bool */ protected function set_port($port) { if ($port === null) { $this->port = null; return true; } elseif (strspn($port, '0123456789') === strlen($port)) { $this->port = (int) $port; $this->scheme_normalization(); return true; } else { $this->port = null; return false; } } /** * Set the ipath. * * @param string $ipath * @return bool */ protected function set_path($ipath) { static $cache; if (!$cache) { $cache = array(); } $ipath = (string) $ipath; if (isset($cache[$ipath])) { $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; } else { $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); $removed = $this->remove_dot_segments($valid); $cache[$ipath] = array($valid, $removed); $this->ipath = ($this->scheme !== null) ? $removed : $valid; } $this->scheme_normalization(); return true; } /** * Set the iquery. * * @param string $iquery * @return bool */ protected function set_query($iquery) { if ($iquery === null) { $this->iquery = null; } else { $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); $this->scheme_normalization(); } return true; } /** * Set the ifragment. * * @param string $ifragment * @return bool */ protected function set_fragment($ifragment) { if ($ifragment === null) { $this->ifragment = null; } else { $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); $this->scheme_normalization(); } return true; } /** * Convert an IRI to a URI (or parts thereof) * * @return string */ protected function to_uri($string) { static $non_ascii; if (!$non_ascii) { $non_ascii = implode('', range("\x80", "\xFF")); } $position = 0; $strlen = strlen($string); while (($position += strcspn($string, $non_ascii, $position)) < $strlen) { $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); $position += 3; $strlen += 2; } return $string; } /** * Get the complete IRI * * @return string */ protected function get_iri() { if (!$this->is_valid()) { return false; } $iri = ''; if ($this->scheme !== null) { $iri .= $this->scheme . ':'; } if (($iauthority = $this->get_iauthority()) !== null) { $iri .= '//' . $iauthority; } $iri .= $this->ipath; if ($this->iquery !== null) { $iri .= '?' . $this->iquery; } if ($this->ifragment !== null) { $iri .= '#' . $this->ifragment; } return $iri; } /** * Get the complete URI * * @return string */ protected function get_uri() { return $this->to_uri($this->get_iri()); } /** * Get the complete iauthority * * @return string */ protected function get_iauthority() { if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null) { $iauthority = ''; if ($this->iuserinfo !== null) { $iauthority .= $this->iuserinfo . '@'; } if ($this->ihost !== null) { $iauthority .= $this->ihost; } if ($this->port !== null) { $iauthority .= ':' . $this->port; } return $iauthority; } else { return null; } } /** * Get the complete authority * * @return string */ protected function get_authority() { $iauthority = $this->get_iauthority(); if (is_string($iauthority)) return $this->to_uri($iauthority); else return $iauthority; } } <?php /** * Proxy connection interface * * @package Requests * @subpackage Proxy * @since 1.6 */ /** * Proxy connection interface * * Implement this interface to handle proxy settings and authentication * * Parameters should be passed via the constructor where possible, as this * makes it much easier for users to use your provider. * * @see Requests_Hooks * @package Requests * @subpackage Proxy * @since 1.6 */ interface Requests_Proxy { /** * Register hooks as needed * * This method is called in {@see Requests::request} when the user has set * an instance as the 'auth' option. Use this callback to register all the * hooks you'll need. * * @see Requests_Hooks::register * @param Requests_Hooks $hooks Hook system */ public function register(Requests_Hooks &$hooks); }<?php /** * HTTP Proxy connection interface * * @package Requests * @subpackage Proxy * @since 1.6 */ /** * HTTP Proxy connection interface * * Provides a handler for connection via an HTTP proxy * * @package Requests * @subpackage Proxy * @since 1.6 */ class Requests_Proxy_HTTP implements Requests_Proxy { /** * Proxy host and port * * Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128) * * @var string */ public $proxy; /** * Username * * @var string */ public $user; /** * Password * * @var string */ public $pass; /** * Do we need to authenticate? (ie username & password have been provided) * * @var boolean */ public $use_authentication; /** * Constructor * * @since 1.6 * @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`) * @param array|null $args Array of user and password. Must have exactly two elements */ public function __construct($args = null) { if (is_string($args)) { $this->proxy = $args; } elseif (is_array($args)) { if (count($args) == 1) { list($this->proxy) = $args; } elseif (count($args) == 3) { list($this->proxy, $this->user, $this->pass) = $args; $this->use_authentication = true; } else { throw new Requests_Exception( 'Invalid number of arguments', 'proxyhttpbadargs'); } } } /** * Register the necessary callbacks * * @since 1.6 * @see curl_before_send * @see fsockopen_remote_socket * @see fsockopen_remote_host_path * @see fsockopen_header * @param Requests_Hooks $hooks Hook system */ public function register(Requests_Hooks &$hooks) { $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); $hooks->register('fsockopen.remote_socket', array(&$this, 'fsockopen_remote_socket')); $hooks->register('fsockopen.remote_host_path', array(&$this, 'fsockopen_remote_host_path')); if( $this->use_authentication ) { $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); } } /** * Set cURL parameters before the data is sent * * @since 1.6 * @param resource $handle cURL resource */ public function curl_before_send(&$handle) { curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); curl_setopt($handle, CURLOPT_PROXY, $this->proxy); if ($this->use_authentication) { curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string()); } } /** * Alter remote socket information before opening socket connection * * @since 1.6 * @param string $out HTTP header string */ public function fsockopen_remote_socket( &$remote_socket ) { $remote_socket = $this->proxy; } /** * Alter remote path before getting stream data * * @since 1.6 * @param string $out HTTP header string */ public function fsockopen_remote_host_path( &$path, $url ) { $path = $url; } /** * Add extra headers to the request before sending * * @since 1.6 * @param string $out HTTP header string */ public function fsockopen_header( &$out ) { $out .= "Proxy-Authorization: Basic " . base64_encode($this->get_auth_string()) . "\r\n"; } /** * Get the authentication string (user:pass) * * @since 1.6 * @return string */ public function get_auth_string() { return $this->user . ':' . $this->pass; } }<?php /** * HTTP response class * * Contains a response from Requests::request() * @package Requests */ /** * HTTP response class * * Contains a response from Requests::request() * @package Requests */ class Requests_Response { /** * Constructor */ public function __construct() { $this->headers = new Requests_Response_Headers(); } /** * Response body * @var string */ public $body = ''; /** * Raw HTTP data from the transport * @var string */ public $raw = ''; /** * Headers, as an associative array * @var array */ public $headers = array(); /** * Status code, false if non-blocking * @var integer|boolean */ public $status_code = false; /** * Whether the request succeeded or not * @var boolean */ public $success = false; /** * Number of redirects the request used * @var integer */ public $redirects = 0; /** * URL requested * @var string */ public $url = ''; /** * Previous requests (from redirects) * @var array Array of Requests_Response objects */ public $history = array(); /** * Cookies from the request */ public $cookies = array(); /** * Throws an exception if the request was not successful * * @throws Requests_Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`) * @throws Requests_Exception_HTTP On non-successful status code. Exception class corresponds to code (e.g. {@see Requests_Exception_HTTP_404}) * @param boolean $allow_redirects Set to false to throw on a 3xx as well */ public function throw_for_status($allow_redirects = true) { if ($this->status_code >= 300 && $this->status_code < 400) { if (!$allow_redirects) { throw new Requests_Exception('Redirection not allowed', 'response.no_redirects', $this); } } elseif (!$this->success) { $exception = Requests_Exception_HTTP::get_class($this->status_code); throw new $exception(null, $this); } } }<?php /** * Case-insensitive dictionary, suitable for HTTP headers * * @package Requests */ /** * Case-insensitive dictionary, suitable for HTTP headers * * @package Requests */ class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictionary { /** * Get the given header * * Unlike {@see self::getValues()}, this returns a string. If there are * multiple values, it concatenates them with a comma as per RFC2616. * * Avoid using this where commas may be used unquoted in values, such as * Set-Cookie headers. * * @param string $key * @return string Header value */ public function offsetGet($key) { $key = strtolower($key); if (!isset($this->data[$key])) return null; return $this->flatten($this->data[$key]); } /** * Set the given item * * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) * * @param string $key Item name * @param string $value Item value */ public function offsetSet($key, $value) { if ($key === null) { throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); } $key = strtolower($key); if (!isset($this->data[$key])) { $this->data[$key] = array(); } $this->data[$key][] = $value; } /** * Get all values for a given header * * @param string $key * @return array Header values */ public function getValues($key) { $key = strtolower($key); if (!isset($this->data[$key])) return null; return $this->data[$key]; } /** * Flattens a value into a string * * Converts an array into a string by imploding values with a comma, as per * RFC2616's rules for folding headers. * * @param string|array $value Value to flatten * @return string Flattened value */ public function flatten($value) { if (is_array($value)) $value = implode(',', $value); return $value; } /** * Get an iterator for the data * * Converts the internal * @return ArrayIterator */ public function getIterator() { return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten')); } } <?php /** * SSL utilities for Requests * * @package Requests * @subpackage Utilities */ /** * SSL utilities for Requests * * Collection of utilities for working with and verifying SSL certificates. * * @package Requests * @subpackage Utilities */ class Requests_SSL { /** * Verify the certificate against common name and subject alternative names * * Unfortunately, PHP doesn't check the certificate against the alternative * names, leading things like 'https://www.github.com/' to be invalid. * Instead * * @see http://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 * * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) * @param string $host Host name to verify against * @param resource $context Stream context * @return bool */ public static function verify_certificate($host, $cert) { // Calculate the valid wildcard match if the host is not an IP address $parts = explode('.', $host); if (ip2long($host) === false) { $parts[0] = '*'; } $wildcard = implode('.', $parts); $has_dns_alt = false; // Check the subjectAltName if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) { $altnames = explode(',', $cert['extensions']['subjectAltName']); foreach ($altnames as $altname) { $altname = trim($altname); if (strpos($altname, 'DNS:') !== 0) continue; $has_dns_alt = true; // Strip the 'DNS:' prefix and trim whitespace $altname = trim(substr($altname, 4)); // Check for a match if (self::match_domain($host, $altname) === true) { return true; } } } // Fall back to checking the common name if we didn't get any dNSName // alt names, as per RFC2818 if (!$has_dns_alt && !empty($cert['subject']['CN'])) { // Check for a match if (self::match_domain($host, $cert['subject']['CN']) === true) { return true; } } return false; } /** * Verify that a reference name is valid * * Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules: * - Wildcards can only occur in a name with more than 3 components * - Wildcards can only occur as the last character in the first * component * - Wildcards may be preceded by additional characters * * We modify these rules to be a bit stricter and only allow the wildcard * character to be the full first component; that is, with the exclusion of * the third rule. * * @param string $reference Reference dNSName * @return boolean Is the name valid? */ public static function verify_reference_name($reference) { $parts = explode('.', $reference); // Check the first part of the name $first = array_shift($parts); if (strpos($first, '*') !== false) { // Check that the wildcard is the full part if ($first !== '*') { return false; } // Check that we have at least 3 components (including first) if (count($parts) < 2) { return false; } } // Check the remaining parts foreach ($parts as $part) { if (strpos($part, '*') !== false) { return false; } } // Nothing found, verified! return true; } /** * Match a hostname against a dNSName reference * * @param string $host Requested host * @param string $reference dNSName to match against * @return boolean Does the domain match? */ public static function match_domain($host, $reference) { // Check if the reference is blacklisted first if (self::verify_reference_name($reference) !== true) { return false; } // Check for a direct match if ($host === $reference) { return true; } // Calculate the valid wildcard match if the host is not an IP address // Also validates that the host has 3 parts or more, as per Firefox's // ruleset. if (ip2long($host) === false) { $parts = explode('.', $host); $parts[0] = '*'; $wildcard = implode('.', $parts); if ($wildcard === $reference) { return true; } } return false; } }<?php /** * Session handler for persistent requests and default parameters * * @package Requests * @subpackage Session Handler */ /** * Session handler for persistent requests and default parameters * * Allows various options to be set as default values, and merges both the * options and URL properties together. A base URL can be set for all requests, * with all subrequests resolved from this. Base options can be set (including * a shared cookie jar), then overridden for individual requests. * * @package Requests * @subpackage Session Handler */ class Requests_Session { /** * Base URL for requests * * URLs will be made absolute using this as the base * @var string|null */ public $url = null; /** * Base headers for requests * @var array */ public $headers = array(); /** * Base data for requests * * If both the base data and the per-request data are arrays, the data will * be merged before sending the request. * * @var array */ public $data = array(); /** * Base options for requests * * The base options are merged with the per-request data for each request. * The only default option is a shared cookie jar between requests. * * Values here can also be set directly via properties on the Session * object, e.g. `$session->useragent = 'X';` * * @var array */ public $options = array(); /** * Create a new session * * @param string|null $url Base URL for requests * @param array $headers Default headers for requests * @param array $data Default data for requests * @param array $options Default options for requests */ public function __construct($url = null, $headers = array(), $data = array(), $options = array()) { $this->url = $url; $this->headers = $headers; $this->data = $data; $this->options = $options; if (empty($this->options['cookies'])) { $this->options['cookies'] = new Requests_Cookie_Jar(); } } /** * Get a property's value * * @param string $key Property key * @return mixed|null Property value, null if none found */ public function __get($key) { if (isset($this->options[$key])) return $this->options[$key]; return null; } /** * Set a property's value * * @param string $key Property key * @param mixed $value Property value */ public function __set($key, $value) { $this->options[$key] = $value; } /** * Remove a property's value * * @param string $key Property key */ public function __isset($key) { return isset($this->options[$key]); } /** * Remove a property's value * * @param string $key Property key */ public function __unset($key) { $this->options[$key] = null; } /**#@+ * @see request() * @param string $url * @param array $headers * @param array $options * @return Requests_Response */ /** * Send a GET request */ public function get($url, $headers = array(), $options = array()) { return $this->request($url, $headers, null, Requests::GET, $options); } /** * Send a HEAD request */ public function head($url, $headers = array(), $options = array()) { return $this->request($url, $headers, null, Requests::HEAD, $options); } /** * Send a DELETE request */ public function delete($url, $headers = array(), $options = array()) { return $this->request($url, $headers, null, Requests::DELETE, $options); } /**#@-*/ /**#@+ * @see request() * @param string $url * @param array $headers * @param array $data * @param array $options * @return Requests_Response */ /** * Send a POST request */ public function post($url, $headers = array(), $data = array(), $options = array()) { return $this->request($url, $headers, $data, Requests::POST, $options); } /** * Send a PUT request */ public function put($url, $headers = array(), $data = array(), $options = array()) { return $this->request($url, $headers, $data, Requests::PUT, $options); } /** * Send a PATCH request * * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the * specification recommends that should send an ETag * * @link http://tools.ietf.org/html/rfc5789 */ public function patch($url, $headers, $data = array(), $options = array()) { return $this->request($url, $headers, $data, Requests::PATCH, $options); } /**#@-*/ /** * Main interface for HTTP requests * * This method initiates a request and sends it via a transport before * parsing. * * @see Requests::request() * * @throws Requests_Exception On invalid URLs (`nonhttp`) * * @param string $url URL to request * @param array $headers Extra headers to send with the request * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests * @param string $type HTTP request type (use Requests constants) * @param array $options Options for the request (see {@see Requests::request}) * @return Requests_Response */ public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) { $request = $this->merge_request(compact('url', 'headers', 'data', 'options')); return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']); } /** * Send multiple HTTP requests simultaneously * * @see Requests::request_multiple() * * @param array $requests Requests data (see {@see Requests::request_multiple}) * @param array $options Global and default options (see {@see Requests::request}) * @return array Responses (either Requests_Response or a Requests_Exception object) */ public function request_multiple($requests, $options = array()) { foreach ($requests as $key => $request) { $requests[$key] = $this->merge_request($request, false); } $options = array_merge($this->options, $options); // Disallow forcing the type, as that's a per request setting unset($options['type']); return Requests::request_multiple($requests, $options); } /** * Merge a request's data with the default data * * @param array $request Request data (same form as {@see request_multiple}) * @param boolean $merge_options Should we merge options as well? * @return array Request data */ protected function merge_request($request, $merge_options = true) { if ($this->url !== null) { $request['url'] = Requests_IRI::absolutize($this->url, $request['url']); $request['url'] = $request['url']->uri; } $request['headers'] = array_merge($this->headers, $request['headers']); if (is_array($request['data']) && is_array($this->data)) { $request['data'] = array_merge($this->data, $request['data']); } if ($merge_options !== false) { $request['options'] = array_merge($this->options, $request['options']); // Disallow forcing the type, as that's a per request setting unset($request['options']['type']); } return $request; } }<?php /** * Base HTTP transport * * @package Requests * @subpackage Transport */ /** * Base HTTP transport * * @package Requests * @subpackage Transport */ interface Requests_Transport { /** * Perform a request * * @param string $url URL to request * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation * @return string Raw HTTP result */ public function request($url, $headers = array(), $data = array(), $options = array()); /** * Send multiple requests simultaneously * * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request} * @param array $options Global options, see {@see Requests::response()} for documentation * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) */ public function request_multiple($requests, $options); /** * Self-test whether the transport can be used * @return bool */ public static function test(); }<?php /** * cURL HTTP transport * * @package Requests * @subpackage Transport */ /** * cURL HTTP transport * * @package Requests * @subpackage Transport */ class Requests_Transport_cURL implements Requests_Transport { /** * Raw HTTP data * * @var string */ public $headers = ''; /** * Information on the current request * * @var array cURL information array, see {@see http://php.net/curl_getinfo} */ public $info; /** * Version string * * @var string */ public $version; /** * cURL handle * * @var resource */ protected $fp; /** * Have we finished the headers yet? * * @var boolean */ protected $done_headers = false; /** * If streaming to a file, keep the file pointer * * @var resource */ protected $stream_handle; /** * Constructor */ public function __construct() { $curl = curl_version(); $this->version = $curl['version']; $this->fp = curl_init(); curl_setopt($this->fp, CURLOPT_HEADER, false); curl_setopt($this->fp, CURLOPT_RETURNTRANSFER, 1); if (version_compare($this->version, '7.10.5', '>=')) { curl_setopt($this->fp, CURLOPT_ENCODING, ''); } if (defined('CURLOPT_PROTOCOLS')) { curl_setopt($this->fp, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); } if (defined('CURLOPT_REDIR_PROTOCOLS')) { curl_setopt($this->fp, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); } } /** * Perform a request * * @throws Requests_Exception On a cURL error (`curlerror`) * * @param string $url URL to request * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation * @return string Raw HTTP result */ public function request($url, $headers = array(), $data = array(), $options = array()) { $this->setup_handle($url, $headers, $data, $options); $options['hooks']->dispatch('curl.before_send', array(&$this->fp)); if ($options['filename'] !== false) { $this->stream_handle = fopen($options['filename'], 'wb'); curl_setopt($this->fp, CURLOPT_FILE, $this->stream_handle); } if (isset($options['verify'])) { if ($options['verify'] === false) { curl_setopt($this->fp, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($this->fp, CURLOPT_SSL_VERIFYPEER, 0); } elseif (is_string($options['verify'])) { curl_setopt($this->fp, CURLOPT_CAINFO, $options['verify']); } } if (isset($options['verifyname']) && $options['verifyname'] === false) { curl_setopt($this->fp, CURLOPT_SSL_VERIFYHOST, 0); } $response = curl_exec($this->fp); $options['hooks']->dispatch('curl.after_send', array(&$fake_headers)); if (curl_errno($this->fp) === 23 || curl_errno($this->fp) === 61) { curl_setopt($this->fp, CURLOPT_ENCODING, 'none'); $response = curl_exec($this->fp); } $this->process_response($response, $options); curl_close($this->fp); return $this->headers; } /** * Send multiple requests simultaneously * * @param array $requests Request data * @param array $options Global options * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) */ public function request_multiple($requests, $options) { $multihandle = curl_multi_init(); $subrequests = array(); $subhandles = array(); $class = get_class($this); foreach ($requests as $id => $request) { $subrequests[$id] = new $class(); $subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']); $request['options']['hooks']->dispatch('curl.before_multi_add', array(&$subhandles[$id])); curl_multi_add_handle($multihandle, $subhandles[$id]); } $completed = 0; $responses = array(); $request['options']['hooks']->dispatch('curl.before_multi_exec', array(&$multihandle)); do { $active = false; do { $status = curl_multi_exec($multihandle, $active); } while ($status === CURLM_CALL_MULTI_PERFORM); $to_process = array(); // Read the information as needed while ($done = curl_multi_info_read($multihandle)) { $key = array_search($done['handle'], $subhandles, true); if (!isset($to_process[$key])) { $to_process[$key] = $done; } } // Parse the finished requests before we start getting the new ones foreach ($to_process as $key => $done) { $options = $requests[$key]['options']; $responses[$key] = $subrequests[$key]->process_response(curl_multi_getcontent($done['handle']), $options); $options['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$key], $requests[$key])); curl_multi_remove_handle($multihandle, $done['handle']); curl_close($done['handle']); if (!is_string($responses[$key])) { $options['hooks']->dispatch('multiple.request.complete', array(&$responses[$key], $key)); } $completed++; } } while ($active || $completed < count($subrequests)); $request['options']['hooks']->dispatch('curl.after_multi_exec', array(&$multihandle)); curl_multi_close($multihandle); return $responses; } /** * Get the cURL handle for use in a multi-request * * @param string $url URL to request * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation * @return resource Subrequest's cURL handle */ public function &get_subrequest_handle($url, $headers, $data, $options) { $this->setup_handle($url, $headers, $data, $options); if ($options['filename'] !== false) { $this->stream_handle = fopen($options['filename'], 'wb'); curl_setopt($this->fp, CURLOPT_FILE, $this->stream_handle); } return $this->fp; } /** * Setup the cURL handle for the given data * * @param string $url URL to request * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation */ protected function setup_handle($url, $headers, $data, $options) { $options['hooks']->dispatch('curl.before_request', array(&$this->fp)); $headers = Requests::flatten($headers); if (in_array($options['type'], array(Requests::HEAD, Requests::GET, Requests::DELETE)) & !empty($data)) { $url = self::format_get($url, $data); } elseif (!empty($data) && !is_string($data)) { $data = http_build_query($data, null, '&'); } switch ($options['type']) { case Requests::POST: curl_setopt($this->fp, CURLOPT_POST, true); curl_setopt($this->fp, CURLOPT_POSTFIELDS, $data); break; case Requests::PATCH: case Requests::PUT: curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, $options['type']); curl_setopt($this->fp, CURLOPT_POSTFIELDS, $data); break; case Requests::DELETE: curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, 'DELETE'); break; case Requests::HEAD: curl_setopt($this->fp, CURLOPT_NOBODY, true); break; } curl_setopt($this->fp, CURLOPT_URL, $url); curl_setopt($this->fp, CURLOPT_TIMEOUT, $options['timeout']); curl_setopt($this->fp, CURLOPT_CONNECTTIMEOUT, $options['timeout']); curl_setopt($this->fp, CURLOPT_REFERER, $url); curl_setopt($this->fp, CURLOPT_USERAGENT, $options['useragent']); curl_setopt($this->fp, CURLOPT_HTTPHEADER, $headers); if (true === $options['blocking']) { curl_setopt($this->fp, CURLOPT_HEADERFUNCTION, array(&$this, 'stream_headers')); } } public function process_response($response, $options) { if ($options['blocking'] === false) { $fake_headers = ''; $options['hooks']->dispatch('curl.after_request', array(&$fake_headers)); return false; } if ($options['filename'] !== false) { fclose($this->stream_handle); $this->headers = trim($this->headers); } else { $this->headers .= $response; } if (curl_errno($this->fp)) { throw new Requests_Exception('cURL error ' . curl_errno($this->fp) . ': ' . curl_error($this->fp), 'curlerror', $this->fp); return; } $this->info = curl_getinfo($this->fp); $options['hooks']->dispatch('curl.after_request', array(&$this->headers)); return $this->headers; } /** * Collect the headers as they are received * * @param resource $handle cURL resource * @param string $headers Header string * @return integer Length of provided header */ protected function stream_headers($handle, $headers) { // Why do we do this? cURL will send both the final response and any // interim responses, such as a 100 Continue. We don't need that. // (We may want to keep this somewhere just in case) if ($this->done_headers) { $this->headers = ''; $this->done_headers = false; } $this->headers .= $headers; if ($headers === "\r\n") { $this->done_headers = true; } return strlen($headers); } /** * Format a URL given GET data * * @param string $url * @param array|object $data Data to build query using, see {@see http://php.net/http_build_query} * @return string URL with data */ protected static function format_get($url, $data) { if (!empty($data)) { $url_parts = parse_url($url); if (empty($url_parts['query'])) { $query = $url_parts['query'] = ''; } else { $query = $url_parts['query']; } $query .= '&' . http_build_query($data, null, '&'); $query = trim($query, '&'); if (empty($url_parts['query'])) { $url .= '?' . $query; } else { $url = str_replace($url_parts['query'], $query, $url); } } return $url; } /** * Whether this transport is valid * * @codeCoverageIgnore * @return boolean True if the transport is valid, false otherwise. */ public static function test() { return (function_exists('curl_init') && function_exists('curl_exec')); } } <?php /** * fsockopen HTTP transport * * @package Requests * @subpackage Transport */ /** * fsockopen HTTP transport * * @package Requests * @subpackage Transport */ class Requests_Transport_fsockopen implements Requests_Transport { /** * Raw HTTP data * * @var string */ public $headers = ''; /** * Stream metadata * * @var array Associative array of properties, see {@see http://php.net/stream_get_meta_data} */ public $info; protected $connect_error = ''; /** * Perform a request * * @throws Requests_Exception On failure to connect to socket (`fsockopenerror`) * @throws Requests_Exception On socket timeout (`timeout`) * * @param string $url URL to request * @param array $headers Associative array of request headers * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD * @param array $options Request options, see {@see Requests::response()} for documentation * @return string Raw HTTP result */ public function request($url, $headers = array(), $data = array(), $options = array()) { $options['hooks']->dispatch('fsockopen.before_request'); $url_parts = parse_url($url); $host = $url_parts['host']; $context = stream_context_create(); $verifyname = false; // HTTPS support if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') { $remote_socket = 'ssl://' . $host; $url_parts['port'] = 443; $context_options = array( 'verify_peer' => true, // 'CN_match' => $host, 'capture_peer_cert' => true ); $verifyname = true; // SNI, if enabled (OpenSSL >=0.9.8j) if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) { $context_options['SNI_enabled'] = true; if (isset($options['verifyname']) && $options['verifyname'] === false) { $context_options['SNI_enabled'] = false; } } if (isset($options['verify'])) { if ($options['verify'] === false) { $context_options['verify_peer'] = false; } elseif (is_string($options['verify'])) { $context_options['cafile'] = $options['verify']; } } if (isset($options['verifyname']) && $options['verifyname'] === false) { $verifyname = false; } stream_context_set_option($context, array('ssl' => $context_options)); } else { $remote_socket = 'tcp://' . $host; } $proxy = isset( $options['proxy'] ); $proxy_auth = $proxy && isset( $options['proxy_username'] ) && isset( $options['proxy_password'] ); if (!isset($url_parts['port'])) { $url_parts['port'] = 80; } $remote_socket .= ':' . $url_parts['port']; set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE); $options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket)); $fp = stream_socket_client($remote_socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $context); restore_error_handler(); if ($verifyname) { if (!$this->verify_certificate_from_context($host, $context)) { throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match'); } } if (!$fp) { if ($errno === 0) { // Connection issue throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error'); } else { throw new Requests_Exception($errstr, 'fsockopenerror'); return; } } $request_body = ''; $out = ''; switch ($options['type']) { case Requests::POST: case Requests::PUT: case Requests::PATCH: if (isset($url_parts['path'])) { $path = $url_parts['path']; if (isset($url_parts['query'])) { $path .= '?' . $url_parts['query']; } } else { $path = '/'; } $options['hooks']->dispatch( 'fsockopen.remote_host_path', array( &$path, $url ) ); $out = $options['type'] . " $path HTTP/1.0\r\n"; if (is_array($data)) { $request_body = http_build_query($data, null, '&'); } else { $request_body = $data; } if (empty($headers['Content-Length'])) { $headers['Content-Length'] = strlen($request_body); } if (empty($headers['Content-Type'])) { $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; } break; case Requests::HEAD: case Requests::GET: case Requests::DELETE: $path = self::format_get($url_parts, $data); $options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url)); $out = $options['type'] . " $path HTTP/1.0\r\n"; break; } $out .= "Host: {$url_parts['host']}"; if ($url_parts['port'] !== 80) { $out .= ":{$url_parts['port']}"; } $out .= "\r\n"; $out .= "User-Agent: {$options['useragent']}\r\n"; $accept_encoding = $this->accept_encoding(); if (!empty($accept_encoding)) { $out .= "Accept-Encoding: $accept_encoding\r\n"; } $headers = Requests::flatten($headers); if (!empty($headers)) { $out .= implode($headers, "\r\n") . "\r\n"; } $options['hooks']->dispatch('fsockopen.after_headers', array(&$out)); if (substr($out, -2) !== "\r\n") { $out .= "\r\n"; } $out .= "Connection: Close\r\n\r\n" . $request_body; $options['hooks']->dispatch('fsockopen.before_send', array(&$out)); fwrite($fp, $out); $options['hooks']->dispatch('fsockopen.after_send', array(&$fake_headers)); if (!$options['blocking']) { fclose($fp); $fake_headers = ''; $options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers)); return ''; } stream_set_timeout($fp, $options['timeout']); $this->info = stream_get_meta_data($fp); $this->headers = ''; $this->info = stream_get_meta_data($fp); if (!$options['filename']) { while (!feof($fp)) { $this->info = stream_get_meta_data($fp); if ($this->info['timed_out']) { throw new Requests_Exception('fsocket timed out', 'timeout'); } $this->headers .= fread($fp, 1160); } } else { $download = fopen($options['filename'], 'wb'); $doingbody = false; $response = ''; while (!feof($fp)) { $this->info = stream_get_meta_data($fp); if ($this->info['timed_out']) { throw new Requests_Exception('fsocket timed out', 'timeout'); } $block = fread($fp, 1160); if ($doingbody) { fwrite($download, $block); } else { $response .= $block; if (strpos($response, "\r\n\r\n")) { list($this->headers, $block) = explode("\r\n\r\n", $response, 2); $doingbody = true; fwrite($download, $block); } } } fclose($download); } fclose($fp); $options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers)); return $this->headers; } /** * Send multiple requests simultaneously * * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request} * @param array $options Global options, see {@see Requests::response()} for documentation * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) */ public function request_multiple($requests, $options) { $responses = array(); $class = get_class($this); foreach ($requests as $id => $request) { try { $handler = new $class(); $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request)); } catch (Requests_Exception $e) { $responses[$id] = $e; } if (!is_string($responses[$id])) { $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id)); } } return $responses; } /** * Retrieve the encodings we can accept * * @return string Accept-Encoding header value */ protected static function accept_encoding() { $type = array(); if (function_exists('gzinflate')) { $type[] = 'deflate;q=1.0'; } if (function_exists('gzuncompress')) { $type[] = 'compress;q=0.5'; } $type[] = 'gzip;q=0.5'; return implode(', ', $type); } /** * Format a URL given GET data * * @param array $url_parts * @param array|object $data Data to build query using, see {@see http://php.net/http_build_query} * @return string URL with data */ protected static function format_get($url_parts, $data) { if (!empty($data)) { if (empty($url_parts['query'])) $url_parts['query'] = ''; $url_parts['query'] .= '&' . http_build_query($data, null, '&'); $url_parts['query'] = trim($url_parts['query'], '&'); } if (isset($url_parts['path'])) { if (isset($url_parts['query'])) { $get = $url_parts['path'] . '?' . $url_parts['query']; } else { $get = $url_parts['path']; } } else { $get = '/'; } return $get; } /** * Error handler for stream_socket_client() * * @param int $errno Error number (e.g. E_WARNING) * @param string $errstr Error message */ public function connect_error_handler($errno, $errstr) { // Double-check we can handle it if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) { // Return false to indicate the default error handler should engage return false; } $this->connect_error .= $errstr . "\n"; return true; } /** * Verify the certificate against common name and subject alternative names * * Unfortunately, PHP doesn't check the certificate against the alternative * names, leading things like 'https://www.github.com/' to be invalid. * Instead * * @see http://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 * * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`) * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) * @param string $host Host name to verify against * @param resource $context Stream context * @return bool */ public function verify_certificate_from_context($host, $context) { $meta = stream_context_get_options($context); // If we don't have SSL options, then we couldn't make the connection at // all if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error'); } $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); return Requests_SSL::verify_certificate($host, $cert); } /** * Whether this transport is valid * * @codeCoverageIgnore * @return boolean True if the transport is valid, false otherwise. */ public static function test($capabilities = array()) { if (!function_exists('fsockopen')) return false; // If needed, check that streams support SSL if (isset( $capabilities['ssl'] ) && $capabilities['ssl']) { if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) return false; // Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156 if (defined('HHVM_VERSION')) return false; } return true; } } <?php /** * Case-insensitive dictionary, suitable for HTTP headers * * @package Requests * @subpackage Utilities */ /** * Case-insensitive dictionary, suitable for HTTP headers * * @package Requests * @subpackage Utilities */ class Requests_Utility_CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate { /** * Actual item data * * @var array */ protected $data = array(); /** * Check if the given item exists * * @param string $key Item key * @return boolean Does the item exist? */ public function offsetExists($key) { $key = strtolower($key); return isset($this->data[$key]); } /** * Get the value for the item * * @param string $key Item key * @return string Item value */ public function offsetGet($key) { $key = strtolower($key); if (!isset($this->data[$key])) return null; return $this->data[$key]; } /** * Set the given item * * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) * * @param string $key Item name * @param string $value Item value */ public function offsetSet($key, $value) { if ($key === null) { throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); } $key = strtolower($key); $this->data[$key] = $value; } /** * Unset the given header * * @param string $key */ public function offsetUnset($key) { unset($this->data[strtolower($key)]); } /** * Get an iterator for the data * * @return ArrayIterator */ public function getIterator() { return new ArrayIterator($this->data); } /** * Get the headers as an array * * @return array Header data */ public function getAll() { return $this->data; } } <?php /** * Iterator for arrays requiring filtered values * * @package Requests * @subpackage Utilities */ /** * Iterator for arrays requiring filtered values * * @package Requests * @subpackage Utilities */ class Requests_Utility_FilteredIterator extends ArrayIterator { /** * Create a new iterator * * @param array $data * @param callable $callback Callback to be called on each value */ public function __construct($data, $callback) { parent::__construct($data); $this->callback = $callback; } /** * Get the current item's value after filtering * * @return string */ public function current() { $value = parent::current(); $value = call_user_func($this->callback, $value); return $value; } } <?php // autoload_namespaces.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'cli' => array($vendorDir . '/wp-cli/php-cli-tools/lib'), 'WP_CLI' => array($baseDir . '/php'), 'Requests' => array($vendorDir . '/rmccue/requests/library'), 'Oxymel' => array($vendorDir . '/nb/oxymel'), 'Mustache' => array($vendorDir . '/mustache/mustache/src'), 'Behat\\Gherkin' => array($vendorDir . '/behat/gherkin/src'), 'Behat\\Behat' => array($vendorDir . '/behat/behat/src'), ); <?php // autoload_psr4.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), 'Symfony\\Component\\DependencyInjection\\' => array($vendorDir . '/symfony/dependency-injection'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'), 'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'), ); <?php // autoload_classmap.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'File_Iterator' => $vendorDir . '/phpunit/php-file-iterator/src/Iterator.php', 'File_Iterator_Facade' => $vendorDir . '/phpunit/php-file-iterator/src/Facade.php', 'File_Iterator_Factory' => $vendorDir . '/phpunit/php-file-iterator/src/Factory.php', 'PHPUnit_Extensions_GroupTestSuite' => $vendorDir . '/phpunit/phpunit/PHPUnit/Extensions/GroupTestSuite.php', 'PHPUnit_Extensions_PhptTestCase' => $vendorDir . '/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase.php', 'PHPUnit_Extensions_PhptTestCase_Logger' => $vendorDir . '/phpunit/phpunit/PHPUnit/Extensions/PhptTestCase/Logger.php', 'PHPUnit_Extensions_PhptTestSuite' => $vendorDir . '/phpunit/phpunit/PHPUnit/Extensions/PhptTestSuite.php', 'PHPUnit_Extensions_RepeatedTest' => $vendorDir . '/phpunit/phpunit/PHPUnit/Extensions/RepeatedTest.php', 'PHPUnit_Extensions_TestDecorator' => $vendorDir . '/phpunit/phpunit/PHPUnit/Extensions/TestDecorator.php', 'PHPUnit_Extensions_TicketListener' => $vendorDir . '/phpunit/phpunit/PHPUnit/Extensions/TicketListener.php', 'PHPUnit_Framework_Assert' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Assert.php', 'PHPUnit_Framework_AssertionFailedError' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/AssertionFailedError.php', 'PHPUnit_Framework_Comparator' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator.php', 'PHPUnit_Framework_ComparatorFactory' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/ComparatorFactory.php', 'PHPUnit_Framework_Comparator_Array' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Array.php', 'PHPUnit_Framework_Comparator_DOMDocument' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/DOMDocument.php', 'PHPUnit_Framework_Comparator_Double' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Double.php', 'PHPUnit_Framework_Comparator_Exception' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Exception.php', 'PHPUnit_Framework_Comparator_MockObject' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/MockObject.php', 'PHPUnit_Framework_Comparator_Numeric' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Numeric.php', 'PHPUnit_Framework_Comparator_Object' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Object.php', 'PHPUnit_Framework_Comparator_Resource' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Resource.php', 'PHPUnit_Framework_Comparator_Scalar' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Scalar.php', 'PHPUnit_Framework_Comparator_SplObjectStorage' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/SplObjectStorage.php', 'PHPUnit_Framework_Comparator_Type' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Comparator/Type.php', 'PHPUnit_Framework_ComparisonFailure' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/ComparisonFailure.php', 'PHPUnit_Framework_Constraint' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint.php', 'PHPUnit_Framework_Constraint_And' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/And.php', 'PHPUnit_Framework_Constraint_ArrayHasKey' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/ArrayHasKey.php', 'PHPUnit_Framework_Constraint_Attribute' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Attribute.php', 'PHPUnit_Framework_Constraint_Callback' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Callback.php', 'PHPUnit_Framework_Constraint_ClassHasAttribute' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasAttribute.php', 'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/ClassHasStaticAttribute.php', 'PHPUnit_Framework_Constraint_Composite' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Composite.php', 'PHPUnit_Framework_Constraint_Count' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Count.php', 'PHPUnit_Framework_Constraint_Exception' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Exception.php', 'PHPUnit_Framework_Constraint_ExceptionCode' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionCode.php', 'PHPUnit_Framework_Constraint_ExceptionMessage' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/ExceptionMessage.php', 'PHPUnit_Framework_Constraint_FileExists' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/FileExists.php', 'PHPUnit_Framework_Constraint_GreaterThan' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/GreaterThan.php', 'PHPUnit_Framework_Constraint_IsAnything' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsAnything.php', 'PHPUnit_Framework_Constraint_IsEmpty' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEmpty.php', 'PHPUnit_Framework_Constraint_IsEqual' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsEqual.php', 'PHPUnit_Framework_Constraint_IsFalse' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsFalse.php', 'PHPUnit_Framework_Constraint_IsIdentical' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsIdentical.php', 'PHPUnit_Framework_Constraint_IsInstanceOf' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsInstanceOf.php', 'PHPUnit_Framework_Constraint_IsJson' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsJson.php', 'PHPUnit_Framework_Constraint_IsNull' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsNull.php', 'PHPUnit_Framework_Constraint_IsTrue' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsTrue.php', 'PHPUnit_Framework_Constraint_IsType' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/IsType.php', 'PHPUnit_Framework_Constraint_JsonMatches' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches.php', 'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php', 'PHPUnit_Framework_Constraint_LessThan' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/LessThan.php', 'PHPUnit_Framework_Constraint_Not' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Not.php', 'PHPUnit_Framework_Constraint_ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/ObjectHasAttribute.php', 'PHPUnit_Framework_Constraint_Or' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Or.php', 'PHPUnit_Framework_Constraint_PCREMatch' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/PCREMatch.php', 'PHPUnit_Framework_Constraint_SameSize' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/SameSize.php', 'PHPUnit_Framework_Constraint_StringContains' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/StringContains.php', 'PHPUnit_Framework_Constraint_StringEndsWith' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/StringEndsWith.php', 'PHPUnit_Framework_Constraint_StringMatches' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/StringMatches.php', 'PHPUnit_Framework_Constraint_StringStartsWith' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/StringStartsWith.php', 'PHPUnit_Framework_Constraint_TraversableContains' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContains.php', 'PHPUnit_Framework_Constraint_TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/TraversableContainsOnly.php', 'PHPUnit_Framework_Constraint_Xor' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Constraint/Xor.php', 'PHPUnit_Framework_Error' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Error.php', 'PHPUnit_Framework_Error_Deprecated' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Error/Deprecated.php', 'PHPUnit_Framework_Error_Notice' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Error/Notice.php', 'PHPUnit_Framework_Error_Warning' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Error/Warning.php', 'PHPUnit_Framework_Exception' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Exception.php', 'PHPUnit_Framework_ExpectationFailedException' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/ExpectationFailedException.php', 'PHPUnit_Framework_IncompleteTest' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/IncompleteTest.php', 'PHPUnit_Framework_IncompleteTestError' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/IncompleteTestError.php', 'PHPUnit_Framework_MockObject_Builder_Identity' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Identity.php', 'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/InvocationMocker.php', 'PHPUnit_Framework_MockObject_Builder_Match' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Match.php', 'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/MethodNameMatch.php', 'PHPUnit_Framework_MockObject_Builder_Namespace' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Namespace.php', 'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/ParametersMatch.php', 'PHPUnit_Framework_MockObject_Builder_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Builder/Stub.php', 'PHPUnit_Framework_MockObject_Generator' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Generator.php', 'PHPUnit_Framework_MockObject_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation.php', 'PHPUnit_Framework_MockObject_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/InvocationMocker.php', 'PHPUnit_Framework_MockObject_Invocation_Object' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Object.php', 'PHPUnit_Framework_MockObject_Invocation_Static' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invocation/Static.php', 'PHPUnit_Framework_MockObject_Invokable' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Invokable.php', 'PHPUnit_Framework_MockObject_Matcher' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher.php', 'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyInvokedCount.php', 'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/AnyParameters.php', 'PHPUnit_Framework_MockObject_Matcher_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Invocation.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtIndex.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedCount.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/InvokedRecorder.php', 'PHPUnit_Framework_MockObject_Matcher_MethodName' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/MethodName.php', 'PHPUnit_Framework_MockObject_Matcher_Parameters' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/Parameters.php', 'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Matcher/StatelessInvocation.php', 'PHPUnit_Framework_MockObject_MockBuilder' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockBuilder.php', 'PHPUnit_Framework_MockObject_MockObject' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/MockObject.php', 'PHPUnit_Framework_MockObject_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub.php', 'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ConsecutiveCalls.php', 'PHPUnit_Framework_MockObject_Stub_Exception' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Exception.php', 'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/MatcherCollection.php', 'PHPUnit_Framework_MockObject_Stub_Return' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/Return.php', 'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnArgument.php', 'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnCallback.php', 'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnSelf.php', 'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Stub/ReturnValueMap.php', 'PHPUnit_Framework_MockObject_Verifiable' => $vendorDir . '/phpunit/phpunit-mock-objects/PHPUnit/Framework/MockObject/Verifiable.php', 'PHPUnit_Framework_OutputError' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/OutputError.php', 'PHPUnit_Framework_SelfDescribing' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/SelfDescribing.php', 'PHPUnit_Framework_SkippedTest' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/SkippedTest.php', 'PHPUnit_Framework_SkippedTestError' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/SkippedTestError.php', 'PHPUnit_Framework_SkippedTestSuiteError' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/SkippedTestSuiteError.php', 'PHPUnit_Framework_SyntheticError' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/SyntheticError.php', 'PHPUnit_Framework_Test' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Test.php', 'PHPUnit_Framework_TestCase' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/TestCase.php', 'PHPUnit_Framework_TestFailure' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/TestFailure.php', 'PHPUnit_Framework_TestListener' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/TestListener.php', 'PHPUnit_Framework_TestResult' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/TestResult.php', 'PHPUnit_Framework_TestSuite' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/TestSuite.php', 'PHPUnit_Framework_TestSuite_DataProvider' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/TestSuite/DataProvider.php', 'PHPUnit_Framework_Warning' => $vendorDir . '/phpunit/phpunit/PHPUnit/Framework/Warning.php', 'PHPUnit_Runner_BaseTestRunner' => $vendorDir . '/phpunit/phpunit/PHPUnit/Runner/BaseTestRunner.php', 'PHPUnit_Runner_StandardTestSuiteLoader' => $vendorDir . '/phpunit/phpunit/PHPUnit/Runner/StandardTestSuiteLoader.php', 'PHPUnit_Runner_TestSuiteLoader' => $vendorDir . '/phpunit/phpunit/PHPUnit/Runner/TestSuiteLoader.php', 'PHPUnit_Runner_Version' => $vendorDir . '/phpunit/phpunit/PHPUnit/Runner/Version.php', 'PHPUnit_TextUI_Command' => $vendorDir . '/phpunit/phpunit/PHPUnit/TextUI/Command.php', 'PHPUnit_TextUI_ResultPrinter' => $vendorDir . '/phpunit/phpunit/PHPUnit/TextUI/ResultPrinter.php', 'PHPUnit_TextUI_TestRunner' => $vendorDir . '/phpunit/phpunit/PHPUnit/TextUI/TestRunner.php', 'PHPUnit_Util_Class' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Class.php', 'PHPUnit_Util_Configuration' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Configuration.php', 'PHPUnit_Util_DeprecatedFeature' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature.php', 'PHPUnit_Util_DeprecatedFeature_Logger' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php', 'PHPUnit_Util_Diff' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Diff.php', 'PHPUnit_Util_ErrorHandler' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/ErrorHandler.php', 'PHPUnit_Util_Fileloader' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Fileloader.php', 'PHPUnit_Util_Filesystem' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Filesystem.php', 'PHPUnit_Util_Filter' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Filter.php', 'PHPUnit_Util_Getopt' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Getopt.php', 'PHPUnit_Util_GlobalState' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/GlobalState.php', 'PHPUnit_Util_InvalidArgumentHelper' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/InvalidArgumentHelper.php', 'PHPUnit_Util_Log_JSON' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Log/JSON.php', 'PHPUnit_Util_Log_JUnit' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Log/JUnit.php', 'PHPUnit_Util_Log_TAP' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Log/TAP.php', 'PHPUnit_Util_PHP' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/PHP.php', 'PHPUnit_Util_PHP_Default' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/PHP/Default.php', 'PHPUnit_Util_PHP_Windows' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/PHP/Windows.php', 'PHPUnit_Util_Printer' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Printer.php', 'PHPUnit_Util_String' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/String.php', 'PHPUnit_Util_Test' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Test.php', 'PHPUnit_Util_TestDox_NamePrettifier' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/TestDox/NamePrettifier.php', 'PHPUnit_Util_TestDox_ResultPrinter' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter.php', 'PHPUnit_Util_TestDox_ResultPrinter_HTML' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/HTML.php', 'PHPUnit_Util_TestDox_ResultPrinter_Text' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/TestDox/ResultPrinter/Text.php', 'PHPUnit_Util_TestSuiteIterator' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/TestSuiteIterator.php', 'PHPUnit_Util_Type' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/Type.php', 'PHPUnit_Util_XML' => $vendorDir . '/phpunit/phpunit/PHPUnit/Util/XML.php', 'PHP_CodeCoverage' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage.php', 'PHP_CodeCoverage_Driver' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Driver.php', 'PHP_CodeCoverage_Driver_Xdebug' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Driver/Xdebug.php', 'PHP_CodeCoverage_Exception' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Exception.php', 'PHP_CodeCoverage_Filter' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Filter.php', 'PHP_CodeCoverage_Report_Clover' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Clover.php', 'PHP_CodeCoverage_Report_Factory' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Factory.php', 'PHP_CodeCoverage_Report_HTML' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML.php', 'PHP_CodeCoverage_Report_HTML_Renderer' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer.php', 'PHP_CodeCoverage_Report_HTML_Renderer_Dashboard' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Dashboard.php', 'PHP_CodeCoverage_Report_HTML_Renderer_Directory' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/Directory.php', 'PHP_CodeCoverage_Report_HTML_Renderer_File' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/HTML/Renderer/File.php', 'PHP_CodeCoverage_Report_Node' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node.php', 'PHP_CodeCoverage_Report_Node_Directory' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Directory.php', 'PHP_CodeCoverage_Report_Node_File' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/File.php', 'PHP_CodeCoverage_Report_Node_Iterator' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Node/Iterator.php', 'PHP_CodeCoverage_Report_PHP' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/PHP.php', 'PHP_CodeCoverage_Report_Text' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Report/Text.php', 'PHP_CodeCoverage_Util' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Util.php', 'PHP_CodeCoverage_Util_InvalidArgumentHelper' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Util/InvalidArgumentHelper.php', 'PHP_CodeCoverage_Version' => $vendorDir . '/phpunit/php-code-coverage/PHP/CodeCoverage/Version.php', 'PHP_Timer' => $vendorDir . '/phpunit/php-timer/src/Timer.php', 'PHP_Token' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_TokenWithScope' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_TokenWithScopeAndVisibility' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ABSTRACT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_AMPERSAND' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_AND_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ARRAY' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ARRAY_CAST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_AS' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_AT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_BACKTICK' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_BAD_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_BOOLEAN_AND' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_BOOLEAN_OR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_BOOL_CAST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_BREAK' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CALLABLE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CARET' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CASE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CATCH' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLASS' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLASS_C' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLASS_NAME_CONSTANT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLONE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLOSE_BRACKET' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLOSE_CURLY' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLOSE_SQUARE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CLOSE_TAG' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_COLON' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_COMMA' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_COMMENT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CONCAT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CONST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CONSTANT_ENCAPSED_STRING' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CONTINUE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_CURLY_OPEN' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DEC' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DECLARE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DEFAULT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DIR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DIV' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DIV_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DNUMBER' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DO' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOC_COMMENT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOLLAR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOUBLE_ARROW' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOUBLE_CAST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOUBLE_COLON' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_DOUBLE_QUOTES' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ECHO' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ELSE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ELSEIF' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_EMPTY' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ENCAPSED_AND_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ENDDECLARE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ENDFOR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ENDFOREACH' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ENDIF' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ENDSWITCH' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ENDWHILE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_END_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_EVAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_EXCLAMATION_MARK' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_EXIT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_EXTENDS' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_FILE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_FINAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_FINALLY' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_FOR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_FOREACH' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_FUNC_C' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_GLOBAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_GOTO' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_GT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_HALT_COMPILER' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IF' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IMPLEMENTS' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INC' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INCLUDE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INCLUDE_ONCE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INLINE_HTML' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INSTANCEOF' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INSTEADOF' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INTERFACE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_INT_CAST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_ISSET' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IS_GREATER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IS_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IS_NOT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IS_NOT_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_IS_SMALLER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_Includes' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_LINE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_LIST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_LNUMBER' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_LOGICAL_AND' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_LOGICAL_OR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_LOGICAL_XOR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_LT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_METHOD_C' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_MINUS' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_MINUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_MOD_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_MULT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_MUL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_NAMESPACE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_NEW' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_NS_C' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_NS_SEPARATOR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_NUM_STRING' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OBJECT_CAST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OPEN_BRACKET' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OPEN_CURLY' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OPEN_SQUARE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OPEN_TAG' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OPEN_TAG_WITH_ECHO' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PERCENT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PIPE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PLUS' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PLUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PRINT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PRIVATE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PROTECTED' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_PUBLIC' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_QUESTION_MARK' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_REQUIRE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_REQUIRE_ONCE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_RETURN' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_SEMICOLON' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_SL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_SL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_SR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_SR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_START_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_STATIC' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_STRING' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_STRING_CAST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_STRING_VARNAME' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_SWITCH' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_Stream' => $vendorDir . '/phpunit/php-token-stream/PHP/Token/Stream.php', 'PHP_Token_Stream_CachingFactory' => $vendorDir . '/phpunit/php-token-stream/PHP/Token/Stream/CachingFactory.php', 'PHP_Token_THROW' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_TILDE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_TRAIT' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_TRAIT_C' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_TRY' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_UNSET' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_UNSET_CAST' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_USE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_VAR' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_VARIABLE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_WHILE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_XOR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'PHP_Token_YIELD' => $vendorDir . '/phpunit/php-token-stream/PHP/Token.php', 'Text_Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php', 'WP_Export_Base_Writer' => $baseDir . '/php/export/writers.php', 'WP_Export_Exception' => $baseDir . '/php/export/class-wp-export-query.php', 'WP_Export_File_Writer' => $baseDir . '/php/export/writers.php', 'WP_Export_Oxymel' => $baseDir . '/php/export/class-wp-export-oxymel.php', 'WP_Export_Query' => $baseDir . '/php/export/class-wp-export-query.php', 'WP_Export_Returner' => $baseDir . '/php/export/writers.php', 'WP_Export_Split_Files_Writer' => $baseDir . '/php/export/writers.php', 'WP_Export_Term_Exception' => $baseDir . '/php/export/class-wp-export-query.php', 'WP_Export_WXR_Formatter' => $baseDir . '/php/export/class-wp-export-wxr-formatter.php', 'WP_Export_XML_Over_HTTP' => $baseDir . '/php/export/writers.php', 'WP_Iterator_Exception' => $baseDir . '/php/export/iterators.php', 'WP_Map_Iterator' => $baseDir . '/php/export/iterators.php', 'WP_Post_IDs_Iterator' => $baseDir . '/php/export/iterators.php', ); <?php // include_paths.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( $vendorDir . '/phpunit/phpunit-mock-objects', $vendorDir . '/phpunit/php-token-stream', $vendorDir . '/phpunit/php-code-coverage', $vendorDir . '/phpunit/phpunit', $vendorDir . '/symfony/yaml', ); <?php // autoload_files.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( $vendorDir . '/ramsey/array_column/src/array_column.php', $vendorDir . '/mustangostang/spyc/Spyc.php', $vendorDir . '/wp-cli/php-cli-tools/lib/cli/cli.php', ); <?php // autoload_real.php @generated by Composer class ComposerAutoloaderInit675287e1dbacd27bbfb1e3e9e0740a37 { private static $loader; public static function loadClassLoader($class) { if ('Composer\Autoload\ClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } } public static function getLoader() { if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('ComposerAutoloaderInit675287e1dbacd27bbfb1e3e9e0740a37', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit675287e1dbacd27bbfb1e3e9e0740a37', 'loadClassLoader')); $includePaths = require __DIR__ . '/include_paths.php'; array_push($includePaths, get_include_path()); set_include_path(join(PATH_SEPARATOR, $includePaths)); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } $loader->register(true); $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { composerRequire675287e1dbacd27bbfb1e3e9e0740a37($file); } return $loader; } } function composerRequire675287e1dbacd27bbfb1e3e9e0740a37($file) { require $file; } <?php /* * This file is part of Composer. * * (c) Nils Adermann <naderman@naderman.de> * Jordi Boggiano <j.boggiano@seld.be> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Autoload; /** * ClassLoader implements a PSR-0 class loader * * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md * * $loader = new \Composer\Autoload\ClassLoader(); * * // register classes with namespaces * $loader->add('Symfony\Component', __DIR__.'/component'); * $loader->add('Symfony', __DIR__.'/framework'); * * // activate the autoloader * $loader->register(); * * // to enable searching the include path (eg. for PEAR packages) * $loader->setUseIncludePath(true); * * In this example, if you try to use a class in the Symfony\Component * namespace or one of its children (Symfony\Component\Console for instance), * the autoloader will first look for the class under the component/ * directory, and it will then fallback to the framework/ directory if not * found before giving up. * * This class is loosely based on the Symfony UniversalClassLoader. * * @author Fabien Potencier <fabien@symfony.com> * @author Jordi Boggiano <j.boggiano@seld.be> */ class ClassLoader { // PSR-4 private $prefixLengthsPsr4 = array(); private $prefixDirsPsr4 = array(); private $fallbackDirsPsr4 = array(); // PSR-0 private $prefixesPsr0 = array(); private $fallbackDirsPsr0 = array(); private $useIncludePath = false; private $classMap = array(); private $classMapAuthoritative = false; public function getPrefixes() { if (!empty($this->prefixesPsr0)) { return call_user_func_array('array_merge', $this->prefixesPsr0); } return array(); } public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } public function getFallbackDirs() { return $this->fallbackDirsPsr0; } public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } public function getClassMap() { return $this->classMap; } /** * @param array $classMap Class to filename map */ public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } } /** * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * * @param string $prefix The prefix * @param array|string $paths The PSR-0 root directories * @param bool $prepend Whether to prepend the directories */ public function add($prefix, $paths, $prepend = false) { if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( (array) $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, (array) $paths ); } return; } $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { $this->prefixesPsr0[$first][$prefix] = (array) $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( (array) $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], (array) $paths ); } } /** * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-0 base directories * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException */ public function addPsr4($prefix, $paths, $prepend = false) { if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( (array) $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, (array) $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { // Register directories for a new namespace. $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( (array) $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], (array) $paths ); } } /** * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * * @param string $prefix The prefix * @param array|string $paths The PSR-0 base directories */ public function set($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr0 = (array) $paths; } else { $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } } /** * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException */ public function setPsr4($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr4 = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } } /** * Turns on searching the include path for class files. * * @param bool $useIncludePath */ public function setUseIncludePath($useIncludePath) { $this->useIncludePath = $useIncludePath; } /** * Can be used to check if the autoloader uses the include path to check * for classes. * * @return bool */ public function getUseIncludePath() { return $this->useIncludePath; } /** * Turns off searching the prefix and fallback directories for classes * that have not been registered with the class map. * * @param bool $classMapAuthoritative */ public function setClassMapAuthoritative($classMapAuthoritative) { $this->classMapAuthoritative = $classMapAuthoritative; } /** * Should class lookup fail if not found in the current class map? * * @return bool */ public function isClassMapAuthoritative() { return $this->classMapAuthoritative; } /** * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); } /** * Unregisters this instance as an autoloader. */ public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); } /** * Loads the given class or interface. * * @param string $class The name of the class * @return bool|null True if loaded, null otherwise */ public function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file); return true; } } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|false The path if found, false otherwise */ public function findFile($class) { // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 if ('\\' == $class[0]) { $class = substr($class, 1); } // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative) { return false; } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if ($file === null && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if ($file === null) { // Remember that this class does not exist. return $this->classMap[$class] = false; } return $file; } private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirsPsr4[$prefix] as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } // PSR-0 include paths. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } } } /** * Scope isolated include. * * Prevents access to $this/self from included files. */ function includeFile($file) { include $file; } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver; use Composer\Semver\Constraint\Constraint; class Comparator { /** * Evaluates the expression: $version1 > $version2. * * @param string $version1 * @param string $version2 * * @return bool */ public static function greaterThan($version1, $version2) { return self::compare($version1, '>', $version2); } /** * Evaluates the expression: $version1 >= $version2. * * @param string $version1 * @param string $version2 * * @return bool */ public static function greaterThanOrEqualTo($version1, $version2) { return self::compare($version1, '>=', $version2); } /** * Evaluates the expression: $version1 < $version2. * * @param string $version1 * @param string $version2 * * @return bool */ public static function lessThan($version1, $version2) { return self::compare($version1, '<', $version2); } /** * Evaluates the expression: $version1 <= $version2. * * @param string $version1 * @param string $version2 * * @return bool */ public static function lessThanOrEqualTo($version1, $version2) { return self::compare($version1, '<=', $version2); } /** * Evaluates the expression: $version1 == $version2. * * @param string $version1 * @param string $version2 * * @return bool */ public static function equalTo($version1, $version2) { return self::compare($version1, '==', $version2); } /** * Evaluates the expression: $version1 != $version2. * * @param string $version1 * @param string $version2 * * @return bool */ public static function notEqualTo($version1, $version2) { return self::compare($version1, '!=', $version2); } /** * Evaluates the expression: $version1 $operator $version2. * * @param string $version1 * @param string $operator * @param string $version2 * * @return bool */ public static function compare($version1, $operator, $version2) { $constraint = new Constraint($operator, $version2); return $constraint->matches(new Constraint('==', $version1)); } } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver\Constraint; /** * Base constraint class. */ abstract class AbstractConstraint implements ConstraintInterface { /** @var string */ protected $prettyString; /** * @param ConstraintInterface $provider * * @return bool */ public function matches(ConstraintInterface $provider) { if ($provider instanceof MultiConstraint) { // turn matching around to find a match return $provider->matches($this); } if ($provider instanceof $this) { // see note at bottom of this class declaration return $this->matchSpecific($provider); } return true; } /** * @param string $prettyString */ public function setPrettyString($prettyString) { $this->prettyString = $prettyString; } /** * @return string */ public function getPrettyString() { if ($this->prettyString) { return $this->prettyString; } return $this->__toString(); } // implementations must implement a method of this format: // not declared abstract here because type hinting violates parameter coherence (TODO right word?) // public function matchSpecific(<SpecificConstraintType> $provider); } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver\Constraint; /** * Defines a constraint. */ class Constraint extends AbstractConstraint { /* operator integer values */ const OP_EQ = 0; const OP_LT = 1; const OP_LE = 2; const OP_GT = 3; const OP_GE = 4; const OP_NE = 5; /** * Operator to integer translation table. * * @var array */ private static $transOpStr = array( '=' => self::OP_EQ, '==' => self::OP_EQ, '<' => self::OP_LT, '<=' => self::OP_LE, '>' => self::OP_GT, '>=' => self::OP_GE, '<>' => self::OP_NE, '!=' => self::OP_NE, ); /** * Integer to operator translation table. * * @var array */ private static $transOpInt = array( self::OP_EQ => '==', self::OP_LT => '<', self::OP_LE => '<=', self::OP_GT => '>', self::OP_GE => '>=', self::OP_NE => '!=', ); /** @var string */ private $operator; /** @var string */ private $version; /** * Get all supported comparison operators. * * @return array */ public static function getSupportedOperators() { return array_keys(self::$transOpStr); } /** * Sets operator and version to compare with. * * @param string $operator * @param string $version * * @throws \InvalidArgumentException if invalid operator is given. */ public function __construct($operator, $version) { if (!isset(self::$transOpStr[$operator])) { throw new \InvalidArgumentException(sprintf( 'Invalid operator "%s" given, expected one of: %s', $operator, implode(', ', self::getSupportedOperators()) )); } $this->operator = self::$transOpStr[$operator]; $this->version = $version; } /** * @param string $a * @param string $b * @param string $operator * @param bool $compareBranches * * @throws \InvalidArgumentException if invalid operator is given. * * @return bool */ public function versionCompare($a, $b, $operator, $compareBranches = false) { if (!isset(self::$transOpStr[$operator])) { throw new \InvalidArgumentException(sprintf( 'Invalid operator "%s" given, expected one of: %s', $operator, implode(', ', self::getSupportedOperators()) )); } $aIsBranch = 'dev-' === substr($a, 0, 4); $bIsBranch = 'dev-' === substr($b, 0, 4); if ($aIsBranch && $bIsBranch) { return $operator === '==' && $a === $b; } // when branches are not comparable, we make sure dev branches never match anything if (!$compareBranches && ($aIsBranch || $bIsBranch)) { return false; } return version_compare($a, $b, $operator); } /** * @param Constraint $provider * @param bool $compareBranches * * @return bool */ public function matchSpecific(Constraint $provider, $compareBranches = false) { $noEqualOp = str_replace('=', '', self::$transOpInt[$this->operator]); $providerNoEqualOp = str_replace('=', '', self::$transOpInt[$provider->operator]); $isEqualOp = self::OP_EQ === $this->operator; $isNonEqualOp = self::OP_NE === $this->operator; $isProviderEqualOp = self::OP_EQ === $provider->operator; $isProviderNonEqualOp = self::OP_NE === $provider->operator; // '!=' operator is match when other operator is not '==' operator or version is not match // these kinds of comparisons always have a solution if ($isNonEqualOp || $isProviderNonEqualOp) { return !$isEqualOp && !$isProviderEqualOp || $this->versionCompare($provider->version, $this->version, '!=', $compareBranches); } // an example for the condition is <= 2.0 & < 1.0 // these kinds of comparisons always have a solution if ($this->operator !== self::OP_EQ && $noEqualOp === $providerNoEqualOp) { return true; } if ($this->versionCompare($provider->version, $this->version, self::$transOpInt[$this->operator], $compareBranches)) { // special case, e.g. require >= 1.0 and provide < 1.0 // 1.0 >= 1.0 but 1.0 is outside of the provided interval if ($provider->version === $this->version && self::$transOpInt[$provider->operator] === $providerNoEqualOp && self::$transOpInt[$this->operator] !== $noEqualOp) { return false; } return true; } return false; } /** * @return string */ public function __toString() { return self::$transOpInt[$this->operator] . ' ' . $this->version; } } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver\Constraint; interface ConstraintInterface { /** * @param ConstraintInterface $provider * * @return bool */ public function matches(ConstraintInterface $provider); /** * @param string $prettyString */ public function setPrettyString($prettyString); /** * @return string */ public function getPrettyString(); /** * @return string */ public function __toString(); } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver\Constraint; /** * Defines the absence of a constraint. */ class EmptyConstraint implements ConstraintInterface { /** @var string */ protected $prettyString; /** * @param ConstraintInterface $provider * * @return bool */ public function matches(ConstraintInterface $provider) { return true; } /** * @param $prettyString */ public function setPrettyString($prettyString) { $this->prettyString = $prettyString; } /** * @return string */ public function getPrettyString() { if ($this->prettyString) { return $this->prettyString; } return $this->__toString(); } /** * @return string */ public function __toString() { return '[]'; } } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver\Constraint; /** * Defines a conjunctive or disjunctive set of constraints. */ class MultiConstraint implements ConstraintInterface { /** @var ConstraintInterface[] */ protected $constraints; /** @var string */ protected $prettyString; /** @var bool */ protected $conjunctive; /** * @param ConstraintInterface[] $constraints A set of constraints * @param bool $conjunctive Whether the constraints should be treated as conjunctive or disjunctive */ public function __construct(array $constraints, $conjunctive = true) { $this->constraints = $constraints; $this->conjunctive = $conjunctive; } /** * @param ConstraintInterface $provider * * @return bool */ public function matches(ConstraintInterface $provider) { if (false === $this->conjunctive) { foreach ($this->constraints as $constraint) { if ($constraint->matches($provider)) { return true; } } return false; } foreach ($this->constraints as $constraint) { if (!$constraint->matches($provider)) { return false; } } return true; } /** * @param string $prettyString */ public function setPrettyString($prettyString) { $this->prettyString = $prettyString; } /** * @return string */ public function getPrettyString() { if ($this->prettyString) { return $this->prettyString; } return $this->__toString(); } /** * @return string */ public function __toString() { $constraints = array(); foreach ($this->constraints as $constraint) { $constraints[] = (string) $constraint; } return '[' . implode($this->conjunctive ? ' ' : ' || ', $constraints) . ']'; } } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver; use Composer\Semver\Constraint\Constraint; class Semver { const SORT_ASC = 1; const SORT_DESC = -1; /** @var VersionParser */ private static $versionParser; /** * Determine if given version satisfies given constraints. * * @param string $version * @param string $constraints * * @return bool */ public static function satisfies($version, $constraints) { if (null === self::$versionParser) { self::$versionParser = new VersionParser(); } $versionParser = self::$versionParser; $provider = new Constraint('==', $versionParser->normalize($version)); $constraints = $versionParser->parseConstraints($constraints); return $constraints->matches($provider); } /** * Return all versions that satisfy given constraints. * * @param array $versions * @param string $constraints * * @return array */ public static function satisfiedBy(array $versions, $constraints) { $versions = array_filter($versions, function ($version) use ($constraints) { return Semver::satisfies($version, $constraints); }); return array_values($versions); } /** * Sort given array of versions. * * @param array $versions * * @return array */ public static function sort(array $versions) { return self::usort($versions, self::SORT_ASC); } /** * Sort given array of versions in reverse. * * @param array $versions * * @return array */ public static function rsort(array $versions) { return self::usort($versions, self::SORT_DESC); } /** * @param array $versions * @param int $direction * * @return array */ private static function usort(array $versions, $direction) { if (null === self::$versionParser) { self::$versionParser = new VersionParser(); } $versionParser = self::$versionParser; $normalized = array(); // Normalize outside of usort() scope for minor performance increase. // Creates an array of arrays: [[normalized, key], ...] foreach ($versions as $key => $version) { $normalized[] = array($versionParser->normalize($version), $key); } usort($normalized, function (array $left, array $right) use ($direction) { if ($left[0] === $right[0]) { return 0; } if (Comparator::lessThan($left[0], $right[0])) { return -$direction; } return $direction; }); // Recreate input array, using the original indexes which are now in sorted order. $sorted = array(); foreach ($normalized as $item) { $sorted[] = $versions[$item[1]]; } return $sorted; } } <?php /* * This file is part of composer/semver. * * (c) Composer <https://github.com/composer> * * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace Composer\Semver; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\EmptyConstraint; use Composer\Semver\Constraint\MultiConstraint; use Composer\Semver\Constraint\Constraint; /** * Version parser. * * @author Jordi Boggiano <j.boggiano@seld.be> */ class VersionParser { /** @var string */ private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?'; /** @var array */ private static $stabilities = array( 'stable', 'RC', 'beta', 'alpha', 'dev', ); /** * Returns the stability of a version. * * @param string $version * * @return string */ public static function parseStability($version) { $version = preg_replace('{#.+$}i', '', $version); if ('dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4)) { return 'dev'; } preg_match('{' . self::$modifierRegex . '$}i', strtolower($version), $match); if (!empty($match[3])) { return 'dev'; } if (!empty($match[1])) { if ('beta' === $match[1] || 'b' === $match[1]) { return 'beta'; } if ('alpha' === $match[1] || 'a' === $match[1]) { return 'alpha'; } if ('rc' === $match[1]) { return 'RC'; } } return 'stable'; } /** * @param string $stability * * @return string */ public static function normalizeStability($stability) { $stability = strtolower($stability); return $stability === 'rc' ? 'RC' : $stability; } /** * Normalizes a version string to be able to perform comparisons on it. * * @param string $version * @param string $fullVersion optional complete version string to give more context * * @throws \UnexpectedValueException * * @return string */ public function normalize($version, $fullVersion = null) { $version = trim($version); if (null === $fullVersion) { $fullVersion = $version; } // strip off aliasing if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $version, $match)) { $version = $match[1]; } // strip off build metadata if (preg_match('{^([^,\s+]+)\+[^\s]+$}', $version, $match)) { $version = $match[1]; } // match master-like branches if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) { return '9999999-dev'; } if ('dev-' === strtolower(substr($version, 0, 4))) { return 'dev-' . substr($version, 4); } // match classical versioning if (preg_match('{^v?(\d{1,5})(\.\d+)?(\.\d+)?(\.\d+)?' . self::$modifierRegex . '$}i', $version, $matches)) { $version = $matches[1] . (!empty($matches[2]) ? $matches[2] : '.0') . (!empty($matches[3]) ? $matches[3] : '.0') . (!empty($matches[4]) ? $matches[4] : '.0'); $index = 5; // match date(time) based versioning } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) { $version = preg_replace('{\D}', '-', $matches[1]); $index = 2; } // add version modifiers if a version was matched if (isset($index)) { if (!empty($matches[$index])) { if ('stable' === $matches[$index]) { return $version; } $version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index + 1]) ? $matches[$index + 1] : ''); } if (!empty($matches[$index + 2])) { $version .= '-dev'; } return $version; } // match dev branches if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) { try { return $this->normalizeBranch($match[1]); } catch (\Exception $e) { } } $extraMessage = ''; if (preg_match('{ +as +' . preg_quote($version) . '$}', $fullVersion)) { $extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version'; } elseif (preg_match('{^' . preg_quote($version) . ' +as +}', $fullVersion)) { $extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-'; } throw new \UnexpectedValueException('Invalid version string "' . $version . '"' . $extraMessage); } /** * Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison. * * @param string $branch Branch name (e.g. 2.1.x-dev) * * @return string|false Numeric prefix if present (e.g. 2.1.) or false */ public function parseNumericAliasPrefix($branch) { if (preg_match('{^(?P<version>(\d+\\.)*\d+)(?:\.x)?-dev$}i', $branch, $matches)) { return $matches['version'] . '.'; } return false; } /** * Normalizes a branch name to be able to perform comparisons on it. * * @param string $name * * @return string */ public function normalizeBranch($name) { $name = trim($name); if (in_array($name, array('master', 'trunk', 'default'))) { return $this->normalize($name); } if (preg_match('{^v?(\d+)(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?$}i', $name, $matches)) { $version = ''; for ($i = 1; $i < 5; ++$i) { $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x'; } return str_replace('x', '9999999', $version) . '-dev'; } return 'dev-' . $name; } /** * Parses as constraint string into LinkConstraint objects. * * @param string $constraints * * @return ConstraintInterface */ public function parseConstraints($constraints) { $prettyConstraint = $constraints; if (preg_match('{^([^,\s]*?)@(' . implode('|', self::$stabilities) . ')$}i', $constraints, $match)) { $constraints = empty($match[1]) ? '*' : $match[1]; } if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraints, $match)) { $constraints = $match[1]; } $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints)); $orGroups = array(); foreach ($orConstraints as $constraints) { $andConstraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $constraints); if (count($andConstraints) > 1) { $constraintObjects = array(); foreach ($andConstraints as $constraint) { foreach ($this->parseConstraint($constraint) as $parsedConstraint) { $constraintObjects[] = $parsedConstraint; } } } else { $constraintObjects = $this->parseConstraint($andConstraints[0]); } if (1 === count($constraintObjects)) { $constraint = $constraintObjects[0]; } else { $constraint = new MultiConstraint($constraintObjects); } $orGroups[] = $constraint; } if (1 === count($orGroups)) { $constraint = $orGroups[0]; } else { $constraint = new MultiConstraint($orGroups, false); } $constraint->setPrettyString($prettyConstraint); return $constraint; } /** * @param string $constraint * * @throws \UnexpectedValueException * * @return array */ private function parseConstraint($constraint) { if (preg_match('{^([^,\s]+?)@(' . implode('|', self::$stabilities) . ')$}i', $constraint, $match)) { $constraint = $match[1]; if ($match[2] !== 'stable') { $stabilityModifier = $match[2]; } } if (preg_match('{^[xX*](\.[xX*])*$}i', $constraint)) { return array(new EmptyConstraint()); } $versionRegex = 'v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?' . self::$modifierRegex . '(?:\+[^\s]+)?'; // Tilde Range // // Like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous // version, to ensure that unstable instances of the current version are allowed. However, if a stability // suffix is added to the constraint, then a >= match on the current version is used instead. if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) { if (substr($constraint, 0, 2) === '~>') { throw new \UnexpectedValueException( 'Could not parse version constraint ' . $constraint . ': ' . 'Invalid operator "~>", you probably meant to use the "~" operator' ); } // Work out which position in the version we are operating at if (isset($matches[4]) && '' !== $matches[4]) { $position = 4; } elseif (isset($matches[3]) && '' !== $matches[3]) { $position = 3; } elseif (isset($matches[2]) && '' !== $matches[2]) { $position = 2; } else { $position = 1; } // Calculate the stability suffix $stabilitySuffix = ''; if (!empty($matches[5])) { $stabilitySuffix .= '-' . $this->expandStability($matches[5]) . (!empty($matches[6]) ? $matches[6] : ''); } if (!empty($matches[7])) { $stabilitySuffix .= '-dev'; } if (!$stabilitySuffix) { $stabilitySuffix = '-dev'; } $lowVersion = $this->manipulateVersionString($matches, $position, 0) . $stabilitySuffix; $lowerBound = new Constraint('>=', $lowVersion); // For upper bound, we increment the position of one more significance, // but highPosition = 0 would be illegal $highPosition = max(1, $position - 1); $highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev'; $upperBound = new Constraint('<', $highVersion); return array( $lowerBound, $upperBound, ); } // Caret Range // // Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple. // In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for // versions 0.X >=0.1.0, and no updates for versions 0.0.X if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) { // Work out which position in the version we are operating at if ('0' !== $matches[1] || '' === $matches[2]) { $position = 1; } elseif ('0' !== $matches[2] || '' === $matches[3]) { $position = 2; } else { $position = 3; } // Calculate the stability suffix $stabilitySuffix = ''; if (empty($matches[5]) && empty($matches[7])) { $stabilitySuffix .= '-dev'; } $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1)); $lowerBound = new Constraint('>=', $lowVersion); // For upper bound, we increment the position of one more significance, // but highPosition = 0 would be illegal $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev'; $upperBound = new Constraint('<', $highVersion); return array( $lowerBound, $upperBound, ); } // X Range // // Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple. // A partial version range is treated as an X-Range, so the special character is in fact optional. if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$}', $constraint, $matches)) { if (isset($matches[3]) && '' !== $matches[3]) { $position = 3; } elseif (isset($matches[2]) && '' !== $matches[2]) { $position = 2; } else { $position = 1; } $lowVersion = $this->manipulateVersionString($matches, $position) . '-dev'; $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev'; if ($lowVersion === '0.0.0.0-dev') { return array(new Constraint('<', $highVersion)); } return array( new Constraint('>=', $lowVersion), new Constraint('<', $highVersion), ); } // Hyphen Range // // Specifies an inclusive set. If a partial version is provided as the first version in the inclusive range, // then the missing pieces are replaced with zeroes. If a partial version is provided as the second version in // the inclusive range, then all versions that start with the supplied parts of the tuple are accepted, but // nothing that would be greater than the provided tuple parts. if (preg_match('{^(?P<from>' . $versionRegex . ') +- +(?P<to>' . $versionRegex . ')($)}i', $constraint, $matches)) { // Calculate the stability suffix $lowStabilitySuffix = ''; if (empty($matches[6]) && empty($matches[8])) { $lowStabilitySuffix = '-dev'; } $lowVersion = $this->normalize($matches['from']); $lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix); $empty = function ($x) { return ($x === 0 || $x === '0') ? false : empty($x); }; if ((!$empty($matches[11]) && !$empty($matches[12])) || !empty($matches[14]) || !empty($matches[16])) { $highVersion = $this->normalize($matches['to']); $upperBound = new Constraint('<=', $highVersion); } else { $highMatch = array('', $matches[10], $matches[11], $matches[12], $matches[13]); $highVersion = $this->manipulateVersionString($highMatch, $empty($matches[11]) ? 1 : 2, 1) . '-dev'; $upperBound = new Constraint('<', $highVersion); } return array( $lowerBound, $upperBound, ); } // Basic Comparators if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) { try { $version = $this->normalize($matches[2]); if (!empty($stabilityModifier) && $this->parseStability($version) === 'stable') { $version .= '-' . $stabilityModifier; } elseif ('<' === $matches[1] || '>=' === $matches[1]) { if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) { if (substr($matches[2], 0, 4) !== 'dev-') { $version .= '-dev'; } } } return array(new Constraint($matches[1] ?: '=', $version)); } catch (\Exception $e) { } } $message = 'Could not parse version constraint ' . $constraint; if (isset($e)) { $message .= ': ' . $e->getMessage(); } throw new \UnexpectedValueException($message); } /** * Increment, decrement, or simply pad a version number. * * Support function for {@link parseConstraint()} * * @param array $matches Array with version parts in array indexes 1,2,3,4 * @param int $position 1,2,3,4 - which segment of the version to increment/decrement * @param int $increment * @param string $pad The string to pad version parts after $position * * @return string The new version */ private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0') { for ($i = 4; $i > 0; --$i) { if ($i > $position) { $matches[$i] = $pad; } elseif ($i === $position && $increment) { $matches[$i] += $increment; // If $matches[$i] was 0, carry the decrement if ($matches[$i] < 0) { $matches[$i] = $pad; --$position; // Return null on a carry overflow if ($i === 1) { return; } } } } return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4]; } /** * Expand shorthand stability string to long version. * * @param string $stability * * @return string */ private function expandStability($stability) { $stability = strtolower($stability); switch ($stability) { case 'a': return 'alpha'; case 'b': return 'beta'; case 'p': case 'pl': return 'patch'; case 'rc': return 'RC'; default: return $stability; } } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Adapter; /** * Interface for finder engine implementations. * * @author Jean-François Simon <contact@jfsimon.fr> */ abstract class AbstractAdapter implements AdapterInterface { protected $followLinks = false; protected $mode = 0; protected $minDepth = 0; protected $maxDepth = PHP_INT_MAX; protected $exclude = array(); protected $names = array(); protected $notNames = array(); protected $contains = array(); protected $notContains = array(); protected $sizes = array(); protected $dates = array(); protected $filters = array(); protected $sort = false; protected $paths = array(); protected $notPaths = array(); protected $ignoreUnreadableDirs = false; private static $areSupported = array(); /** * {@inheritdoc} */ public function isSupported() { $name = $this->getName(); if (!array_key_exists($name, self::$areSupported)) { self::$areSupported[$name] = $this->canBeUsed(); } return self::$areSupported[$name]; } /** * {@inheritdoc} */ public function setFollowLinks($followLinks) { $this->followLinks = $followLinks; return $this; } /** * {@inheritdoc} */ public function setMode($mode) { $this->mode = $mode; return $this; } /** * {@inheritdoc} */ public function setDepths(array $depths) { $this->minDepth = 0; $this->maxDepth = PHP_INT_MAX; foreach ($depths as $comparator) { switch ($comparator->getOperator()) { case '>': $this->minDepth = $comparator->getTarget() + 1; break; case '>=': $this->minDepth = $comparator->getTarget(); break; case '<': $this->maxDepth = $comparator->getTarget() - 1; break; case '<=': $this->maxDepth = $comparator->getTarget(); break; default: $this->minDepth = $this->maxDepth = $comparator->getTarget(); } } return $this; } /** * {@inheritdoc} */ public function setExclude(array $exclude) { $this->exclude = $exclude; return $this; } /** * {@inheritdoc} */ public function setNames(array $names) { $this->names = $names; return $this; } /** * {@inheritdoc} */ public function setNotNames(array $notNames) { $this->notNames = $notNames; return $this; } /** * {@inheritdoc} */ public function setContains(array $contains) { $this->contains = $contains; return $this; } /** * {@inheritdoc} */ public function setNotContains(array $notContains) { $this->notContains = $notContains; return $this; } /** * {@inheritdoc} */ public function setSizes(array $sizes) { $this->sizes = $sizes; return $this; } /** * {@inheritdoc} */ public function setDates(array $dates) { $this->dates = $dates; return $this; } /** * {@inheritdoc} */ public function setFilters(array $filters) { $this->filters = $filters; return $this; } /** * {@inheritdoc} */ public function setSort($sort) { $this->sort = $sort; return $this; } /** * {@inheritdoc} */ public function setPath(array $paths) { $this->paths = $paths; return $this; } /** * {@inheritdoc} */ public function setNotPath(array $notPaths) { $this->notPaths = $notPaths; return $this; } /** * {@inheritdoc} */ public function ignoreUnreadableDirs($ignore = true) { $this->ignoreUnreadableDirs = (bool) $ignore; return $this; } /** * Returns whether the adapter is supported in the current environment. * * This method should be implemented in all adapters. Do not implement * isSupported in the adapters as the generic implementation provides a cache * layer. * * @see isSupported() * * @return bool Whether the adapter is supported */ abstract protected function canBeUsed(); } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Adapter; use Symfony\Component\Finder\Exception\AccessDeniedException; use Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\Shell\Shell; use Symfony\Component\Finder\Expression\Expression; use Symfony\Component\Finder\Shell\Command; use Symfony\Component\Finder\Comparator\NumberComparator; use Symfony\Component\Finder\Comparator\DateComparator; /** * Shell engine implementation using GNU find command. * * @author Jean-François Simon <contact@jfsimon.fr> */ abstract class AbstractFindAdapter extends AbstractAdapter { /** * @var Shell */ protected $shell; /** * Constructor. */ public function __construct() { $this->shell = new Shell(); } /** * {@inheritdoc} */ public function searchInDirectory($dir) { // having "/../" in path make find fail $dir = realpath($dir); // searching directories containing or not containing strings leads to no result if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) { return new Iterator\FilePathsIterator(array(), $dir); } $command = Command::create(); $find = $this->buildFindCommand($command, $dir); if ($this->followLinks) { $find->add('-follow'); } $find->add('-mindepth')->add($this->minDepth + 1); if (PHP_INT_MAX !== $this->maxDepth) { $find->add('-maxdepth')->add($this->maxDepth + 1); } if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) { $find->add('-type d'); } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) { $find->add('-type f'); } $this->buildNamesFiltering($find, $this->names); $this->buildNamesFiltering($find, $this->notNames, true); $this->buildPathsFiltering($find, $dir, $this->paths); $this->buildPathsFiltering($find, $dir, $this->notPaths, true); $this->buildSizesFiltering($find, $this->sizes); $this->buildDatesFiltering($find, $this->dates); $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs'); $useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut'); if ($useGrep && ($this->contains || $this->notContains)) { $grep = $command->ins('grep'); $this->buildContentFiltering($grep, $this->contains); $this->buildContentFiltering($grep, $this->notContains, true); } if ($useSort) { $this->buildSorting($command, $this->sort); } $command->setErrorHandler( $this->ignoreUnreadableDirs // If directory is unreadable and finder is set to ignore it, `stderr` is ignored. ? function ($stderr) { return; } : function ($stderr) { throw new AccessDeniedException($stderr); } ); $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute()); $iterator = new Iterator\FilePathsIterator($paths, $dir); if ($this->exclude) { $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); } if (!$useGrep && ($this->contains || $this->notContains)) { $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); } if ($this->filters) { $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); } if (!$useSort && $this->sort) { $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); $iterator = $iteratorAggregate->getIterator(); } return $iterator; } /** * {@inheritdoc} */ protected function canBeUsed() { return $this->shell->testCommand('find'); } /** * @param Command $command * @param string $dir * * @return Command */ protected function buildFindCommand(Command $command, $dir) { return $command ->ins('find') ->add('find ') ->arg($dir) ->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions } /** * @param Command $command * @param string[] $names * @param bool $not */ private function buildNamesFiltering(Command $command, array $names, $not = false) { if (0 === count($names)) { return; } $command->add($not ? '-not' : null)->cmd('('); foreach ($names as $i => $name) { $expr = Expression::create($name); // Find does not support expandable globs ("*.{a,b}" syntax). if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { $expr = Expression::create($expr->getGlob()->toRegex(false)); } // Fixes 'not search' and 'full path matching' regex problems. // - Jokers '.' are replaced by [^/]. // - We add '[^/]*' before and after regex (if no ^|$ flags are present). if ($expr->isRegex()) { $regex = $expr->getRegex(); $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*') ->setStartFlag(false) ->setStartJoker(true) ->replaceJokers('[^/]'); if (!$regex->hasEndFlag() || $regex->hasEndJoker()) { $regex->setEndJoker(false)->append('[^/]*'); } } $command ->add($i > 0 ? '-or' : null) ->add($expr->isRegex() ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') : ($expr->isCaseSensitive() ? '-name' : '-iname') ) ->arg($expr->renderPattern()); } $command->cmd(')'); } /** * @param Command $command * @param string $dir * @param string[] $paths * @param bool $not */ private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false) { if (0 === count($paths)) { return; } $command->add($not ? '-not' : null)->cmd('('); foreach ($paths as $i => $path) { $expr = Expression::create($path); // Find does not support expandable globs ("*.{a,b}" syntax). if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { $expr = Expression::create($expr->getGlob()->toRegex(false)); } // Fixes 'not search' regex problems. if ($expr->isRegex()) { $regex = $expr->getRegex(); $regex->prepend($regex->hasStartFlag() ? preg_quote($dir).DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag()); } else { $expr->prepend('*')->append('*'); } $command ->add($i > 0 ? '-or' : null) ->add($expr->isRegex() ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') : ($expr->isCaseSensitive() ? '-path' : '-ipath') ) ->arg($expr->renderPattern()); } $command->cmd(')'); } /** * @param Command $command * @param NumberComparator[] $sizes */ private function buildSizesFiltering(Command $command, array $sizes) { foreach ($sizes as $i => $size) { $command->add($i > 0 ? '-and' : null); switch ($size->getOperator()) { case '<=': $command->add('-size -'.($size->getTarget() + 1).'c'); break; case '>=': $command->add('-size +'.($size->getTarget() - 1).'c'); break; case '>': $command->add('-size +'.$size->getTarget().'c'); break; case '!=': $command->add('-size -'.$size->getTarget().'c'); $command->add('-size +'.$size->getTarget().'c'); break; case '<': default: $command->add('-size -'.$size->getTarget().'c'); } } } /** * @param Command $command * @param DateComparator[] $dates */ private function buildDatesFiltering(Command $command, array $dates) { foreach ($dates as $i => $date) { $command->add($i > 0 ? '-and' : null); $mins = (int) round((time() - $date->getTarget()) / 60); if (0 > $mins) { // mtime is in the future $command->add(' -mmin -0'); // we will have no result so we don't need to continue return; } switch ($date->getOperator()) { case '<=': $command->add('-mmin +'.($mins - 1)); break; case '>=': $command->add('-mmin -'.($mins + 1)); break; case '>': $command->add('-mmin -'.$mins); break; case '!=': $command->add('-mmin +'.$mins.' -or -mmin -'.$mins); break; case '<': default: $command->add('-mmin +'.$mins); } } } /** * @param Command $command * @param string $sort * * @throws \InvalidArgumentException */ private function buildSorting(Command $command, $sort) { $this->buildFormatSorting($command, $sort); } /** * @param Command $command * @param string $sort */ abstract protected function buildFormatSorting(Command $command, $sort); /** * @param Command $command * @param array $contains * @param bool $not */ abstract protected function buildContentFiltering(Command $command, array $contains, $not = false); } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Adapter; /** * @author Jean-François Simon <contact@jfsimon.fr> */ interface AdapterInterface { /** * @param bool $followLinks * * @return AdapterInterface Current instance */ public function setFollowLinks($followLinks); /** * @param int $mode * * @return AdapterInterface Current instance */ public function setMode($mode); /** * @param array $exclude * * @return AdapterInterface Current instance */ public function setExclude(array $exclude); /** * @param array $depths * * @return AdapterInterface Current instance */ public function setDepths(array $depths); /** * @param array $names * * @return AdapterInterface Current instance */ public function setNames(array $names); /** * @param array $notNames * * @return AdapterInterface Current instance */ public function setNotNames(array $notNames); /** * @param array $contains * * @return AdapterInterface Current instance */ public function setContains(array $contains); /** * @param array $notContains * * @return AdapterInterface Current instance */ public function setNotContains(array $notContains); /** * @param array $sizes * * @return AdapterInterface Current instance */ public function setSizes(array $sizes); /** * @param array $dates * * @return AdapterInterface Current instance */ public function setDates(array $dates); /** * @param array $filters * * @return AdapterInterface Current instance */ public function setFilters(array $filters); /** * @param \Closure|int $sort * * @return AdapterInterface Current instance */ public function setSort($sort); /** * @param array $paths * * @return AdapterInterface Current instance */ public function setPath(array $paths); /** * @param array $notPaths * * @return AdapterInterface Current instance */ public function setNotPath(array $notPaths); /** * @param bool $ignore * * @return AdapterInterface Current instance */ public function ignoreUnreadableDirs($ignore = true); /** * @param string $dir * * @return \Iterator Result iterator */ public function searchInDirectory($dir); /** * Tests adapter support for current platform. * * @return bool */ public function isSupported(); /** * Returns adapter name. * * @return string */ public function getName(); } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Adapter; use Symfony\Component\Finder\Shell\Shell; use Symfony\Component\Finder\Shell\Command; use Symfony\Component\Finder\Iterator\SortableIterator; use Symfony\Component\Finder\Expression\Expression; /** * Shell engine implementation using BSD find command. * * @author Jean-François Simon <contact@jfsimon.fr> */ class BsdFindAdapter extends AbstractFindAdapter { /** * {@inheritdoc} */ public function getName() { return 'bsd_find'; } /** * {@inheritdoc} */ protected function canBeUsed() { return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed(); } /** * {@inheritdoc} */ protected function buildFormatSorting(Command $command, $sort) { switch ($sort) { case SortableIterator::SORT_BY_NAME: $command->ins('sort')->add('| sort'); return; case SortableIterator::SORT_BY_TYPE: $format = '%HT'; break; case SortableIterator::SORT_BY_ACCESSED_TIME: $format = '%a'; break; case SortableIterator::SORT_BY_CHANGED_TIME: $format = '%c'; break; case SortableIterator::SORT_BY_MODIFIED_TIME: $format = '%m'; break; default: throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); } $command ->add('-print0 | xargs -0 stat -f') ->arg($format.'%t%N') ->add('| sort | cut -f 2'); } /** * {@inheritdoc} */ protected function buildFindCommand(Command $command, $dir) { parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1); return $command; } /** * {@inheritdoc} */ protected function buildContentFiltering(Command $command, array $contains, $not = false) { foreach ($contains as $contain) { $expr = Expression::create($contain); // todo: avoid forking process for each $pattern by using multiple -e options $command ->add('| grep -v \'^$\'') ->add('| xargs -I{} grep -I') ->add($expr->isCaseSensitive() ? null : '-i') ->add($not ? '-L' : '-l') ->add('-Ee')->arg($expr->renderPattern()) ->add('{}') ; } } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Adapter; use Symfony\Component\Finder\Shell\Shell; use Symfony\Component\Finder\Shell\Command; use Symfony\Component\Finder\Iterator\SortableIterator; use Symfony\Component\Finder\Expression\Expression; /** * Shell engine implementation using GNU find command. * * @author Jean-François Simon <contact@jfsimon.fr> */ class GnuFindAdapter extends AbstractFindAdapter { /** * {@inheritdoc} */ public function getName() { return 'gnu_find'; } /** * {@inheritdoc} */ protected function buildFormatSorting(Command $command, $sort) { switch ($sort) { case SortableIterator::SORT_BY_NAME: $command->ins('sort')->add('| sort'); return; case SortableIterator::SORT_BY_TYPE: $format = '%y'; break; case SortableIterator::SORT_BY_ACCESSED_TIME: $format = '%A@'; break; case SortableIterator::SORT_BY_CHANGED_TIME: $format = '%C@'; break; case SortableIterator::SORT_BY_MODIFIED_TIME: $format = '%T@'; break; default: throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); } $command ->get('find') ->add('-printf') ->arg($format.' %h/%f\\n') ->add('| sort | cut') ->arg('-d ') ->arg('-f2-') ; } /** * {@inheritdoc} */ protected function canBeUsed() { return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed(); } /** * {@inheritdoc} */ protected function buildFindCommand(Command $command, $dir) { return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended'); } /** * {@inheritdoc} */ protected function buildContentFiltering(Command $command, array $contains, $not = false) { foreach ($contains as $contain) { $expr = Expression::create($contain); // todo: avoid forking process for each $pattern by using multiple -e options $command ->add('| xargs -I{} -r grep -I') ->add($expr->isCaseSensitive() ? null : '-i') ->add($not ? '-L' : '-l') ->add('-Ee')->arg($expr->renderPattern()) ->add('{}') ; } } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Adapter; use Symfony\Component\Finder\Iterator; /** * PHP finder engine implementation. * * @author Jean-François Simon <contact@jfsimon.fr> */ class PhpAdapter extends AbstractAdapter { /** * {@inheritdoc} */ public function searchInDirectory($dir) { $flags = \RecursiveDirectoryIterator::SKIP_DOTS; if ($this->followLinks) { $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; } $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs); if ($this->exclude) { $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); } $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) { $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth); } if ($this->mode) { $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode); } if ($this->names || $this->notNames) { $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames); } if ($this->contains || $this->notContains) { $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); } if ($this->sizes) { $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes); } if ($this->dates) { $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates); } if ($this->filters) { $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); } if ($this->paths || $this->notPaths) { $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths); } if ($this->sort) { $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); $iterator = $iteratorAggregate->getIterator(); } return $iterator; } /** * {@inheritdoc} */ public function getName() { return 'php'; } /** * {@inheritdoc} */ protected function canBeUsed() { return true; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Comparator; /** * Comparator. * * @author Fabien Potencier <fabien@symfony.com> */ class Comparator { private $target; private $operator = '=='; /** * Gets the target value. * * @return string The target value */ public function getTarget() { return $this->target; } /** * Sets the target value. * * @param string $target The target value */ public function setTarget($target) { $this->target = $target; } /** * Gets the comparison operator. * * @return string The operator */ public function getOperator() { return $this->operator; } /** * Sets the comparison operator. * * @param string $operator A valid operator * * @throws \InvalidArgumentException */ public function setOperator($operator) { if (!$operator) { $operator = '=='; } if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) { throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); } $this->operator = $operator; } /** * Tests against the target. * * @param mixed $test A test value * * @return bool */ public function test($test) { switch ($this->operator) { case '>': return $test > $this->target; case '>=': return $test >= $this->target; case '<': return $test < $this->target; case '<=': return $test <= $this->target; case '!=': return $test != $this->target; } return $test == $this->target; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Comparator; /** * DateCompare compiles date comparisons. * * @author Fabien Potencier <fabien@symfony.com> */ class DateComparator extends Comparator { /** * Constructor. * * @param string $test A comparison string * * @throws \InvalidArgumentException If the test is not understood */ public function __construct($test) { if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); } try { $date = new \DateTime($matches[2]); $target = $date->format('U'); } catch (\Exception $e) { throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); } $operator = isset($matches[1]) ? $matches[1] : '=='; if ('since' === $operator || 'after' === $operator) { $operator = '>'; } if ('until' === $operator || 'before' === $operator) { $operator = '<'; } $this->setOperator($operator); $this->setTarget($target); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Comparator; /** * NumberComparator compiles a simple comparison to an anonymous * subroutine, which you can call with a value to be tested again. * * Now this would be very pointless, if NumberCompare didn't understand * magnitudes. * * The target value may use magnitudes of kilobytes (k, ki), * megabytes (m, mi), or gigabytes (g, gi). Those suffixed * with an i use the appropriate 2**n version in accordance with the * IEC standard: http://physics.nist.gov/cuu/Units/binary.html * * Based on the Perl Number::Compare module. * * @author Fabien Potencier <fabien@symfony.com> PHP port * @author Richard Clamp <richardc@unixbeard.net> Perl version * @copyright 2004-2005 Fabien Potencier <fabien@symfony.com> * @copyright 2002 Richard Clamp <richardc@unixbeard.net> * * @see http://physics.nist.gov/cuu/Units/binary.html */ class NumberComparator extends Comparator { /** * Constructor. * * @param string $test A comparison string * * @throws \InvalidArgumentException If the test is not understood */ public function __construct($test) { if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) { throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); } $target = $matches[2]; if (!is_numeric($target)) { throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target)); } if (isset($matches[3])) { // magnitude switch (strtolower($matches[3])) { case 'k': $target *= 1000; break; case 'ki': $target *= 1024; break; case 'm': $target *= 1000000; break; case 'mi': $target *= 1024 * 1024; break; case 'g': $target *= 1000000000; break; case 'gi': $target *= 1024 * 1024 * 1024; break; } } $this->setTarget($target); $this->setOperator(isset($matches[1]) ? $matches[1] : '=='); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Exception; /** * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> */ class AccessDeniedException extends \UnexpectedValueException { } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Exception; use Symfony\Component\Finder\Adapter\AdapterInterface; /** * Base exception for all adapter failures. * * @author Jean-François Simon <contact@jfsimon.fr> */ class AdapterFailureException extends \RuntimeException implements ExceptionInterface { /** * @var \Symfony\Component\Finder\Adapter\AdapterInterface */ private $adapter; /** * @param AdapterInterface $adapter * @param string|null $message * @param \Exception|null $previous */ public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null) { $this->adapter = $adapter; parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous); } /** * {@inheritdoc} */ public function getAdapter() { return $this->adapter; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Exception; /** * @author Jean-François Simon <contact@jfsimon.fr> */ interface ExceptionInterface { /** * @return \Symfony\Component\Finder\Adapter\AdapterInterface */ public function getAdapter(); } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Exception; /** * @author Jean-François Simon <contact@jfsimon.fr> */ class OperationNotPermitedException extends AdapterFailureException { } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Exception; use Symfony\Component\Finder\Adapter\AdapterInterface; use Symfony\Component\Finder\Shell\Command; /** * @author Jean-François Simon <contact@jfsimon.fr> */ class ShellCommandFailureException extends AdapterFailureException { /** * @var Command */ private $command; /** * @param AdapterInterface $adapter * @param Command $command * @param \Exception|null $previous */ public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null) { $this->command = $command; parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous); } /** * @return Command */ public function getCommand() { return $this->command; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Expression; /** * @author Jean-François Simon <contact@jfsimon.fr> */ class Expression implements ValueInterface { const TYPE_REGEX = 1; const TYPE_GLOB = 2; /** * @var ValueInterface */ private $value; /** * @param string $expr * * @return Expression */ public static function create($expr) { return new self($expr); } /** * @param string $expr */ public function __construct($expr) { try { $this->value = Regex::create($expr); } catch (\InvalidArgumentException $e) { $this->value = new Glob($expr); } } /** * @return string */ public function __toString() { return $this->render(); } /** * {@inheritdoc} */ public function render() { return $this->value->render(); } /** * {@inheritdoc} */ public function renderPattern() { return $this->value->renderPattern(); } /** * @return bool */ public function isCaseSensitive() { return $this->value->isCaseSensitive(); } /** * @return int */ public function getType() { return $this->value->getType(); } /** * {@inheritdoc} */ public function prepend($expr) { $this->value->prepend($expr); return $this; } /** * {@inheritdoc} */ public function append($expr) { $this->value->append($expr); return $this; } /** * @return bool */ public function isRegex() { return self::TYPE_REGEX === $this->value->getType(); } /** * @return bool */ public function isGlob() { return self::TYPE_GLOB === $this->value->getType(); } /** * @throws \LogicException * * @return Glob */ public function getGlob() { if (self::TYPE_GLOB !== $this->value->getType()) { throw new \LogicException('Regex can\'t be transformed to glob.'); } return $this->value; } /** * @return Regex */ public function getRegex() { return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex(); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Expression; use Symfony\Component\Finder\Glob as FinderGlob; /** * @author Jean-François Simon <contact@jfsimon.fr> */ class Glob implements ValueInterface { /** * @var string */ private $pattern; /** * @param string $pattern */ public function __construct($pattern) { $this->pattern = $pattern; } /** * {@inheritdoc} */ public function render() { return $this->pattern; } /** * {@inheritdoc} */ public function renderPattern() { return $this->pattern; } /** * {@inheritdoc} */ public function getType() { return Expression::TYPE_GLOB; } /** * {@inheritdoc} */ public function isCaseSensitive() { return true; } /** * {@inheritdoc} */ public function prepend($expr) { $this->pattern = $expr.$this->pattern; return $this; } /** * {@inheritdoc} */ public function append($expr) { $this->pattern .= $expr; return $this; } /** * Tests if glob is expandable ("*.{a,b}" syntax). * * @return bool */ public function isExpandable() { return false !== strpos($this->pattern, '{') && false !== strpos($this->pattern, '}'); } /** * @param bool $strictLeadingDot * @param bool $strictWildcardSlash * * @return Regex */ public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true) { $regex = FinderGlob::toRegex($this->pattern, $strictLeadingDot, $strictWildcardSlash, ''); return new Regex($regex); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Expression; /** * @author Jean-François Simon <contact@jfsimon.fr> */ class Regex implements ValueInterface { const START_FLAG = '^'; const END_FLAG = '$'; const BOUNDARY = '~'; const JOKER = '.*'; const ESCAPING = '\\'; /** * @var string */ private $pattern; /** * @var array */ private $options; /** * @var bool */ private $startFlag; /** * @var bool */ private $endFlag; /** * @var bool */ private $startJoker; /** * @var bool */ private $endJoker; /** * @param string $expr * * @return Regex * * @throws \InvalidArgumentException */ public static function create($expr) { if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) { $start = substr($m[1], 0, 1); $end = substr($m[1], -1); if ( ($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start)) || ($start === '{' && $end === '}') || ($start === '(' && $end === ')') ) { return new self(substr($m[1], 1, -1), $m[2], $end); } } throw new \InvalidArgumentException('Given expression is not a regex.'); } /** * @param string $pattern * @param string $options * @param string $delimiter */ public function __construct($pattern, $options = '', $delimiter = null) { if (null !== $delimiter) { // removes delimiter escaping $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern); } $this->parsePattern($pattern); $this->options = $options; } /** * @return string */ public function __toString() { return $this->render(); } /** * {@inheritdoc} */ public function render() { return self::BOUNDARY .$this->renderPattern() .self::BOUNDARY .$this->options; } /** * {@inheritdoc} */ public function renderPattern() { return ($this->startFlag ? self::START_FLAG : '') .($this->startJoker ? self::JOKER : '') .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern) .($this->endJoker ? self::JOKER : '') .($this->endFlag ? self::END_FLAG : ''); } /** * {@inheritdoc} */ public function isCaseSensitive() { return !$this->hasOption('i'); } /** * {@inheritdoc} */ public function getType() { return Expression::TYPE_REGEX; } /** * {@inheritdoc} */ public function prepend($expr) { $this->pattern = $expr.$this->pattern; return $this; } /** * {@inheritdoc} */ public function append($expr) { $this->pattern .= $expr; return $this; } /** * @param string $option * * @return bool */ public function hasOption($option) { return false !== strpos($this->options, $option); } /** * @param string $option * * @return Regex */ public function addOption($option) { if (!$this->hasOption($option)) { $this->options .= $option; } return $this; } /** * @param string $option * * @return Regex */ public function removeOption($option) { $this->options = str_replace($option, '', $this->options); return $this; } /** * @param bool $startFlag * * @return Regex */ public function setStartFlag($startFlag) { $this->startFlag = $startFlag; return $this; } /** * @return bool */ public function hasStartFlag() { return $this->startFlag; } /** * @param bool $endFlag * * @return Regex */ public function setEndFlag($endFlag) { $this->endFlag = (bool) $endFlag; return $this; } /** * @return bool */ public function hasEndFlag() { return $this->endFlag; } /** * @param bool $startJoker * * @return Regex */ public function setStartJoker($startJoker) { $this->startJoker = $startJoker; return $this; } /** * @return bool */ public function hasStartJoker() { return $this->startJoker; } /** * @param bool $endJoker * * @return Regex */ public function setEndJoker($endJoker) { $this->endJoker = (bool) $endJoker; return $this; } /** * @return bool */ public function hasEndJoker() { return $this->endJoker; } /** * @param array $replacement * * @return Regex */ public function replaceJokers($replacement) { $replace = function ($subject) use ($replacement) { $subject = $subject[0]; $replace = 0 === substr_count($subject, '\\') % 2; return $replace ? str_replace('.', $replacement, $subject) : $subject; }; $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern); return $this; } /** * @param string $pattern */ private function parsePattern($pattern) { if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) { $pattern = substr($pattern, 1); } if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) { $pattern = substr($pattern, 2); } if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) { $pattern = substr($pattern, 0, -1); } if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) { $pattern = substr($pattern, 0, -2); } $this->pattern = $pattern; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Expression; /** * @author Jean-François Simon <contact@jfsimon.fr> */ interface ValueInterface { /** * Renders string representation of expression. * * @return string */ public function render(); /** * Renders string representation of pattern. * * @return string */ public function renderPattern(); /** * Returns value case sensitivity. * * @return bool */ public function isCaseSensitive(); /** * Returns expression type. * * @return int */ public function getType(); /** * @param string $expr * * @return ValueInterface */ public function prepend($expr); /** * @param string $expr * * @return ValueInterface */ public function append($expr); } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder; /** * Glob matches globbing patterns against text. * * if match_glob("foo.*", "foo.bar") echo "matched\n"; * * // prints foo.bar and foo.baz * $regex = glob_to_regex("foo.*"); * for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t) * { * if (/$regex/) echo "matched: $car\n"; * } * * Glob implements glob(3) style matching that can be used to match * against text, rather than fetching names from a filesystem. * * Based on the Perl Text::Glob module. * * @author Fabien Potencier <fabien@symfony.com> PHP port * @author Richard Clamp <richardc@unixbeard.net> Perl version * @copyright 2004-2005 Fabien Potencier <fabien@symfony.com> * @copyright 2002 Richard Clamp <richardc@unixbeard.net> */ class Glob { /** * Returns a regexp which is the equivalent of the glob pattern. * * @param string $glob The glob pattern * @param bool $strictLeadingDot * @param bool $strictWildcardSlash * @param string $delimiter Optional delimiter * * @return string regex The regexp */ public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true, $delimiter = '#') { $firstByte = true; $escaping = false; $inCurlies = 0; $regex = ''; $sizeGlob = strlen($glob); for ($i = 0; $i < $sizeGlob; ++$i) { $car = $glob[$i]; if ($firstByte) { if ($strictLeadingDot && '.' !== $car) { $regex .= '(?=[^\.])'; } $firstByte = false; } if ('/' === $car) { $firstByte = true; } if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { $regex .= "\\$car"; } elseif ('*' === $car) { $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); } elseif ('?' === $car) { $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); } elseif ('{' === $car) { $regex .= $escaping ? '\\{' : '('; if (!$escaping) { ++$inCurlies; } } elseif ('}' === $car && $inCurlies) { $regex .= $escaping ? '}' : ')'; if (!$escaping) { --$inCurlies; } } elseif (',' === $car && $inCurlies) { $regex .= $escaping ? ',' : '|'; } elseif ('\\' === $car) { if ($escaping) { $regex .= '\\\\'; $escaping = false; } else { $escaping = true; } continue; } else { $regex .= $car; } $escaping = false; } return $delimiter.'^'.$regex.'$'.$delimiter; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\Comparator\DateComparator; /** * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates). * * @author Fabien Potencier <fabien@symfony.com> */ class DateRangeFilterIterator extends FilterIterator { private $comparators = array(); /** * Constructor. * * @param \Iterator $iterator The Iterator to filter * @param DateComparator[] $comparators An array of DateComparator instances */ public function __construct(\Iterator $iterator, array $comparators) { $this->comparators = $comparators; parent::__construct($iterator); } /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { $fileinfo = $this->current(); if (!file_exists($fileinfo->getRealPath())) { return false; } $filedate = $fileinfo->getMTime(); foreach ($this->comparators as $compare) { if (!$compare->test($filedate)) { return false; } } return true; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * DepthRangeFilterIterator limits the directory depth. * * @author Fabien Potencier <fabien@symfony.com> */ class DepthRangeFilterIterator extends FilterIterator { private $minDepth = 0; /** * Constructor. * * @param \RecursiveIteratorIterator $iterator The Iterator to filter * @param int $minDepth The min depth * @param int $maxDepth The max depth */ public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX) { $this->minDepth = $minDepth; $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth); parent::__construct($iterator); } /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { return $this->getInnerIterator()->getDepth() >= $this->minDepth; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\SplFileInfo; /** * Iterate over shell command result. * * @author Jean-François Simon <contact@jfsimon.fr> */ class FilePathsIterator extends \ArrayIterator { /** * @var string */ private $baseDir; /** * @var int */ private $baseDirLength; /** * @var string */ private $subPath; /** * @var string */ private $subPathname; /** * @var SplFileInfo */ private $current; /** * @param array $paths List of paths returned by shell command * @param string $baseDir Base dir for relative path building */ public function __construct(array $paths, $baseDir) { $this->baseDir = $baseDir; $this->baseDirLength = strlen($baseDir); parent::__construct($paths); } /** * @param string $name * @param array $arguments * * @return mixed */ public function __call($name, array $arguments) { return call_user_func_array(array($this->current(), $name), $arguments); } /** * Return an instance of SplFileInfo with support for relative paths. * * @return SplFileInfo File information */ public function current() { return $this->current; } /** * @return string */ public function key() { return $this->current->getPathname(); } public function next() { parent::next(); $this->buildProperties(); } public function rewind() { parent::rewind(); $this->buildProperties(); } /** * @return string */ public function getSubPath() { return $this->subPath; } /** * @return string */ public function getSubPathname() { return $this->subPathname; } private function buildProperties() { $absolutePath = parent::current(); if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) { $this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\'); $dir = dirname($this->subPathname); $this->subPath = '.' === $dir ? '' : $dir; } else { $this->subPath = $this->subPathname = ''; } $this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * FileTypeFilterIterator only keeps files, directories, or both. * * @author Fabien Potencier <fabien@symfony.com> */ class FileTypeFilterIterator extends FilterIterator { const ONLY_FILES = 1; const ONLY_DIRECTORIES = 2; private $mode; /** * Constructor. * * @param \Iterator $iterator The Iterator to filter * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) */ public function __construct(\Iterator $iterator, $mode) { $this->mode = $mode; parent::__construct($iterator); } /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { $fileinfo = $this->current(); if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) { return false; } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) { return false; } return true; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings). * * @author Fabien Potencier <fabien@symfony.com> * @author Włodzimierz Gajda <gajdaw@gajdaw.pl> */ class FilecontentFilterIterator extends MultiplePcreFilterIterator { /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { if (!$this->matchRegexps && !$this->noMatchRegexps) { return true; } $fileinfo = $this->current(); if ($fileinfo->isDir() || !$fileinfo->isReadable()) { return false; } $content = $fileinfo->getContents(); if (!$content) { return false; } // should at least not match one rule to exclude foreach ($this->noMatchRegexps as $regex) { if (preg_match($regex, $content)) { return false; } } // should at least match one rule $match = true; if ($this->matchRegexps) { $match = false; foreach ($this->matchRegexps as $regex) { if (preg_match($regex, $content)) { return true; } } } return $match; } /** * Converts string to regexp if necessary. * * @param string $str Pattern: string or regexp * * @return string regexp corresponding to a given string or regexp */ protected function toRegex($str) { return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\Expression\Expression; /** * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string). * * @author Fabien Potencier <fabien@symfony.com> */ class FilenameFilterIterator extends MultiplePcreFilterIterator { /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { $filename = $this->current()->getFilename(); // should at least not match one rule to exclude foreach ($this->noMatchRegexps as $regex) { if (preg_match($regex, $filename)) { return false; } } // should at least match one rule $match = true; if ($this->matchRegexps) { $match = false; foreach ($this->matchRegexps as $regex) { if (preg_match($regex, $filename)) { return true; } } } return $match; } /** * Converts glob to regexp. * * PCRE patterns are left unchanged. * Glob strings are transformed with Glob::toRegex(). * * @param string $str Pattern: glob or regexp * * @return string regexp corresponding to a given glob or regexp */ protected function toRegex($str) { return Expression::create($str)->getRegex()->render(); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * This iterator just overrides the rewind method in order to correct a PHP bug. * * @see https://bugs.php.net/bug.php?id=49104 * * @author Alex Bogomazov */ abstract class FilterIterator extends \FilterIterator { /** * This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after * rewind in some cases. * * @see FilterIterator::rewind() */ public function rewind() { $iterator = $this; while ($iterator instanceof \OuterIterator) { $innerIterator = $iterator->getInnerIterator(); if ($innerIterator instanceof RecursiveDirectoryIterator) { if ($innerIterator->isRewindable()) { $innerIterator->next(); $innerIterator->rewind(); } } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) { $iterator->getInnerIterator()->next(); $iterator->getInnerIterator()->rewind(); } $iterator = $iterator->getInnerIterator(); } parent::rewind(); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\Expression\Expression; /** * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings). * * @author Fabien Potencier <fabien@symfony.com> */ abstract class MultiplePcreFilterIterator extends FilterIterator { protected $matchRegexps = array(); protected $noMatchRegexps = array(); /** * Constructor. * * @param \Iterator $iterator The Iterator to filter * @param array $matchPatterns An array of patterns that need to match * @param array $noMatchPatterns An array of patterns that need to not match */ public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns) { foreach ($matchPatterns as $pattern) { $this->matchRegexps[] = $this->toRegex($pattern); } foreach ($noMatchPatterns as $pattern) { $this->noMatchRegexps[] = $this->toRegex($pattern); } parent::__construct($iterator); } /** * Checks whether the string is a regex. * * @param string $str * * @return bool Whether the given string is a regex */ protected function isRegex($str) { return Expression::create($str)->isRegex(); } /** * Converts string into regexp. * * @param string $str Pattern * * @return string regexp corresponding to a given string */ abstract protected function toRegex($str); } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\Comparator\NumberComparator; /** * SizeRangeFilterIterator filters out files that are not in the given size range. * * @author Fabien Potencier <fabien@symfony.com> */ class SizeRangeFilterIterator extends FilterIterator { private $comparators = array(); /** * Constructor. * * @param \Iterator $iterator The Iterator to filter * @param NumberComparator[] $comparators An array of NumberComparator instances */ public function __construct(\Iterator $iterator, array $comparators) { $this->comparators = $comparators; parent::__construct($iterator); } /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { $fileinfo = $this->current(); if (!$fileinfo->isFile()) { return true; } $filesize = $fileinfo->getSize(); foreach ($this->comparators as $compare) { if (!$compare->test($filesize)) { return false; } } return true; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * SortableIterator applies a sort on a given Iterator. * * @author Fabien Potencier <fabien@symfony.com> */ class SortableIterator implements \IteratorAggregate { const SORT_BY_NAME = 1; const SORT_BY_TYPE = 2; const SORT_BY_ACCESSED_TIME = 3; const SORT_BY_CHANGED_TIME = 4; const SORT_BY_MODIFIED_TIME = 5; private $iterator; private $sort; /** * Constructor. * * @param \Traversable $iterator The Iterator to filter * @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback) * * @throws \InvalidArgumentException */ public function __construct(\Traversable $iterator, $sort) { $this->iterator = $iterator; if (self::SORT_BY_NAME === $sort) { $this->sort = function ($a, $b) { return strcmp($a->getRealpath(), $b->getRealpath()); }; } elseif (self::SORT_BY_TYPE === $sort) { $this->sort = function ($a, $b) { if ($a->isDir() && $b->isFile()) { return -1; } elseif ($a->isFile() && $b->isDir()) { return 1; } return strcmp($a->getRealpath(), $b->getRealpath()); }; } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { $this->sort = function ($a, $b) { return ($a->getATime() - $b->getATime()); }; } elseif (self::SORT_BY_CHANGED_TIME === $sort) { $this->sort = function ($a, $b) { return ($a->getCTime() - $b->getCTime()); }; } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { $this->sort = function ($a, $b) { return ($a->getMTime() - $b->getMTime()); }; } elseif (is_callable($sort)) { $this->sort = $sort; } else { throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.'); } } public function getIterator() { $array = iterator_to_array($this->iterator, true); uasort($array, $this->sort); return new \ArrayIterator($array); } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * CustomFilterIterator filters files by applying anonymous functions. * * The anonymous function receives a \SplFileInfo and must return false * to remove files. * * @author Fabien Potencier <fabien@symfony.com> */ class CustomFilterIterator extends FilterIterator { private $filters = array(); /** * Constructor. * * @param \Iterator $iterator The Iterator to filter * @param callable[] $filters An array of PHP callbacks * * @throws \InvalidArgumentException */ public function __construct(\Iterator $iterator, array $filters) { foreach ($filters as $filter) { if (!is_callable($filter)) { throw new \InvalidArgumentException('Invalid PHP callback.'); } } $this->filters = $filters; parent::__construct($iterator); } /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { $fileinfo = $this->current(); foreach ($this->filters as $filter) { if (false === call_user_func($filter, $fileinfo)) { return false; } } return true; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * ExcludeDirectoryFilterIterator filters out directories. * * @author Fabien Potencier <fabien@symfony.com> */ class ExcludeDirectoryFilterIterator extends FilterIterator implements \RecursiveIterator { private $iterator; private $isRecursive; private $excludedDirs = array(); private $excludedPattern; /** * Constructor. * * @param \Iterator $iterator The Iterator to filter * @param array $directories An array of directories to exclude */ public function __construct(\Iterator $iterator, array $directories) { $this->iterator = $iterator; $this->isRecursive = $iterator instanceof \RecursiveIterator; $patterns = array(); foreach ($directories as $directory) { if (!$this->isRecursive || false !== strpos($directory, '/')) { $patterns[] = preg_quote($directory, '#'); } else { $this->excludedDirs[$directory] = true; } } if ($patterns) { $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#'; } parent::__construct($iterator); } /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) { return false; } if ($this->excludedPattern) { $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); $path = str_replace('\\', '/', $path); return !preg_match($this->excludedPattern, $path); } return true; } public function hasChildren() { return $this->isRecursive && $this->iterator->hasChildren(); } public function getChildren() { $children = new self($this->iterator->getChildren(), array()); $children->excludedDirs = $this->excludedDirs; $children->excludedPattern = $this->excludedPattern; return $children; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; /** * PathFilterIterator filters files by path patterns (e.g. some/special/dir). * * @author Fabien Potencier <fabien@symfony.com> * @author Włodzimierz Gajda <gajdaw@gajdaw.pl> */ class PathFilterIterator extends MultiplePcreFilterIterator { /** * Filters the iterator values. * * @return bool true if the value should be kept, false otherwise */ public function accept() { $filename = $this->current()->getRelativePathname(); if ('\\' === DIRECTORY_SEPARATOR) { $filename = str_replace('\\', '/', $filename); } // should at least not match one rule to exclude foreach ($this->noMatchRegexps as $regex) { if (preg_match($regex, $filename)) { return false; } } // should at least match one rule $match = true; if ($this->matchRegexps) { $match = false; foreach ($this->matchRegexps as $regex) { if (preg_match($regex, $filename)) { return true; } } } return $match; } /** * Converts strings to regexp. * * PCRE patterns are left unchanged. * * Default conversion: * 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/' * * Use only / as directory separator (on Windows also). * * @param string $str Pattern: regexp or dirname. * * @return string regexp corresponding to a given string or regexp */ protected function toRegex($str) { return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Iterator; use Symfony\Component\Finder\Exception\AccessDeniedException; use Symfony\Component\Finder\SplFileInfo; /** * Extends the \RecursiveDirectoryIterator to support relative paths. * * @author Victor Berchet <victor@suumit.com> */ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator { /** * @var bool */ private $ignoreUnreadableDirs; /** * @var bool */ private $rewindable; // these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations private $rootPath; private $subPath; private $directorySeparator = '/'; /** * Constructor. * * @param string $path * @param int $flags * @param bool $ignoreUnreadableDirs * * @throws \RuntimeException */ public function __construct($path, $flags, $ignoreUnreadableDirs = false) { if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) { throw new \RuntimeException('This iterator only support returning current as fileinfo.'); } parent::__construct($path, $flags); $this->ignoreUnreadableDirs = $ignoreUnreadableDirs; $this->rootPath = (string) $path; if ('/' !== DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) { $this->directorySeparator = DIRECTORY_SEPARATOR; } } /** * Return an instance of SplFileInfo with support for relative paths. * * @return SplFileInfo File information */ public function current() { // the logic here avoids redoing the same work in all iterations if (null === $subPathname = $this->subPath) { $subPathname = $this->subPath = (string) $this->getSubPath(); } if ('' !== $subPathname) { $subPathname .= $this->directorySeparator; } $subPathname .= $this->getFilename(); return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname); } /** * @return \RecursiveIterator * * @throws AccessDeniedException */ public function getChildren() { try { $children = parent::getChildren(); if ($children instanceof self) { // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs; // performance optimization to avoid redoing the same work in all children $children->rewindable = &$this->rewindable; $children->rootPath = $this->rootPath; } return $children; } catch (\UnexpectedValueException $e) { if ($this->ignoreUnreadableDirs) { // If directory is unreadable and finder is set to ignore it, a fake empty content is returned. return new \RecursiveArrayIterator(array()); } else { throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e); } } } /** * Do nothing for non rewindable stream. */ public function rewind() { if (false === $this->isRewindable()) { return; } // @see https://bugs.php.net/bug.php?id=49104 parent::next(); parent::rewind(); } /** * Checks if the stream is rewindable. * * @return bool true when the stream is rewindable, false otherwise */ public function isRewindable() { if (null !== $this->rewindable) { return $this->rewindable; } if (false !== $stream = @opendir($this->getPath())) { $infos = stream_get_meta_data($stream); closedir($stream); if ($infos['seekable']) { return $this->rewindable = true; } } return $this->rewindable = false; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Shell; /** * @author Jean-François Simon <contact@jfsimon.fr> */ class Shell { const TYPE_UNIX = 1; const TYPE_DARWIN = 2; const TYPE_CYGWIN = 3; const TYPE_WINDOWS = 4; const TYPE_BSD = 5; /** * @var string|null */ private $type; /** * Returns guessed OS type. * * @return int */ public function getType() { if (null === $this->type) { $this->type = $this->guessType(); } return $this->type; } /** * Tests if a command is available. * * @param string $command * * @return bool */ public function testCommand($command) { if (!function_exists('exec')) { return false; } // todo: find a better way (command could not be available) $testCommand = 'which '; if (self::TYPE_WINDOWS === $this->type) { $testCommand = 'where '; } $command = escapeshellcmd($command); exec($testCommand.$command, $output, $code); return 0 === $code && count($output) > 0; } /** * Guesses OS type. * * @return int */ private function guessType() { $os = strtolower(PHP_OS); if (false !== strpos($os, 'cygwin')) { return self::TYPE_CYGWIN; } if (false !== strpos($os, 'darwin')) { return self::TYPE_DARWIN; } if (false !== strpos($os, 'bsd')) { return self::TYPE_BSD; } if (0 === strpos($os, 'win')) { return self::TYPE_WINDOWS; } return self::TYPE_UNIX; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder\Shell; /** * @author Jean-François Simon <contact@jfsimon.fr> */ class Command { /** * @var Command|null */ private $parent; /** * @var array */ private $bits = array(); /** * @var array */ private $labels = array(); /** * @var \Closure|null */ private $errorHandler; /** * Constructor. * * @param Command|null $parent Parent command */ public function __construct(Command $parent = null) { $this->parent = $parent; } /** * Returns command as string. * * @return string */ public function __toString() { return $this->join(); } /** * Creates a new Command instance. * * @param Command|null $parent Parent command * * @return Command New Command instance */ public static function create(Command $parent = null) { return new self($parent); } /** * Escapes special chars from input. * * @param string $input A string to escape * * @return string The escaped string */ public static function escape($input) { return escapeshellcmd($input); } /** * Quotes input. * * @param string $input An argument string * * @return string The quoted string */ public static function quote($input) { return escapeshellarg($input); } /** * Appends a string or a Command instance. * * @param string|Command $bit * * @return Command The current Command instance */ public function add($bit) { $this->bits[] = $bit; return $this; } /** * Prepends a string or a command instance. * * @param string|Command $bit * * @return Command The current Command instance */ public function top($bit) { array_unshift($this->bits, $bit); foreach ($this->labels as $label => $index) { $this->labels[$label] += 1; } return $this; } /** * Appends an argument, will be quoted. * * @param string $arg * * @return Command The current Command instance */ public function arg($arg) { $this->bits[] = self::quote($arg); return $this; } /** * Appends escaped special command chars. * * @param string $esc * * @return Command The current Command instance */ public function cmd($esc) { $this->bits[] = self::escape($esc); return $this; } /** * Inserts a labeled command to feed later. * * @param string $label The unique label * * @return Command The current Command instance * * @throws \RuntimeException If label already exists */ public function ins($label) { if (isset($this->labels[$label])) { throw new \RuntimeException(sprintf('Label "%s" already exists.', $label)); } $this->bits[] = self::create($this); $this->labels[$label] = count($this->bits) - 1; return $this->bits[$this->labels[$label]]; } /** * Retrieves a previously labeled command. * * @param string $label * * @return Command The labeled command * * @throws \RuntimeException */ public function get($label) { if (!isset($this->labels[$label])) { throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label)); } return $this->bits[$this->labels[$label]]; } /** * Returns parent command (if any). * * @return Command Parent command * * @throws \RuntimeException If command has no parent */ public function end() { if (null === $this->parent) { throw new \RuntimeException('Calling end on root command doesn\'t make sense.'); } return $this->parent; } /** * Counts bits stored in command. * * @return int The bits count */ public function length() { return count($this->bits); } /** * @param \Closure $errorHandler * * @return Command */ public function setErrorHandler(\Closure $errorHandler) { $this->errorHandler = $errorHandler; return $this; } /** * @return \Closure|null */ public function getErrorHandler() { return $this->errorHandler; } /** * Executes current command. * * @return array The command result * * @throws \RuntimeException */ public function execute() { if (null === $errorHandler = $this->errorHandler) { exec($this->join(), $output); } else { $process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes); $output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY); if ($error = stream_get_contents($pipes[2])) { $errorHandler($error); } proc_close($process); } return $output ?: array(); } /** * Joins bits. * * @return string */ public function join() { return implode(' ', array_filter( array_map(function ($bit) { return $bit instanceof Command ? $bit->join() : ($bit ?: null); }, $this->bits), function ($bit) { return null !== $bit; } )); } /** * Insert a string or a Command instance before the bit at given position $index (index starts from 0). * * @param string|Command $bit * @param int $index * * @return Command The current Command instance */ public function addAtIndex($bit, $index) { array_splice($this->bits, $index, 0, $bit instanceof self ? array($bit) : $bit); return $this; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder; /** * Extends \SplFileInfo to support relative paths. * * @author Fabien Potencier <fabien@symfony.com> */ class SplFileInfo extends \SplFileInfo { private $relativePath; private $relativePathname; /** * Constructor. * * @param string $file The file name * @param string $relativePath The relative path * @param string $relativePathname The relative path name */ public function __construct($file, $relativePath, $relativePathname) { parent::__construct($file); $this->relativePath = $relativePath; $this->relativePathname = $relativePathname; } /** * Returns the relative path. * * @return string the relative path */ public function getRelativePath() { return $this->relativePath; } /** * Returns the relative path name. * * @return string the relative path name */ public function getRelativePathname() { return $this->relativePathname; } /** * Returns the contents of the file. * * @return string the contents of the file * * @throws \RuntimeException */ public function getContents() { $level = error_reporting(0); $content = file_get_contents($this->getPathname()); error_reporting($level); if (false === $content) { $error = error_get_last(); throw new \RuntimeException($error['message']); } return $content; } } <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Finder; use Symfony\Component\Finder\Adapter\AdapterInterface; use Symfony\Component\Finder\Adapter\GnuFindAdapter; use Symfony\Component\Finder\Adapter\BsdFindAdapter; use Symfony\Component\Finder\Adapter\PhpAdapter; use Symfony\Component\Finder\Comparator\DateComparator; use Symfony\Component\Finder\Comparator\NumberComparator; use Symfony\Component\Finder\Exception\ExceptionInterface; use Symfony\Component\Finder\Iterator\CustomFilterIterator; use Symfony\Component\Finder\Iterator\DateRangeFilterIterator; use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator; use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator; use Symfony\Component\Finder\Iterator\FilecontentFilterIterator; use Symfony\Component\Finder\Iterator\FilenameFilterIterator; use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator; use Symfony\Component\Finder\Iterator\SortableIterator; /** * Finder allows to build rules to find files and directories. * * It is a thin wrapper around several specialized iterator classes. * * All rules may be invoked several times. * * All methods return the current Finder object to allow easy chaining: * * $finder = Finder::create()->files()->name('*.php')->in(__DIR__); * * @author Fabien Potencier <fabien@symfony.com> */ class Finder implements \IteratorAggregate, \Countable { const IGNORE_VCS_FILES = 1; const IGNORE_DOT_FILES = 2; private $mode = 0; private $names = array(); private $notNames = array(); private $exclude = array(); private $filters = array(); private $depths = array(); private $sizes = array(); private $followLinks = false; private $sort = false; private $ignore = 0; private $dirs = array(); private $dates = array(); private $iterators = array(); private $contains = array(); private $notContains = array(); private $adapters = array(); private $paths = array(); private $notPaths = array(); private $ignoreUnreadableDirs = false; private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'); /** * Constructor. */ public function __construct() { $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES; $this ->addAdapter(new GnuFindAdapter()) ->addAdapter(new BsdFindAdapter()) ->addAdapter(new PhpAdapter(), -50) ->setAdapter('php') ; } /** * Creates a new Finder. * * @return Finder A new Finder instance */ public static function create() { return new static(); } /** * Registers a finder engine implementation. * * @param AdapterInterface $adapter An adapter instance * @param int $priority Highest is selected first * * @return Finder The current Finder instance */ public function addAdapter(AdapterInterface $adapter, $priority = 0) { $this->adapters[$adapter->getName()] = array( 'adapter' => $adapter, 'priority' => $priority, 'selected' => false, ); return $this->sortAdapters(); } /** * Sets the selected adapter to the best one according to the current platform the code is run on. * * @return Finder The current Finder instance */ public function useBestAdapter() { $this->resetAdapterSelection(); return $this->sortAdapters(); } /** * Selects the adapter to use. * * @param string $name * * @throws \InvalidArgumentException * * @return Finder The current Finder instance */ public function setAdapter($name) { if (!isset($this->adapters[$name])) { throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name)); } $this->resetAdapterSelection(); $this->adapters[$name]['selected'] = true; return $this->sortAdapters(); } /** * Removes all adapters registered in the finder. * * @return Finder The current Finder instance */ public function removeAdapters() { $this->adapters = array(); return $this; } /** * Returns registered adapters ordered by priority without extra information. * * @return AdapterInterface[] */ public function getAdapters() { return array_values(array_map(function (array $adapter) { return $adapter['adapter']; }, $this->adapters)); } /** * Restricts the matching to directories only. * * @return Finder The current Finder instance */ public function directories() { $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES; return $this; } /** * Restricts the matching to files only. * * @return Finder The current Finder instance */ public function files() { $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES; return $this; } /** * Adds tests for the directory depth. * * Usage: * * $finder->depth('> 1') // the Finder will start matching at level 1. * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point. * * @param int $level The depth level expression * * @return Finder The current Finder instance * * @see DepthRangeFilterIterator * @see NumberComparator */ public function depth($level) { $this->depths[] = new Comparator\NumberComparator($level); return $this; } /** * Adds tests for file dates (last modified). * * The date must be something that strtotime() is able to parse: * * $finder->date('since yesterday'); * $finder->date('until 2 days ago'); * $finder->date('> now - 2 hours'); * $finder->date('>= 2005-10-15'); * * @param string $date A date range string * * @return Finder The current Finder instance * * @see strtotime * @see DateRangeFilterIterator * @see DateComparator */ public function date($date) { $this->dates[] = new Comparator\DateComparator($date); return $this; } /** * Adds rules that files must match. * * You can use patterns (delimited with / sign), globs or simple strings. * * $finder->name('*.php') * $finder->name('/\.php$/') // same as above * $finder->name('test.php') * * @param string $pattern A pattern (a regexp, a glob, or a string) * * @return Finder The current Finder instance * * @see FilenameFilterIterator */ public function name($pattern) { $this->names[] = $pattern; return $this; } /** * Adds rules that files must not match. * * @param string $pattern A pattern (a regexp, a glob, or a string) * * @return Finder The current Finder instance * * @see FilenameFilterIterator */ public function notName($pattern) { $this->notNames[] = $pattern; return $this; } /** * Adds tests that file contents must match. * * Strings or PCRE patterns can be used: * * $finder->contains('Lorem ipsum') * $finder->contains('/Lorem ipsum/i') * * @param string $pattern A pattern (string or regexp) * * @return Finder The current Finder instance * * @see FilecontentFilterIterator */ public function contains($pattern) { $this->contains[] = $pattern; return $this; } /** * Adds tests that file contents must not match. * * Strings or PCRE patterns can be used: * * $finder->notContains('Lorem ipsum') * $finder->notContains('/Lorem ipsum/i') * * @param string $pattern A pattern (string or regexp) * * @return Finder The current Finder instance * * @see FilecontentFilterIterator */ public function notContains($pattern) { $this->notContains[] = $pattern; return $this; } /** * Adds rules that filenames must match. * * You can use patterns (delimited with / sign) or simple strings. * * $finder->path('some/special/dir') * $finder->path('/some\/special\/dir/') // same as above * * Use only / as dirname separator. * * @param string $pattern A pattern (a regexp or a string) * * @return Finder The current Finder instance * * @see FilenameFilterIterator */ public function path($pattern) { $this->paths[] = $pattern; return $this; } /** * Adds rules that filenames must not match. * * You can use patterns (delimited with / sign) or simple strings. * * $finder->notPath('some/special/dir') * $finder->notPath('/some\/special\/dir/') // same as above * * Use only / as dirname separator. * * @param string $pattern A pattern (a regexp or a string) * * @return Finder The current Finder instance * * @see FilenameFilterIterator */ public function notPath($pattern) { $this->notPaths[] = $pattern; return $this; } /** * Adds tests for file sizes. * * $finder->size('> 10K'); * $finder->size('<= 1Ki'); * $finder->size(4); * * @param string $size A size range string * * @return Finder The current Finder instance * * @see SizeRangeFilterIterator * @see NumberComparator */ public function size($size) { $this->sizes[] = new Comparator\NumberComparator($size); return $this; } /** * Excludes directories. * * @param string|array $dirs A directory path or an array of directories * * @return Finder The current Finder instance * * @see ExcludeDirectoryFilterIterator */ public function exclude($dirs) { $this->exclude = array_merge($this->exclude, (array) $dirs); return $this; } /** * Excludes "hidden" directories and files (starting with a dot). * * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not * * @return Finder The current Finder instance * * @see ExcludeDirectoryFilterIterator */ public function ignoreDotFiles($ignoreDotFiles) { if ($ignoreDotFiles) { $this->ignore |= static::IGNORE_DOT_FILES; } else { $this->ignore &= ~static::IGNORE_DOT_FILES; } return $this; } /** * Forces the finder to ignore version control directories. * * @param bool $ignoreVCS Whether to exclude VCS files or not * * @return Finder The current Finder instance * * @see ExcludeDirectoryFilterIterator */ public function ignoreVCS($ignoreVCS) { if ($ignoreVCS) { $this->ignore |= static::IGNORE_VCS_FILES; } else { $this->ignore &= ~static::IGNORE_VCS_FILES; } return $this; } /** * Adds VCS patterns. * * @see ignoreVCS() * * @param string|string[] $pattern VCS patterns to ignore */ public static function addVCSPattern($pattern) { foreach ((array) $pattern as $p) { self::$vcsPatterns[] = $p; } self::$vcsPatterns = array_unique(self::$vcsPatterns); } /** * Sorts files and directories by an anonymous function. * * The anonymous function receives two \SplFileInfo instances to compare. * * This can be slow as all the matching files and directories must be retrieved for comparison. * * @param \Closure $closure An anonymous function * * @return Finder The current Finder instance * * @see SortableIterator */ public function sort(\Closure $closure) { $this->sort = $closure; return $this; } /** * Sorts files and directories by name. * * This can be slow as all the matching files and directories must be retrieved for comparison. * * @return Finder The current Finder instance * * @see SortableIterator */ public function sortByName() { $this->sort = Iterator\SortableIterator::SORT_BY_NAME; return $this; } /** * Sorts files and directories by type (directories before files), then by name. * * This can be slow as all the matching files and directories must be retrieved for comparison. * * @return Finder The current Finder instance * * @see SortableIterator */ public function sortByType() { $this->sort = Iterator\SortableIterator::SORT_BY_TYPE; return $this; } /** * Sorts files and directories by the last accessed time. * * This is the time that the file was last accessed, read or written to. * * This can be slow as all the matching files and directories must be retrieved for comparison. * * @return Finder The current Finder instance * * @see SortableIterator */ public function sortByAccessedTime() { $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME; return $this; } /** * Sorts files and directories by the last inode changed time. * * This is the time that the inode information was last modified (permissions, owner, group or other metadata). * * On Windows, since inode is not available, changed time is actually the file creation time. * * This can be slow as all the matching files and directories must be retrieved for comparison. * * @return Finder The current Finder instance * * @see SortableIterator */ public function sortByChangedTime() { $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME; return $this; } /** * Sorts files and directories by the last modified time. * * This is the last time the actual contents of the file were last modified. * * This can be slow as all the matching files and directories must be retrieved for comparison. * * @return Finder The current Finder instance * * @see SortableIterator */ public function sortByModifiedTime() { $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME; return $this; } /** * Filters the iterator with an anonymous function. * * The anonymous function receives a \SplFileInfo and must return false * to remove files. * * @param \Closure $closure An anonymous function * * @return Finder The current Finder instance * * @see CustomFilterIterator */ public function filter(\Closure $closure) { $this->filters[] = $closure; return $this; } /** * Forces the following of symlinks. * * @return Finder The current Finder instance */ public function followLinks() { $this->followLinks = true; return $this; } /** * Tells finder to ignore unreadable directories. * * By default, scanning unreadable directories content throws an AccessDeniedException. * * @param bool $ignore * * @return Finder The current Finder instance */ public function ignoreUnreadableDirs($ignore = true) { $this->ignoreUnreadableDirs = (bool) $ignore; return $this; } /** * Searches files and directories which match defined rules. * * @param string|array $dirs A directory path or an array of directories * * @return Finder The current Finder instance * * @throws \InvalidArgumentException if one of the directories does not exist */ public function in($dirs) { $resolvedDirs = array(); foreach ((array) $dirs as $dir) { if (is_dir($dir)) { $resolvedDirs[] = $dir; } elseif ($glob = glob($dir, (defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) { $resolvedDirs = array_merge($resolvedDirs, $glob); } else { throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir)); } } $this->dirs = array_merge($this->dirs, $resolvedDirs); return $this; } /** * Returns an Iterator for the current Finder configuration. * * This method implements the IteratorAggregate interface. * * @return \Iterator An iterator * * @throws \LogicException if the in() method has not been called */ public function getIterator() { if (0 === count($this->dirs) && 0 === count($this->iterators)) { throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.'); } if (1 === count($this->dirs) && 0 === count($this->iterators)) { return $this->searchInDirectory($this->dirs[0]); } $iterator = new \AppendIterator(); foreach ($this->dirs as $dir) { $iterator->append($this->searchInDirectory($dir)); } foreach ($this->iterators as $it) { $iterator->append($it); } return $iterator; } /** * Appends an existing set of files/directories to the finder. * * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array. * * @param mixed $iterator * * @return Finder The finder * * @throws \InvalidArgumentException When the given argument is not iterable. */ public function append($iterator) { if ($iterator instanceof \IteratorAggregate) { $this->iterators[] = $iterator->getIterator(); } elseif ($iterator instanceof \Iterator) { $this->iterators[] = $iterator; } elseif ($iterator instanceof \Traversable || is_array($iterator)) { $it = new \ArrayIterator(); foreach ($iterator as $file) { $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file)); } $this->iterators[] = $it; } else { throw new \InvalidArgumentException('Finder::append() method wrong argument type.'); } return $this; } /** * Counts all the results collected by the iterators. * * @return int */ public function count() { return iterator_count($this->getIterator()); } /** * @return Finder The current Finder instance */ private function sortAdapters() { uasort($this->adapters, function (array $a, array $b) { if ($a['selected'] || $b['selected']) { return $a['selected'] ? -1 : 1; } return $a['priority'] > $b['priority'] ? -1 : 1; }); return $this; } /** * @param $dir * * @return \Iterator * * @throws \RuntimeException When none of the adapters are supported */ private function searchInDirectory($dir) { if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) { $this->exclude = array_merge($this->exclude, self::$vcsPatterns); } if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) { $this->notPaths[] = '#(^|/)\..+(/|$)#'; } foreach ($this->adapters as $adapter) { if ($adapter['adapter']->isSupported()) { try { return $this ->buildAdapter($adapter['adapter']) ->searchInDirectory($dir); } catch (ExceptionInterface $e) { } } } throw new \RuntimeException('No supported adapter found.'); } /** * @param AdapterInterface $adapter * * @return AdapterInterface */ private function buildAdapter(AdapterInterface $adapter) { return $adapter ->setFollowLinks($this->followLinks) ->setDepths($this->depths) ->setMode($this->mode) ->setExclude($this->exclude) ->setNames($this->names) ->setNotNames($this->notNames) ->setContains($this->contains) ->setNotContains($this->notContains) ->setSizes($this->sizes) ->setDates($this->dates) ->setFilters($this->filters) ->setSort($this->sort) ->setPath($this->paths) ->setNotPath($this->notPaths) ->ignoreUnreadableDirs($this->ignoreUnreadableDirs); } /** * Unselects all adapters. */ private function resetAdapterSelection() { $this->adapters = array_map(function (array $properties) { $properties['selected'] = false; return $properties; }, $this->adapters); } } <?php class Oxymel { private $xml; private $dom; private $current_element; private $last_inserted; private $go_deep_on_next_element = 0; private $go_up_on_next_element = 0; private $nesting_level = 0; private $contains_nesting_level = 0; private $indentation= ' '; public function __construct() { $this->xml = ''; $this->init_new_dom(); } public function to_string() { return $this->xml .= $this->indent( $this->xml_from_dom(), $this->nesting_level ); } public function __call( $name, $args ) { array_unshift( $args, $name ); return call_user_func_array( array( $this, 'tag' ), $args ); } public function __get( $name ) { return $this->$name(); } public function contains() { $this->contains_nesting_level++; $this->nesting_level++; if ( $this->go_deep_on_next_element ) { throw new OxymelException( 'contains cannot be used consecutively more than once' ); } $this->go_deep_on_next_element++; return $this; } public function end() { $this->contains_nesting_level--; $this->nesting_level--; if ( $this->contains_nesting_level < 0 ) { throw new OxymelException( 'end is used without a matching contains' ); } $this->go_up_on_next_element++; return $this; } public function tag( $name, $content_or_attributes = null, $attributes = array() ) { list( $content, $attributes ) = $this->get_content_and_attributes_from_tag_args( $content_or_attributes, $attributes ); $is_opening = 0 === strpos( $name, 'open_' ); $is_closing = 0 === strpos( $name, 'close_' ); $name = preg_replace("/^(open|close)_/", '', $name ); $element = $this->create_element( $name, $content, $attributes ); if ( !$is_opening && !$is_closing ) $this->add_element_to_dom( $element ); elseif ( $is_opening ) $this->add_opening_tag_from_element( $element ); elseif ( $is_closing ) $this->add_closing_tag_from_tag_name( $name ); return $this; } public function cdata( $text ) { $this->add_element_to_dom( $this->dom->createCDATASection( $text ) ); return $this; } public function text( $text ) { $this->add_element_to_dom( $this->dom->createTextNode( $text ) ); return $this; } public function comment( $text ) { $this->add_element_to_dom( $this->dom->createComment( $text ) ); return $this; } public function xml() { $this->add_element_to_dom( $this->dom->createProcessingInstruction( 'xml', 'version="1.0" encoding="UTF-8"' ) ); return $this; } public function oxymel( Oxymel $other ) { foreach( $other->dom->childNodes as $child ) { $child = $this->dom->importNode( $child, true ); $this->add_element_to_dom( $child ); } return $this; } public function raw( $raw_xml ) { if ( !$raw_xml ) { return $this; } $fragment = $this->dom->createDocumentFragment(); $fragment->appendXML($raw_xml); $this->add_element_to_dom( $fragment ); return $this; } private function add_element_to_dom( $element ) { $this->move_current_element_deep(); $this->move_current_element_up(); $this->last_inserted = $this->current_element->appendChild($element); } private function move_current_element_deep() { if ( $this->go_deep_on_next_element ) { if ( !$this->last_inserted ) { throw new OxymelException( 'contains has been used before adding any tags' ); } $this->current_element = $this->last_inserted; $this->go_deep_on_next_element--; } } private function move_current_element_up() { if ( $this->go_up_on_next_element ) { while ( $this->go_up_on_next_element ) { $this->current_element = $this->current_element->parentNode; $this->go_up_on_next_element--; } } } private function get_content_and_attributes_from_tag_args( $content_or_attributes, array $attributes ) { $content = null; if ( !$attributes ) { if ( is_array( $content_or_attributes ) ) $attributes = $content_or_attributes; else $content = $content_or_attributes; } else { $content = $content_or_attributes; } return array( $content, $attributes ); } private function init_new_dom() { unset( $this->dom, $this->current_element ); $this->dom = new DOMDocument(); $this->dom->formatOutput = true; $this->current_element = $this->dom; $this->last_inserted = null; } private function xml_from_dom() { if ( 0 !== $this->contains_nesting_level ) { throw new OxymelException( 'contains and end calls do not match' ); } $xml = ''; foreach( $this->dom->childNodes as $child ) { $xml .= $this->dom->saveXML( $child ) . "\n"; } return $xml; } private function create_element( $name, $content, $attributes ) { if ( !is_null( $content ) ) $element = $this->dom->createElement( $name, $content ); else $element = $this->dom->createElement( $name ); foreach( $attributes as $attribute_name => $attribute_value ) { $element->setAttribute( $attribute_name, $attribute_value ); } return $element; } private function add_opening_tag_from_element( $element ) { $this->xml .= $this->indent( $this->xml_from_dom(), $this->nesting_level ); $tag = $this->dom->saveXML($element); $this->xml .= $this->indent( str_replace( '/>', '>', $tag ) . "\n", $this->nesting_level ); $this->nesting_level++; $this->init_new_dom(); } private function add_closing_tag_from_tag_name( $name ) { $this->xml .= $this->xml_from_dom(); $this->nesting_level--; if ( $this->nesting_level < 0 ) { $this->xml = $this->indent( $this->xml, -$this->nesting_level ); $this->nesting_level = 0; } $this->xml .= $this->indent( "</$name>\n", $this->nesting_level ); $this->init_new_dom(); } private function indent( $string, $level ) { if ( !$level ) { return $string; } $lines = explode( "\n", $string ); foreach( $lines as &$line ) { if ( !trim( $line ) ) continue; $line = str_repeat( $this->indentation, $level ) . $line; } return implode( "\n", $lines ); } } class OxymelException extends Exception { } <?php require_once dirname( __FILE__ ) . '/Oxymel.php'; class OxymelTest extends PHPUnit_Framework_TestCase { function __construct() { $this->x = new Oxymel; } function test_xml_adds_procesing_instrution_with_version_and_encoding() { $this->a( '<?xml version="1.0" encoding="UTF-8"?>', $this->x->xml ); } function test_self_closing() { $this->a('<baba/>', $this->x->baba); } function test_self_closing_method() { $this->a('<baba/>', $this->x->baba()); } function test_attribute_no_content() { $this->a('<baba a="b" c="d"/>', $this->x->baba( array( 'a' => 'b', 'c' => 'd' ) ) ); } function test_content_no_attribute() { $this->a('<baba>content</baba>', $this->x->baba( 'content' ) ); } function test_escapiing_of_content() { $this->a('<baba><</baba>', $this->x->baba( '<' ) ); } function test_escaping_of_attributes() { $this->a('<baba a="<"/>', $this->x->baba( array( 'a' => '<' ) ) ); } function test_content_and_attributes() { $this->a('<baba a="b" c="d">content</baba>', $this->x->baba( 'content', array( 'a' => 'b', 'c' => 'd' ) ) ); } function test_content_and_attributes_error() { $this->setExpectedException( 'PHPUnit_Framework_Error' ); $this->a('<baba a="b" c="d">content</baba>', $this->x->baba( array( 'a' => 'b', 'c' => 'd' ), 'content' ) ); } function test_go_down() { $this->a('<baba> <dyado/> </baba>', $this->x->baba->contains->dyado->end ); } function test_go_down_and_up() { $this->a("<level0> <level1/> </level0> <level0/>", $this->x->level0->contains->level1->end->level0 ); } function test_cdata() { $this->a('<baba><![CDATA[content]]></baba>', $this->x->baba->contains->cdata('content')->end); } function test_nested_cdata() { $this->a( '<![CDATA[x]]]]><![CDATA[>]]>', $this->x->cdata( 'x]]>' ) ); } function test_raw() { $this->a('<baba> <dyado/> </baba>', $this->x->baba->contains->raw('<dyado></dyado>')->end); } function test_raw_doesnt_do_anything_with_empty_arg() { $this->a('<baba/>', $this->x->baba->raw( '' ) ); } function test_oxymel_ns() { $oxymel = new Oxymel; $oxymel->tag( 'wp:baba' ); $this->a( '<wp:baba/>', $this->x->oxymel( $oxymel ) ); } function x_test_only_up_error() { $this->a('', $this->x->end ); } function test_open_in_the_end() { $this->a("<baba/> <newtag>", $this->x->baba->open_newtag ); } function test_open_with_attributes() { $this->a("<baba/> <newtag a=\"b\">", $this->x->baba->open_newtag( array( 'a' => 'b' ) ) ); } function test_dom_open_dom() { $this->a("<baba/> <newtag> <baba/>", $this->x->baba->open_newtag->baba ); } function test_close_in_the_beginning() { $this->a("</oldtag> <baba/>", $this->x->close_oldtag->baba ); } function test_dom_close_dom() { $this->a(" <baba/> </oldtag> <baba/>", $this->x->baba->close_oldtag->baba ); } function test_baba() { $this->a("<a>baba<x/></a>", $this->x->a('baba')->contains->x->end ); } function test_comment() { $this->a('<!--baba-->', $this->x->comment('baba')); } function test_two_invocations() { $this->x->baba(); $this->x->dyado(); $this->a( "<baba/> <dyado/>", $this->x ); } function test_double_end() { $this->a( "<out> <mid> <in/> </mid> </out> <nextout/>", $this->x->out->contains->mid->contains->in->end->end->nextout ); } function test_end_without_contains() { $this->setExpectedException( 'OxymelException' ); $this->x->end->baba; } function test_leading_contains() { $this->setExpectedException( 'OxymelException' ); $this->a( '<baba/>', $this->x->contains->baba ); } function test_consecutive_contains_should_error() { $this->setExpectedException( 'OxymelException' ); $this->x->baba->contains->contains->dyado->end->wink; } function test_end_without_mathcing_contains_but_with_enough_parents() { $this->setExpectedException( 'OxymelException' ); $this->x->contains->baba->end->end->baba; } function test_contains_after_newly_initialized_dom_should_error() { $this->setExpectedException( 'OxymelException' ); $this->x->baba->open_rss->contains->baba->end; } function test_nested_open_tags_should_be_indented() { $this->a( "<baba> <baba>", $this->x->open_baba->open_baba ); } function test_normal_xml_between_two_opening_tags_should_be_indented() { $this->a( "<baba> <baba/> <baba>", $this->x->open_baba->baba->open_baba ); } function test_normal_xml_after_opening_tag_should_be_indented() { $this->a( "<baba> <baba/>", $this->x->open_baba->baba ); } function test_normal_xml_before_closing_tag_should_be_indented() { $this->a( " <baba/> </baba>", $this->x->baba->close_baba ); } function test_text_should_escape_the_content_inside() { $this->a( 'a & b', $this->x->text( 'a & b' ) ); } function test_dangling_contains_should_be_caught_in_the_end() { $this->setExpectedException( 'OxymelException' ); $this->x->contains->to_string(); } private function a($value, $x) { $this->assertEquals( $value . "\n", $x->to_string()); } } <?php /** * This file is part of the array_column library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Ben Ramsey (http://benramsey.com) * @license http://opensource.org/licenses/MIT MIT */ if (!function_exists('array_column')) { /** * Returns the values from a single column of the input array, identified by * the $columnKey. * * Optionally, you may provide an $indexKey to index the values in the returned * array by the values from the $indexKey column in the input array. * * @param array $input A multi-dimensional array (record set) from which to pull * a column of values. * @param mixed $columnKey The column of values to return. This value may be the * integer key of the column you wish to retrieve, or it * may be the string key name for an associative array. * @param mixed $indexKey (Optional.) The column to use as the index/keys for * the returned array. This value may be the integer key * of the column, or it may be the string key name. * @return array */ function array_column($input = null, $columnKey = null, $indexKey = null) { // Using func_get_args() in order to check for proper number of // parameters and trigger errors exactly as the built-in array_column() // does in PHP 5.5. $argc = func_num_args(); $params = func_get_args(); if ($argc < 2) { trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING); return null; } if (!is_array($params[0])) { trigger_error( 'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given', E_USER_WARNING ); return null; } if (!is_int($params[1]) && !is_float($params[1]) && !is_string($params[1]) && $params[1] !== null && !(is_object($params[1]) && method_exists($params[1], '__toString')) ) { trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING); return false; } if (isset($params[2]) && !is_int($params[2]) && !is_float($params[2]) && !is_string($params[2]) && !(is_object($params[2]) && method_exists($params[2], '__toString')) ) { trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING); return false; } $paramsInput = $params[0]; $paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null; $paramsIndexKey = null; if (isset($params[2])) { if (is_float($params[2]) || is_int($params[2])) { $paramsIndexKey = (int) $params[2]; } else { $paramsIndexKey = (string) $params[2]; } } $resultArray = array(); foreach ($paramsInput as $row) { $key = $value = null; $keySet = $valueSet = false; if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) { $keySet = true; $key = (string) $row[$paramsIndexKey]; } if ($paramsColumnKey === null) { $valueSet = true; $value = $row; } elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) { $valueSet = true; $value = $row[$paramsColumnKey]; } if ($valueSet) { if ($keySet) { $resultArray[$key] = $value; } else { $resultArray[] = $value; } } } return $resultArray; } } <?php /** * Spyc -- A Simple PHP YAML Class * @version 0.5.1 * @author Vlad Andersen <vlad.andersen@gmail.com> * @author Chris Wanstrath <chris@ozmm.org> * @link http://code.google.com/p/spyc/ * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen * @license http://www.opensource.org/licenses/mit-license.php MIT License * @package Spyc */ if (!function_exists('spyc_load')) { /** * Parses YAML to array. * @param string $string YAML string. * @return array */ function spyc_load ($string) { return Spyc::YAMLLoadString($string); } } if (!function_exists('spyc_load_file')) { /** * Parses YAML to array. * @param string $file Path to YAML file. * @return array */ function spyc_load_file ($file) { return Spyc::YAMLLoad($file); } } /** * The Simple PHP YAML Class. * * This class can be used to read a YAML file and convert its contents * into a PHP array. It currently supports a very limited subsection of * the YAML spec. * * Usage: * <code> * $Spyc = new Spyc; * $array = $Spyc->load($file); * </code> * or: * <code> * $array = Spyc::YAMLLoad($file); * </code> * or: * <code> * $array = spyc_load_file($file); * </code> * @package Spyc */ class Spyc { // SETTINGS const REMPTY = "\0\0\0\0\0"; /** * Setting this to true will force YAMLDump to enclose any string value in * quotes. False by default. * * @var bool */ public $setting_dump_force_quotes = false; /** * Setting this to true will forse YAMLLoad to use syck_load function when * possible. False by default. * @var bool */ public $setting_use_syck_is_possible = false; /**#@+ * @access private * @var mixed */ private $_dumpIndent; private $_dumpWordWrap; private $_containsGroupAnchor = false; private $_containsGroupAlias = false; private $path; private $result; private $LiteralPlaceHolder = '___YAML_Literal_Block___'; private $SavedGroups = array(); private $indent; /** * Path modifier that should be applied after adding current element. * @var array */ private $delayedPath = array(); /**#@+ * @access public * @var mixed */ public $_nodeId; /** * Load a valid YAML string to Spyc. * @param string $input * @return array */ public function load ($input) { return $this->__loadString($input); } /** * Load a valid YAML file to Spyc. * @param string $file * @return array */ public function loadFile ($file) { return $this->__load($file); } /** * Load YAML into a PHP array statically * * The load method, when supplied with a YAML stream (string or file), * will do its best to convert YAML in a file into a PHP array. Pretty * simple. * Usage: * <code> * $array = Spyc::YAMLLoad('lucky.yaml'); * print_r($array); * </code> * @access public * @return array * @param string $input Path of YAML file or string containing YAML */ public static function YAMLLoad($input) { $Spyc = new Spyc; return $Spyc->__load($input); } /** * Load a string of YAML into a PHP array statically * * The load method, when supplied with a YAML string, will do its best * to convert YAML in a string into a PHP array. Pretty simple. * * Note: use this function if you don't want files from the file system * loaded and processed as YAML. This is of interest to people concerned * about security whose input is from a string. * * Usage: * <code> * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); * print_r($array); * </code> * @access public * @return array * @param string $input String containing YAML */ public static function YAMLLoadString($input) { $Spyc = new Spyc; return $Spyc->__loadString($input); } /** * Dump YAML from PHP array statically * * The dump method, when supplied with an array, will do its best * to convert the array into friendly YAML. Pretty simple. Feel free to * save the returned string as nothing.yaml and pass it around. * * Oh, and you can decide how big the indent is and what the wordwrap * for folding is. Pretty cool -- just pass in 'false' for either if * you want to use the default. * * Indent's default is 2 spaces, wordwrap's default is 40 characters. And * you can turn off wordwrap by passing in 0. * * @access public * @return string * @param array $array PHP array * @param int $indent Pass in false to use the default, which is 2 * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) */ public static function YAMLDump($array,$indent = false,$wordwrap = false) { $spyc = new Spyc; return $spyc->dump($array,$indent,$wordwrap); } /** * Dump PHP array to YAML * * The dump method, when supplied with an array, will do its best * to convert the array into friendly YAML. Pretty simple. Feel free to * save the returned string as tasteful.yaml and pass it around. * * Oh, and you can decide how big the indent is and what the wordwrap * for folding is. Pretty cool -- just pass in 'false' for either if * you want to use the default. * * Indent's default is 2 spaces, wordwrap's default is 40 characters. And * you can turn off wordwrap by passing in 0. * * @access public * @return string * @param array $array PHP array * @param int $indent Pass in false to use the default, which is 2 * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) */ public function dump($array,$indent = false,$wordwrap = false) { // Dumps to some very clean YAML. We'll have to add some more features // and options soon. And better support for folding. // New features and options. if ($indent === false or !is_numeric($indent)) { $this->_dumpIndent = 2; } else { $this->_dumpIndent = $indent; } if ($wordwrap === false or !is_numeric($wordwrap)) { $this->_dumpWordWrap = 40; } else { $this->_dumpWordWrap = $wordwrap; } // New YAML document $string = "---\n"; // Start at the base of the array and move through it. if ($array) { $array = (array)$array; $previous_key = -1; foreach ($array as $key => $value) { if (!isset($first_key)) $first_key = $key; $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array); $previous_key = $key; } } return $string; } /** * Attempts to convert a key / value array item to YAML * @access private * @return string * @param $key The name of the key * @param $value The value of the item * @param $indent The indent of the current node */ private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) { if (is_array($value)) { if (empty ($value)) return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array); // It has children. What to do? // Make it the right kind of item $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array); // Add the indent $indent += $this->_dumpIndent; // Yamlize the array $string .= $this->_yamlizeArray($value,$indent); } elseif (!is_array($value)) { // It doesn't have children. Yip. $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array); } return $string; } /** * Attempts to convert an array to YAML * @access private * @return string * @param $array The array you want to convert * @param $indent The indent of the current level */ private function _yamlizeArray($array,$indent) { if (is_array($array)) { $string = ''; $previous_key = -1; foreach ($array as $key => $value) { if (!isset($first_key)) $first_key = $key; $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array); $previous_key = $key; } return $string; } else { return false; } } /** * Returns YAML from a key and a value * @access private * @return string * @param $key The name of the key * @param $value The value of the item * @param $indent The indent of the current node */ private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) { // do some folding here, for blocks if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, ' ') !== false || strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 || substr ($value, -1, 1) == ':') ) { $value = $this->_doLiteralBlock($value,$indent); } else { $value = $this->_doFolding($value,$indent); } if ($value === array()) $value = '[ ]'; if (in_array ($value, array ('true', 'TRUE', 'false', 'FALSE', 'y', 'Y', 'n', 'N', 'null', 'NULL'), true)) { $value = $this->_doLiteralBlock($value,$indent); } if (trim ($value) != $value) $value = $this->_doLiteralBlock($value,$indent); if (is_bool($value)) { $value = ($value) ? "true" : "false"; } if ($value === null) $value = 'null'; if ($value === "'" . self::REMPTY . "'") $value = null; $spaces = str_repeat(' ',$indent); //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) { // It's a sequence $string = $spaces.'- '.$value."\n"; } else { // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); // It's mapped if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; } $string = rtrim ($spaces.$key.': '.$value)."\n"; } return $string; } /** * Creates a literal block for dumping * @access private * @return string * @param $value * @param $indent int The value of the indent */ private function _doLiteralBlock($value,$indent) { if ($value === "\n") return '\n'; if (strpos($value, "\n") === false && strpos($value, "'") === false) { return sprintf ("'%s'", $value); } if (strpos($value, "\n") === false && strpos($value, '"') === false) { return sprintf ('"%s"', $value); } $exploded = explode("\n",$value); $newValue = '|'; $indent += $this->_dumpIndent; $spaces = str_repeat(' ',$indent); foreach ($exploded as $line) { $newValue .= "\n" . $spaces . ($line); } return $newValue; } /** * Folds a string of text, if necessary * @access private * @return string * @param $value The string you wish to fold */ private function _doFolding($value,$indent) { // Don't do anything if wordwrap is set to 0 if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { $indent += $this->_dumpIndent; $indent = str_repeat(' ',$indent); $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); $value = ">\n".$indent.$wrapped; } else { if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY) $value = '"' . $value . '"'; } return $value; } // LOADING FUNCTIONS private function __load($input) { $Source = $this->loadFromSource($input); return $this->loadWithSource($Source); } private function __loadString($input) { $Source = $this->loadFromString($input); return $this->loadWithSource($Source); } private function loadWithSource($Source) { if (empty ($Source)) return array(); if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { $array = syck_load (implode ('', $Source)); return is_array($array) ? $array : array(); } $this->path = array(); $this->result = array(); $cnt = count($Source); for ($i = 0; $i < $cnt; $i++) { $line = $Source[$i]; $this->indent = strlen($line) - strlen(ltrim($line)); $tempPath = $this->getParentPathByIndent($this->indent); $line = self::stripIndent($line, $this->indent); if (self::isComment($line)) continue; if (self::isEmpty($line)) continue; $this->path = $tempPath; $literalBlockStyle = self::startsLiteralBlock($line); if ($literalBlockStyle) { $line = rtrim ($line, $literalBlockStyle . " \n"); $literalBlock = ''; $line .= $this->LiteralPlaceHolder; $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1])); while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent); } $i--; } // Strip out comments if (strpos ($line, '#')) { $line = preg_replace('/\s*#([^"\']+)$/','',$line); } while (++$i < $cnt && self::greedilyNeedNextLine($line)) { $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); } $i--; $lineArray = $this->_parseLine($line); if ($literalBlockStyle) $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); $this->addArray($lineArray, $this->indent); foreach ($this->delayedPath as $indent => $delayedPath) $this->path[$indent] = $delayedPath; $this->delayedPath = array(); } return $this->result; } private function loadFromSource ($input) { if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) return file($input); return $this->loadFromString($input); } private function loadFromString ($input) { $lines = explode("\n",$input); foreach ($lines as $k => $_) { $lines[$k] = rtrim ($_, "\r"); } return $lines; } /** * Parses YAML code and returns an array for a node * @access private * @return array * @param string $line A line from the YAML file */ private function _parseLine($line) { if (!$line) return array(); $line = trim($line); if (!$line) return array(); $array = array(); $group = $this->nodeContainsGroup($line); if ($group) { $this->addGroup($line, $group); $line = $this->stripGroup ($line, $group); } if ($this->startsMappedSequence($line)) return $this->returnMappedSequence($line); if ($this->startsMappedValue($line)) return $this->returnMappedValue($line); if ($this->isArrayElement($line)) return $this->returnArrayElement($line); if ($this->isPlainArray($line)) return $this->returnPlainArray($line); return $this->returnKeyValuePair($line); } /** * Finds the type of the passed value, returns the value as the new type. * @access private * @param string $value * @return mixed */ private function _toType($value) { if ($value === '') return null; $first_character = $value[0]; $last_character = substr($value, -1, 1); $is_quoted = false; do { if (!$value) break; if ($first_character != '"' && $first_character != "'") break; if ($last_character != '"' && $last_character != "'") break; $is_quoted = true; } while (0); if ($is_quoted) return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\'')); if (strpos($value, ' #') !== false && !$is_quoted) $value = preg_replace('/\s+#(.+)$/','',$value); if (!$is_quoted) $value = str_replace('\n', "\n", $value); if ($first_character == '[' && $last_character == ']') { // Take out strings sequences and mappings $innerValue = trim(substr ($value, 1, -1)); if ($innerValue === '') return array(); $explode = $this->_inlineEscape($innerValue); // Propagate value array $value = array(); foreach ($explode as $v) { $value[] = $this->_toType($v); } return $value; } if (strpos($value,': ')!==false && $first_character != '{') { $array = explode(': ',$value); $key = trim($array[0]); array_shift($array); $value = trim(implode(': ',$array)); $value = $this->_toType($value); return array($key => $value); } if ($first_character == '{' && $last_character == '}') { $innerValue = trim(substr ($value, 1, -1)); if ($innerValue === '') return array(); // Inline Mapping // Take out strings sequences and mappings $explode = $this->_inlineEscape($innerValue); // Propagate value array $array = array(); foreach ($explode as $v) { $SubArr = $this->_toType($v); if (empty($SubArr)) continue; if (is_array ($SubArr)) { $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; } $array[] = $SubArr; } return $array; } if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { return null; } if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){ $intvalue = (int)$value; if ($intvalue != PHP_INT_MAX) $value = $intvalue; return $value; } if (in_array($value, array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) { return true; } if (in_array(strtolower($value), array('false', 'off', '-', 'no', 'n'))) { return false; } if (is_numeric($value)) { if ($value === '0') return 0; if (rtrim ($value, 0) === $value) $value = (float)$value; return $value; } return $value; } /** * Used in inlines to check for more inlines or quoted strings * @access private * @return array */ private function _inlineEscape($inline) { // There's gotta be a cleaner way to do this... // While pure sequences seem to be nesting just fine, // pure mappings and mappings with sequences inside can't go very // deep. This needs to be fixed. $seqs = array(); $maps = array(); $saved_strings = array(); // Check for strings $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; if (preg_match_all($regex,$inline,$strings)) { $saved_strings = $strings[0]; $inline = preg_replace($regex,'YAMLString',$inline); } unset($regex); $i = 0; do { // Check for sequences while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { $seqs[] = $matchseqs[0]; $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); } // Check for mappings while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { $maps[] = $matchmaps[0]; $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); } if ($i++ >= 10) break; } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); $explode = explode(',',$inline); $explode = array_map('trim', $explode); $stringi = 0; $i = 0; while (1) { // Re-add the sequences if (!empty($seqs)) { foreach ($explode as $key => $value) { if (strpos($value,'YAMLSeq') !== false) { foreach ($seqs as $seqk => $seq) { $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); $value = $explode[$key]; } } } } // Re-add the mappings if (!empty($maps)) { foreach ($explode as $key => $value) { if (strpos($value,'YAMLMap') !== false) { foreach ($maps as $mapk => $map) { $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); $value = $explode[$key]; } } } } // Re-add the strings if (!empty($saved_strings)) { foreach ($explode as $key => $value) { while (strpos($value,'YAMLString') !== false) { $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); unset($saved_strings[$stringi]); ++$stringi; $value = $explode[$key]; } } } $finished = true; foreach ($explode as $key => $value) { if (strpos($value,'YAMLSeq') !== false) { $finished = false; break; } if (strpos($value,'YAMLMap') !== false) { $finished = false; break; } if (strpos($value,'YAMLString') !== false) { $finished = false; break; } } if ($finished) break; $i++; if ($i > 10) break; // Prevent infinite loops. } return $explode; } private function literalBlockContinues ($line, $lineIndent) { if (!trim($line)) return true; if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; return false; } private function referenceContentsByAlias ($alias) { do { if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } $groupPath = $this->SavedGroups[$alias]; $value = $this->result; foreach ($groupPath as $k) { $value = $value[$k]; } } while (false); return $value; } private function addArrayInline ($array, $indent) { $CommonGroupPath = $this->path; if (empty ($array)) return false; foreach ($array as $k => $_) { $this->addArray(array($k => $_), $indent); $this->path = $CommonGroupPath; } return true; } private function addArray ($incoming_data, $incoming_indent) { // print_r ($incoming_data); if (count ($incoming_data) > 1) return $this->addArrayInline ($incoming_data, $incoming_indent); $key = key ($incoming_data); $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; if ($key === '__!YAMLZero') $key = '0'; if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. if ($key || $key === '' || $key === '0') { $this->result[$key] = $value; } else { $this->result[] = $value; end ($this->result); $key = key ($this->result); } $this->path[$incoming_indent] = $key; return; } $history = array(); // Unfolding inner array tree. $history[] = $_arr = $this->result; foreach ($this->path as $k) { $history[] = $_arr = $_arr[$k]; } if ($this->_containsGroupAlias) { $value = $this->referenceContentsByAlias($this->_containsGroupAlias); $this->_containsGroupAlias = false; } // Adding string or numeric key to the innermost level or $this->arr. if (is_string($key) && $key == '<<') { if (!is_array ($_arr)) { $_arr = array (); } $_arr = array_merge ($_arr, $value); } else if ($key || $key === '' || $key === '0') { if (!is_array ($_arr)) $_arr = array ($key=>$value); else $_arr[$key] = $value; } else { if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } } $reverse_path = array_reverse($this->path); $reverse_history = array_reverse ($history); $reverse_history[0] = $_arr; $cnt = count($reverse_history) - 1; for ($i = 0; $i < $cnt; $i++) { $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; } $this->result = $reverse_history[$cnt]; $this->path[$incoming_indent] = $key; if ($this->_containsGroupAnchor) { $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; if (is_array ($value)) { $k = key ($value); if (!is_int ($k)) { $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; } } $this->_containsGroupAnchor = false; } } private static function startsLiteralBlock ($line) { $lastChar = substr (trim($line), -1); if ($lastChar != '>' && $lastChar != '|') return false; if ($lastChar == '|') return $lastChar; // HTML tags should not be counted as literal blocks. if (preg_match ('#<.*?>$#', $line)) return false; return $lastChar; } private static function greedilyNeedNextLine($line) { $line = trim ($line); if (!strlen($line)) return false; if (substr ($line, -1, 1) == ']') return false; if ($line[0] == '[') return true; if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; return false; } private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) { $line = self::stripIndent($line, $indent); if ($literalBlockStyle !== '|') { $line = self::stripIndent($line); } $line = rtrim ($line, "\r\n\t ") . "\n"; if ($literalBlockStyle == '|') { return $literalBlock . $line; } if (strlen($line) == 0) return rtrim($literalBlock, ' ') . "\n"; if ($line == "\n" && $literalBlockStyle == '>') { return rtrim ($literalBlock, " \t") . "\n"; } if ($line != "\n") $line = trim ($line, "\r\n ") . " "; return $literalBlock . $line; } function revertLiteralPlaceHolder ($lineArray, $literalBlock) { foreach ($lineArray as $k => $_) { if (is_array($_)) $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) $lineArray[$k] = rtrim ($literalBlock, " \r\n"); } return $lineArray; } private static function stripIndent ($line, $indent = -1) { if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); return substr ($line, $indent); } private function getParentPathByIndent ($indent) { if ($indent == 0) return array(); $linePath = $this->path; do { end($linePath); $lastIndentInParentPath = key($linePath); if ($indent <= $lastIndentInParentPath) array_pop ($linePath); } while ($indent <= $lastIndentInParentPath); return $linePath; } private function clearBiggerPathValues ($indent) { if ($indent == 0) $this->path = array(); if (empty ($this->path)) return true; foreach ($this->path as $k => $_) { if ($k > $indent) unset ($this->path[$k]); } return true; } private static function isComment ($line) { if (!$line) return false; if ($line[0] == '#') return true; if (trim($line, " \r\n\t") == '---') return true; return false; } private static function isEmpty ($line) { return (trim ($line) === ''); } private function isArrayElement ($line) { if (!$line) return false; if ($line[0] != '-') return false; if (strlen ($line) > 3) if (substr($line,0,3) == '---') return false; return true; } private function isHashElement ($line) { return strpos($line, ':'); } private function isLiteral ($line) { if ($this->isArrayElement($line)) return false; if ($this->isHashElement($line)) return false; return true; } private static function unquote ($value) { if (!$value) return $value; if (!is_string($value)) return $value; if ($value[0] == '\'') return trim ($value, '\''); if ($value[0] == '"') return trim ($value, '"'); return $value; } private function startsMappedSequence ($line) { return ($line[0] == '-' && substr ($line, -1, 1) == ':'); } private function returnMappedSequence ($line) { $array = array(); $key = self::unquote(trim(substr($line,1,-1))); $array[$key] = array(); $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); return array($array); } private function returnMappedValue ($line) { $array = array(); $key = self::unquote (trim(substr($line,0,-1))); $array[$key] = ''; return $array; } private function startsMappedValue ($line) { return (substr ($line, -1, 1) == ':'); } private function isPlainArray ($line) { return ($line[0] == '[' && substr ($line, -1, 1) == ']'); } private function returnPlainArray ($line) { return $this->_toType($line); } private function returnKeyValuePair ($line) { $array = array(); $key = ''; if (strpos ($line, ':')) { // It's a key/value pair most likely // If the key is in double quotes pull it out if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { $value = trim(str_replace($matches[1],'',$line)); $key = $matches[2]; } else { // Do some guesswork as to the key and the value $explode = explode(':',$line); $key = trim($explode[0]); array_shift($explode); $value = trim(implode(':',$explode)); } // Set the type of the value. Int, string, etc $value = $this->_toType($value); if ($key === '0') $key = '__!YAMLZero'; $array[$key] = $value; } else { $array = array ($line); } return $array; } private function returnArrayElement ($line) { if (strlen($line) <= 1) return array(array()); // Weird %) $array = array(); $value = trim(substr($line,1)); $value = $this->_toType($value); $array[] = $value; return $array; } private function nodeContainsGroup ($line) { $symbolsForReference = 'A-z0-9_\-'; if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; return false; } private function addGroup ($line, $group) { if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); //print_r ($this->path); } private function stripGroup ($line, $group) { $line = trim(str_replace($group, '', $line)); return $line; } } // Enable use of Spyc from command line // The syntax is the following: php Spyc.php spyc.yaml define ('SPYC_FROM_COMMAND_LINE', false); do { if (!SPYC_FROM_COMMAND_LINE) break; if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'Spyc.php') break; $file = $argv[1]; printf ("Spyc loading file: %s\n", $file); print_r (spyc_load_file ($file)); } while (0); <?php # # S P Y C # a simple php yaml class # # Feel free to dump an array to YAML, and then to load that YAML back into an # array. This is a good way to test the limitations of the parser and maybe # learn some basic YAML. # include('../Spyc.php'); $array[] = 'Sequence item'; $array['The Key'] = 'Mapped value'; $array[] = array('A sequence','of a sequence'); $array[] = array('first' => 'A sequence','second' => 'of mapped values'); $array['Mapped'] = array('A sequence','which is mapped'); $array['A Note'] = 'What if your text is too long?'; $array['Another Note'] = 'If that is the case, the dumper will probably fold your text by using a block. Kinda like this.'; $array['The trick?'] = 'The trick is that we overrode the default indent, 2, to 4 and the default wordwrap, 40, to 60.'; $array['Old Dog'] = "And if you want\n to preserve line breaks, \ngo ahead!"; $array['key:withcolon'] = "Should support this to"; $yaml = Spyc::YAMLDump($array,4,60); <?php # # S P Y C # a simple php yaml class # # license: [MIT License, http://www.opensource.org/licenses/mit-license.php] # include('../Spyc.php'); $array = Spyc::YAMLLoad('../spyc.yaml'); echo '<pre><a href="spyc.yaml">spyc.yaml</a> loaded into PHP:<br/>'; print_r($array); echo '</pre>'; echo '<pre>YAML Data dumped back:<br/>'; echo Spyc::YAMLDump($array); echo '</pre>'; <?php php5to4 ("../spyc.php", 'spyc-latest.php4'); function php5to4 ($src, $dest) { $code = file_get_contents ($src); $code = preg_replace ('#(public|private|protected)\s+\$#i', 'var \$', $code); $code = preg_replace ('#(public|private|protected)\s+static\s+\$#i', 'var \$', $code); $code = preg_replace ('#(public|private|protected)\s+function#i', 'function', $code); $code = preg_replace ('#(public|private|protected)\s+static\s+function#i', 'function', $code); $code = preg_replace ('#throw new Exception\\(([^)]*)\\)#i', 'trigger_error($1,E_USER_ERROR)', $code); $code = str_replace ('self::', '$this->', $code); $f = fopen ($dest, 'w'); fwrite($f, $code); fclose ($f); print "Written to $dest.\n"; }# This file is for unifying the coding style for different editors and IDEs # editorconfig.org # WordPress Coding Standards # https://make.wordpress.org/core/handbook/coding-standards/ root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = tab [{.jshintrc,*.json,*.yml}] indent_style = space indent_size = 2 [{*.txt,wp-config-sample.php}] end_of_line = crlf language: php notifications: email: on_success: never on_failure: change php: - 5.3 - 5.6 env: {{#wp_versions_to_test}} - WP_VERSION={{.}} WP_MULTISITE=0 {{/wp_versions_to_test}} matrix: include: - php: 5.3 env: WP_VERSION=latest WP_MULTISITE=1 before_script: - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION script: phpunit language: php php: - 5.3 - 5.5 env: global: - WP_CLI_BIN_DIR=/tmp/wp-cli-phar - WP_CLI_CONFIG_PATH=/tmp/wp-cli-phar/config.yml before_script: - bash bin/install-package-tests.sh script: ./vendor/bin/behat <?php $_tests_dir = getenv( 'WP_TESTS_DIR' ); if ( ! $_tests_dir ) { $_tests_dir = '/tmp/wordpress-tests-lib'; } require_once $_tests_dir . '/includes/functions.php'; function _manually_load_plugin() { require dirname( dirname( __FILE__ ) ) . '/{{plugin_slug}}.php'; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); require $_tests_dir . '/includes/bootstrap.php'; /* Theme Name: {{theme_name}} Theme URI: {{theme_uri}} Description: {{description}} Author: {{author}} Author URI: {{author_uri}} Template: {{parent_theme}} Version: 0.1.0 */ <?php add_action( 'wp_enqueue_scripts', '{{parent_theme_function_safe}}_parent_theme_enqueue_styles' ); function {{parent_theme_function_safe}}_parent_theme_enqueue_styles() { wp_enqueue_style( '{{parent_theme}}-style', get_template_directory_uri() . '/style.css' ); wp_enqueue_style( '{{slug}}-style', get_stylesheet_directory_uri() . '/style.css', array( '{{parent_theme}}-style' ) ); } #!/usr/bin/env bash set -ex PACKAGE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../ && pwd )" download() { if [ `which curl` ]; then curl -s "$1" > "$2"; elif [ `which wget` ]; then wget -nv -O "$2" "$1" fi } install_wp_cli() { # the Behat test suite will pick up the executable found in $WP_CLI_BIN_DIR mkdir -p $WP_CLI_BIN_DIR download https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar $WP_CLI_BIN_DIR/wp chmod +x $WP_CLI_BIN_DIR/wp } set_package_context() { touch $WP_CLI_CONFIG_PATH printf 'require:' > $WP_CLI_CONFIG_PATH requires=$(php $PACKAGE_DIR/utils/get-package-require-from-composer.php composer.json) for require in "${requires[@]}" do printf "\n%2s-%1s$PACKAGE_DIR/$require" >> $WP_CLI_CONFIG_PATH done printf "\n" >> $WP_CLI_CONFIG_PATH } download_behat() { cd $PACKAGE_DIR download https://getcomposer.org/installer installer php installer php composer.phar require --dev behat/behat='~2.5' } install_db() { mysql -e 'CREATE DATABASE IF NOT EXISTS wp_cli_test;' -uroot mysql -e 'GRANT ALL PRIVILEGES ON wp_cli_test.* TO "wp_cli_test"@"localhost" IDENTIFIED BY "password1"' -uroot } install_wp_cli set_package_context download_behat install_db #!/usr/bin/env bash if [ $# -lt 3 ]; then echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version]" exit 1 fi DB_NAME=$1 DB_USER=$2 DB_PASS=$3 DB_HOST=${4-localhost} WP_VERSION=${5-latest} WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib} WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} download() { if [ `which curl` ]; then curl -s "$1" > "$2"; elif [ `which wget` ]; then wget -nv -O "$2" "$1" fi } if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then WP_TESTS_TAG="tags/$WP_VERSION" elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then WP_TESTS_TAG="trunk" else # http serves a single offer, whereas https serves multiple. we only want one download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') if [[ -z "$LATEST_VERSION" ]]; then echo "Latest WordPress version could not be found" exit 1 fi WP_TESTS_TAG="tags/$LATEST_VERSION" fi set -ex install_wp() { if [ -d $WP_CORE_DIR ]; then return; fi mkdir -p $WP_CORE_DIR if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then mkdir -p /tmp/wordpress-nightly download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/ mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR else if [ $WP_VERSION == 'latest' ]; then local ARCHIVE_NAME='latest' else local ARCHIVE_NAME="wordpress-$WP_VERSION" fi download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR fi download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php } install_test_suite() { # portable in-place argument for both GNU sed and Mac OSX sed if [[ $(uname -s) == 'Darwin' ]]; then local ioption='-i .bak' else local ioption='-i' fi # set up testing suite if it doesn't yet exist if [ ! -d $WP_TESTS_DIR ]; then # set up testing suite mkdir -p $WP_TESTS_DIR svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes fi cd $WP_TESTS_DIR if [ ! -f wp-tests-config.php ]; then download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php fi } install_db() { # parse DB_HOST for port or socket references local PARTS=(${DB_HOST//\:/ }) local DB_HOSTNAME=${PARTS[0]}; local DB_SOCK_OR_PORT=${PARTS[1]}; local EXTRA="" if ! [ -z $DB_HOSTNAME ] ; then if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" elif ! [ -z $DB_SOCK_OR_PORT ] ; then EXTRA=" --socket=$DB_SOCK_OR_PORT" elif ! [ -z $DB_HOSTNAME ] ; then EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" fi fi # create database mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA } install_wp install_test_suite install_db Feature: Test that WP-CLI loads. Scenario: WP-CLI loads for your tests Given a WP install When I run `wp eval 'echo "Hello world.";'` Then STDOUT should contain: """ Hello world. """ {{#is_subcommand}} {{/is_subcommand}} ## GLOBAL PARAMETERS {{#parameters}} {{synopsis}} {{desc}} {{/parameters}} {{#root_command}} Run 'wp help <command>' to get more information on a specific command. {{/root_command}} ## NAME {{name}} ## DESCRIPTION {{shortdesc}} ## SYNOPSIS {{synopsis}} {{#has-subcommands}} ## SUBCOMMANDS {{#subcommands}} {{.}} {{/subcommands}} {{/has-subcommands}} <phpunit bootstrap="tests/bootstrap.php" backupGlobals="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" > <testsuites> <testsuite> <directory prefix="test-" suffix=".php">./tests/</directory> </testsuite> </testsuites> </phpunit> .DS_Store node_modules/ module.exports = function( grunt ) { 'use strict'; var banner = '/**\n * <%= pkg.homepage %>\n * Copyright (c) <%= grunt.template.today("yyyy") %>\n * This file is generated automatically. Do not edit.\n */\n'; // Project configuration grunt.initConfig( { pkg: grunt.file.readJSON( 'package.json' ), addtextdomain: { options: { textdomain: '{{textdomain}}', }, target: { files: { src: [ '*.php', '**/*.php', '!node_modules/**', '!php-tests/**', '!bin/**' ] } } }, wp_readme_to_markdown: { your_target: { files: { 'README.md': 'readme.txt' } }, }, makepot: { target: { options: { domainPath: '/languages', mainFile: '{{plugin_slug}}.php', potFilename: '{{plugin_slug}}.pot', potHeaders: { poedit: true, 'x-poedit-keywordslist': true }, type: 'wp-plugin', updateTimestamp: true } } }, } ); grunt.loadNpmTasks( 'grunt-wp-i18n' ); grunt.loadNpmTasks( 'grunt-wp-readme-to-markdown' ); grunt.registerTask( 'i18n', ['addtextdomain', 'makepot'] ); grunt.registerTask( 'readme', ['wp_readme_to_markdown'] ); grunt.util.linefeed = '\n'; }; { "name": "{{plugin_slug}}", "version": "0.0.0", "main": "Gruntfile.js", "author": "YOUR NAME HERE", "devDependencies": { "grunt": "^0.4.5", "grunt-wp-i18n": "^0.5.0", "grunt-wp-readme-to-markdown": "~0.9.0" } } === {{plugin_name}} === Contributors: (this should be a list of wordpress.org userid's) Donate link: http://example.com/ Tags: comments, spam Requires at least: 3.0.1 Tested up to: 3.4 Stable tag: 4.3 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Here is a short description of the plugin. This should be no more than 150 characters. No markup here. == Description == This is the long description. No limit, and you can use Markdown (as well as in the following sections). For backwards compatibility, if this section is missing, the full length of the short description will be used, and Markdown parsed. A few notes about the sections above: * "Contributors" is a comma separated list of wp.org/wp-plugins.org usernames * "Tags" is a comma separated list of tags that apply to the plugin * "Requires at least" is the lowest version that the plugin will work on * "Tested up to" is the highest version that you've *successfully used to test the plugin*. Note that it might work on higher versions... this is just the highest one you've verified. * Stable tag should indicate the Subversion "tag" of the latest stable version, or "trunk," if you use `/trunk/` for stable. Note that the `readme.txt` of the stable tag is the one that is considered the defining one for the plugin, so if the `/trunk/readme.txt` file says that the stable tag is `4.3`, then it is `/tags/4.3/readme.txt` that'll be used for displaying information about the plugin. In this situation, the only thing considered from the trunk `readme.txt` is the stable tag pointer. Thus, if you develop in trunk, you can update the trunk `readme.txt` to reflect changes in your in-development version, without having that information incorrectly disclosed about the current stable version that lacks those changes -- as long as the trunk's `readme.txt` points to the correct stable tag. If no stable tag is provided, it is assumed that trunk is stable, but you should specify "trunk" if that's where you put the stable version, in order to eliminate any doubt. == Installation == This section describes how to install the plugin and get it working. e.g. 1. Upload `plugin-name.php` to the `/wp-content/plugins/` directory 1. Activate the plugin through the 'Plugins' menu in WordPress 1. Place `<?php do_action('plugin_name_hook'); ?>` in your templates == Frequently Asked Questions == = A question that someone might have = An answer to that question. = What about foo bar? = Answer to foo bar dilemma. == Screenshots == 1. This screen shot description corresponds to screenshot-1.(png|jpg|jpeg|gif). Note that the screenshot is taken from the /assets directory or the directory that contains the stable readme.txt (tags or trunk). Screenshots in the /assets directory take precedence. For example, `/assets/screenshot-1.png` would win over `/tags/4.3/screenshot-1.png` (or jpg, jpeg, gif). 2. This is the second screen shot == Changelog == = 1.0 = * A change since the previous version. * Another change. = 0.5 = * List versions from most recent at top to oldest at bottom. == Upgrade Notice == = 1.0 = Upgrade notices describe the reason a user should upgrade. No more than 300 characters. = 0.5 = This version fixes a security related bug. Upgrade immediately. == Arbitrary section == You may provide arbitrary sections, in the same format as the ones above. This may be of use for extremely complicated plugins where more information needs to be conveyed that doesn't fit into the categories of "description" or "installation." Arbitrary sections will be shown below the built-in sections outlined above. == A brief Markdown Example == Ordered list: 1. Some feature 1. Another feature 1. Something else about the plugin Unordered list: * something * something else * third thing Here's a link to [WordPress](http://wordpress.org/ "Your favorite software") and one to [Markdown's Syntax Documentation][markdown syntax]. Titles are optional, naturally. [markdown syntax]: http://daringfireball.net/projects/markdown/syntax "Markdown is what the parser uses to process much of the readme file" Markdown uses email style notation for blockquotes and I've been told: > Asterisks for *emphasis*. Double it up for **strong**. `<?php code(); // goes in backticks ?>` Plugin %9{{slug}}%n details: Name: {{name}} Status: {{status}}%n Version: {{version}} Author: {{author}} Description: {{description}} <?php /** * Plugin Name: {{plugin_name}} * Version: 0.1-alpha * Description: {{plugin_description}} * Author: {{plugin_author}} * Author URI: {{plugin_author_uri}} * Plugin URI: {{plugin_uri}} * Text Domain: {{textdomain}} * Domain Path: /languages * @package {{plugin_name}} */ register_post_type( '{{slug}}', array( 'labels' => array( 'name' => __( '{{label_plural_ucfirst}}', '{{textdomain}}' ), 'singular_name' => __( '{{label_ucfirst}}', '{{textdomain}}' ), 'all_items' => __( 'All {{label_plural_ucfirst}}', '{{textdomain}}' ), 'new_item' => __( 'New {{label}}', '{{textdomain}}' ), 'add_new' => __( 'Add New', '{{textdomain}}' ), 'add_new_item' => __( 'Add New {{label}}', '{{textdomain}}' ), 'edit_item' => __( 'Edit {{label}}', '{{textdomain}}' ), 'view_item' => __( 'View {{label}}', '{{textdomain}}' ), 'search_items' => __( 'Search {{label_plural}}', '{{textdomain}}' ), 'not_found' => __( 'No {{label_plural}} found', '{{textdomain}}' ), 'not_found_in_trash' => __( 'No {{label_plural}} found in trash', '{{textdomain}}' ), 'parent_item_colon' => __( 'Parent {{label}}', '{{textdomain}}' ), 'menu_name' => __( '{{label_plural_ucfirst}}', '{{textdomain}}' ), ), 'public' => true, 'hierarchical' => false, 'show_ui' => true, 'show_in_nav_menus' => true, 'supports' => array( 'title', 'editor' ), 'has_archive' => true, 'rewrite' => true, 'query_var' => true, 'menu_icon' => 'dashicons-{{dashicon}}', ) ); <?php function {{machine_name}}_init() { {{output}} } add_action( 'init', '{{machine_name}}_init' ); function {{machine_name}}_updated_messages( $messages ) { global $post; $permalink = get_permalink( $post ); $messages['{{slug}}'] = array( 0 => '', // Unused. Messages start at index 1. 1 => sprintf( __('{{label_ucfirst}} updated. <a target="_blank" href="%s">View {{label}}</a>', '{{textdomain}}'), esc_url( $permalink ) ), 2 => __('Custom field updated.', '{{textdomain}}'), 3 => __('Custom field deleted.', '{{textdomain}}'), 4 => __('{{label_ucfirst}} updated.', '{{textdomain}}'), /* translators: %s: date and time of the revision */ 5 => isset($_GET['revision']) ? sprintf( __('{{label_ucfirst}} restored to revision from %s', '{{textdomain}}'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, 6 => sprintf( __('{{label_ucfirst}} published. <a href="%s">View {{label}}</a>', '{{textdomain}}'), esc_url( $permalink ) ), 7 => __('{{label_ucfirst}} saved.', '{{textdomain}}'), 8 => sprintf( __('{{label_ucfirst}} submitted. <a target="_blank" href="%s">Preview {{label}}</a>', '{{textdomain}}'), esc_url( add_query_arg( 'preview', 'true', $permalink ) ) ), 9 => sprintf( __('{{label_ucfirst}} scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview {{label}}</a>', '{{textdomain}}'), // translators: Publish box date format, see http://php.net/date date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( $permalink ) ), 10 => sprintf( __('{{label_ucfirst}} draft updated. <a target="_blank" href="%s">Preview {{label}}</a>', '{{textdomain}}'), esc_url( add_query_arg( 'preview', 'true', $permalink ) ) ), ); return $messages; } add_filter( 'post_updated_messages', '{{machine_name}}_updated_messages' ); register_taxonomy( '{{slug}}', array( {{post_types}} ), array( 'hierarchical' => false, 'public' => true, 'show_in_nav_menus' => true, 'show_ui' => true, 'show_admin_column' => false, 'query_var' => true, 'rewrite' => true, 'capabilities' => array( 'manage_terms' => 'edit_posts', 'edit_terms' => 'edit_posts', 'delete_terms' => 'edit_posts', 'assign_terms' => 'edit_posts' ), 'labels' => array( 'name' => __( '{{label_plural_ucfirst}}', '{{textdomain}}' ), 'singular_name' => _x( '{{label_ucfirst}}', 'taxonomy general name', '{{textdomain}}' ), 'search_items' => __( 'Search {{label_plural}}', '{{textdomain}}' ), 'popular_items' => __( 'Popular {{label_plural}}', '{{textdomain}}' ), 'all_items' => __( 'All {{label_plural}}', '{{textdomain}}' ), 'parent_item' => __( 'Parent {{label}}', '{{textdomain}}' ), 'parent_item_colon' => __( 'Parent {{label}}:', '{{textdomain}}' ), 'edit_item' => __( 'Edit {{label}}', '{{textdomain}}' ), 'update_item' => __( 'Update {{label}}', '{{textdomain}}' ), 'add_new_item' => __( 'New {{label}}', '{{textdomain}}' ), 'new_item_name' => __( 'New {{label}}', '{{textdomain}}' ), 'separate_items_with_commas' => __( '{{label_plural_ucfirst}} separated by comma', '{{textdomain}}' ), 'add_or_remove_items' => __( 'Add or remove {{label_plural}}', '{{textdomain}}' ), 'choose_from_most_used' => __( 'Choose from the most used {{label_plural}}', '{{textdomain}}' ), 'not_found' => __( 'No {{label_plural}} found.', '{{textdomain}}' ), 'menu_name' => __( '{{label_plural_ucfirst}}', '{{textdomain}}' ), ), ) ); <?php function {{machine_name}}_init() { {{output}} } add_action( 'init', '{{machine_name}}_init' ); <?php class SampleTest extends WP_UnitTestCase { function test_sample() { // replace this with some actual testing code $this->assertTrue( true ); } } Theme %9{{slug}}%n details: Name: {{name}} Status: {{status}}%n Version: {{version}} Author: {{author}} WordPress version: {{wp-version}} Database revision: {{db-version}} TinyMCE version: {{mce-version}} <?php // ** MySQL settings ** // /** The name of the database for WordPress */ define('DB_NAME', '{{dbname}}'); /** MySQL database username */ define('DB_USER', '{{dbuser}}'); /** MySQL database password */ define('DB_PASSWORD', '{{dbpass}}'); /** MySQL hostname */ define('DB_HOST', '{{dbhost}}'); /** Database Charset to use in creating database tables. */ define('DB_CHARSET', '{{dbcharset}}'); /** The Database Collate type. Don't change this if in doubt. */ define('DB_COLLATE', '{{dbcollate}}'); {{#keys-and-salts}} {{keys-and-salts}} {{/keys-and-salts}} $table_prefix = '{{dbprefix}}'; {{#add-wplang}} define('WPLANG', '{{locale}}'); {{/add-wplang}} {{extra-php}} /* That's all, stop editing! Happy blogging. */ /** Absolute path to the WordPress directory. */ if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/'); /** Sets up WordPress vars and included files. */ require_once(ABSPATH . 'wp-settings.php'); <?php // autoload.php @generated by Composer require_once __DIR__ . '/composer' . '/autoload_real.php'; return ComposerAutoloaderInit675287e1dbacd27bbfb1e3e9e0740a37::getLoader(); <?php /** * Generate a list of tags to skip during the test run. * * Require a minimum version of WordPress: * * @require-wp-4.0 * Scenario: Core translation CRUD * * Then use in bash script: * * BEHAT_TAGS=$(php behat-tags.php) * vendor/bin/behat --format progress $BEHAT_TAGS */ function version_tags( $prefix, $current, $operator = '<' ) { if ( ! $current ) return; exec( "grep '@{$prefix}-[0-9\.]*' -h -o features/*.feature | uniq", $existing_tags ); $skip_tags = array(); foreach ( $existing_tags as $tag ) { $compare = str_replace( "@{$prefix}-", '', $tag ); if ( version_compare( $current, $compare, $operator ) ) { $skip_tags[] = $tag; } } return $skip_tags; } $skip_tags = array_merge( version_tags( 'require-wp', getenv( 'WP_VERSION' ), '<' ), version_tags( 'require-php', PHP_VERSION, '<' ), version_tags( 'less-than-php', PHP_VERSION, '>' ) ); # Skip Github API tests by default because of rate limiting. See https://github.com/wp-cli/wp-cli/issues/1612 $skip_tags[] = '@github-api'; if ( !empty( $skip_tags ) ) { echo '--tags=~' . implode( '&&~', $skip_tags ); } <?php $file = $argv[1]; if ( ! file_exists( $file ) ) { echo 'File does not exist.'; exit(1); } $contents = file_get_contents( $file ); $composer = json_decode( $contents ); if ( empty( $composer ) || ! is_object( $composer ) ) { echo 'Invalid composer.json for package.'; exit(1); } if ( empty( $composer->autoload->files ) ) { echo 'composer.json must specify valid "autoload" => "files"'; exit(1); } echo implode( PHP_EOL, $composer->autoload->files ); exit(0);## ## ca-bundle.crt -- Bundle of CA Root Certificates ## ## Certificate data from Mozilla as of: Sat Dec 29 20:03:40 2012 ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates ## file (certdata.txt). This file can be found in the mozilla source tree: ## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 ## ## It contains the certificates in PEM format and therefore ## can be directly used with curl / libcurl / php_curl, or with ## an Apache+mod_ssl webserver for SSL client authentication. ## Just configure this file as the SSLCACertificateFile. ## # @(#) $RCSfile: certdata.txt,v $ $Revision: 1.87 $ $Date: 2012/12/29 16:32:45 $ EE Certification Centre Root CA =============================== -----BEGIN CERTIFICATE----- MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw 93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU 3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM dcGWxZ0= -----END CERTIFICATE----- GTE CyberTrust Global Root ========================== -----BEGIN CERTIFICATE----- MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ -----END CERTIFICATE----- Thawte Server CA ================ -----BEGIN CERTIFICATE----- MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl /Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= -----END CERTIFICATE----- Thawte Premium Server CA ======================== -----BEGIN CERTIFICATE----- MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf 8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t UCemDaYj+bvLpgcUQg== -----END CERTIFICATE----- Equifax Secure CA ================= -----BEGIN CERTIFICATE----- MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW 8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 70+sB3c4 -----END CERTIFICATE----- Digital Signature Trust Co. Global CA 1 ======================================= -----BEGIN CERTIFICATE----- MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEy MTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUA A4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJE NySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2i o74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw IoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQY MBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAM BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB ACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEq kzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4 RbyhkwS7hp86W0N6w4pl -----END CERTIFICATE----- Digital Signature Trust Co. Global CA 3 ======================================= -----BEGIN CERTIFICATE----- MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEy MDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUA A4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGOD VvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JS xhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw IoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQY MBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAM BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB AEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfi up/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1 mPnHfxsb1gYgAlihw6ID -----END CERTIFICATE----- Verisign Class 3 Public Primary Certification Authority ======================================================= -----BEGIN CERTIFICATE----- MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf Tqj/ZA1k -----END CERTIFICATE----- Verisign Class 3 Public Primary Certification Authority - G2 ============================================================ -----BEGIN CERTIFICATE----- MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT 1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 -----END CERTIFICATE----- GlobalSign Root CA ================== -----BEGIN CERTIFICATE----- MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== -----END CERTIFICATE----- GlobalSign Root CA - R2 ======================= -----BEGIN CERTIFICATE----- MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp 9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu 01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== -----END CERTIFICATE----- ValiCert Class 1 VA =================== -----BEGIN CERTIFICATE----- MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP Orf1LXLI -----END CERTIFICATE----- ValiCert Class 2 VA =================== -----BEGIN CERTIFICATE----- MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 W9ViH0Pd -----END CERTIFICATE----- RSA Root Certificate 1 ====================== -----BEGIN CERTIFICATE----- MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td 3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs 3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r on+jjBXu -----END CERTIFICATE----- Verisign Class 3 Public Primary Certification Authority - G3 ============================================================ -----BEGIN CERTIFICATE----- MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj 055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC /Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== -----END CERTIFICATE----- Verisign Class 4 Public Primary Certification Authority - G3 ============================================================ -----BEGIN CERTIFICATE----- MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM 8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== -----END CERTIFICATE----- Entrust.net Secure Server CA ============================ -----BEGIN CERTIFICATE----- MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= -----END CERTIFICATE----- Entrust.net Premium 2048 Secure Server CA ========================================= -----BEGIN CERTIFICATE----- MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx NzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhC AQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdER gL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcS o8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z 2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjX OP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ== -----END CERTIFICATE----- Baltimore CyberTrust Root ========================= -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE----- Equifax Secure Global eBusiness CA ================================== -----BEGIN CERTIFICATE----- MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV -----END CERTIFICATE----- Equifax Secure eBusiness CA 1 ============================= -----BEGIN CERTIFICATE----- MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ 1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ KpYrtWKmpj29f5JZzVoqgrI3eQ== -----END CERTIFICATE----- Equifax Secure eBusiness CA 2 ============================= -----BEGIN CERTIFICATE----- MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UE ChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y MB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoT DkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCB nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn 2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5 BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAG A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUx JjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoG A1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9e uSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMB Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1 jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia 78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUm V+GRMOrN -----END CERTIFICATE----- AddTrust Low-Value Services Root ================================ -----BEGIN CERTIFICATE----- MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= -----END CERTIFICATE----- AddTrust External Root ====================== -----BEGIN CERTIFICATE----- MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 +iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy 2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= -----END CERTIFICATE----- AddTrust Public Services Root ============================= -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB /zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL +YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= -----END CERTIFICATE----- AddTrust Qualified Certificates Root ==================================== -----BEGIN CERTIFICATE----- MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx 64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= -----END CERTIFICATE----- Entrust Root Certification Authority ==================================== -----BEGIN CERTIFICATE----- MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 -----END CERTIFICATE----- RSA Security 2048 v3 ==================== -----BEGIN CERTIFICATE----- MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP +Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj 0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA pKnXwiJPZ9d37CAFYd4= -----END CERTIFICATE----- GeoTrust Global CA ================== -----BEGIN CERTIFICATE----- MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet 8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm Mw== -----END CERTIFICATE----- GeoTrust Global CA 2 ==================== -----BEGIN CERTIFICATE----- MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF H4z1Ir+rzoPz4iIprn2DQKi6bA== -----END CERTIFICATE----- GeoTrust Universal CA ===================== -----BEGIN CERTIFICATE----- MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs 7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d 8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI P/rmMuGNG2+k5o7Y+SlIis5z/iw= -----END CERTIFICATE----- GeoTrust Universal CA 2 ======================= -----BEGIN CERTIFICATE----- MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP 20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG 8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 +/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ 4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS -----END CERTIFICATE----- America Online Root Certification Authority 1 ============================================= -----BEGIN CERTIFICATE----- MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP 8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft 3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 -----END CERTIFICATE----- America Online Root Certification Authority 2 ============================================= -----BEGIN CERTIFICATE----- MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn 6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p +DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh 1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= -----END CERTIFICATE----- Visa eCommerce Root =================== -----BEGIN CERTIFICATE----- MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI /k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt 398znM/jra6O1I7mT1GvFpLgXPYHDw== -----END CERTIFICATE----- Certum Root CA ============== -----BEGIN CERTIFICATE----- MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ 89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ 0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== -----END CERTIFICATE----- Comodo AAA Services root ======================== -----BEGIN CERTIFICATE----- MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm 7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z 8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C 12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- Comodo Secure Services root =========================== -----BEGIN CERTIFICATE----- MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP 9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm 4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H RR3B7Hzs/Sk= -----END CERTIFICATE----- Comodo Trusted Services root ============================ -----BEGIN CERTIFICATE----- MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y /9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB /zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O 9y5Xt5hwXsjEeLBi -----END CERTIFICATE----- QuoVadis Root CA ================ -----BEGIN CERTIFICATE----- MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi 5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi 5nrQNiOKSnQ2+Q== -----END CERTIFICATE----- QuoVadis Root CA 2 ================== -----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt 66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK +JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II 4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u -----END CERTIFICATE----- QuoVadis Root CA 3 ================== -----BEGIN CERTIFICATE----- MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp 8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= -----END CERTIFICATE----- Security Communication Root CA ============================== -----BEGIN CERTIFICATE----- MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw 8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX 5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g 0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ 6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi FL39vmwLAw== -----END CERTIFICATE----- Sonera Class 2 Root CA ====================== -----BEGIN CERTIFICATE----- MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 /Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt 0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH llpwrN9M -----END CERTIFICATE----- Staat der Nederlanden Root CA ============================= -----BEGIN CERTIFICATE----- MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== -----END CERTIFICATE----- TDC Internet Root CA ==================== -----BEGIN CERTIFICATE----- MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc 5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ 2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l -----END CERTIFICATE----- UTN DATACorp SGC Root CA ======================== -----BEGIN CERTIFICATE----- MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA 9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv 33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI -----END CERTIFICATE----- UTN USERFirst Hardware Root CA ============================== -----BEGIN CERTIFICATE----- MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM //bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 nfhmqA== -----END CERTIFICATE----- Camerfirma Chambers of Commerce Root ==================================== -----BEGIN CERTIFICATE----- MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 erfutGWaIZDgqtCYvDi1czyL+Nw= -----END CERTIFICATE----- Camerfirma Global Chambersign Root ================================== -----BEGIN CERTIFICATE----- MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J 1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl 6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c 8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== -----END CERTIFICATE----- NetLock Notary (Class A) Root ============================= -----BEGIN CERTIFICATE----- MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC /tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM 8CgHrTwXZoi1/baI -----END CERTIFICATE----- NetLock Business (Class B) Root =============================== -----BEGIN CERTIFICATE----- MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr 1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM 43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI -----END CERTIFICATE----- NetLock Express (Class C) Root ============================== -----BEGIN CERTIFICATE----- MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== -----END CERTIFICATE----- XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc /Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz 8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= -----END CERTIFICATE----- Go Daddy Class 2 CA =================== -----BEGIN CERTIFICATE----- MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv 2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b vZ8= -----END CERTIFICATE----- Starfield Class 2 CA ==================== -----BEGIN CERTIFICATE----- MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 QBFGmh95DmK/D5fs4C8fF5Q= -----END CERTIFICATE----- StartCom Certification Authority ================================ -----BEGIN CERTIFICATE----- MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt 2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z 6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT 37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh 3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl 1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro g14= -----END CERTIFICATE----- Taiwan GRCA =========== -----BEGIN CERTIFICATE----- MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O 1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk 7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy +fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS -----END CERTIFICATE----- Firmaprofesional Root CA ======================== -----BEGIN CERTIFICATE----- MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2 ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf 3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8 NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm 7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA= -----END CERTIFICATE----- Wells Fargo Root CA =================== -----BEGIN CERTIFICATE----- MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMCVVMxFDASBgNV BAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhv cml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN MDAxMDExMTY0MTI4WhcNMjEwMTE0MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dl bGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEv MC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n135zHCLielTWi5MbqNQ1mX x3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHESxP9cMIlrCL1dQu3U+SlK93OvRw6esP3 E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4OJgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5 OEL8pahbSCOz6+MlsoCultQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4j sNtlAHCEAQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMBAAGj YTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcBCzAyMDAGCCsGAQUF BwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRwb2xpY3kwDQYJKoZIhvcNAQEFBQAD ggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrv m+0fazbuSCUlFLZWohDo7qd/0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0R OhPs7fpvcmR7nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ33ZwmVxwQ023 tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s= -----END CERTIFICATE----- Swisscom Root CA 1 ================== -----BEGIN CERTIFICATE----- MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4 MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn 7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5 haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9 MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3 1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW NY6E0F/6MBr1mmz0DlP5OlvRHA== -----END CERTIFICATE----- DigiCert Assured ID Root CA =========================== -----BEGIN CERTIFICATE----- MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO 9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW /lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF 66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i 8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== -----END CERTIFICATE----- DigiCert Global Root CA ======================= -----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H 4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y 7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm 8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE----- DigiCert High Assurance EV Root CA ================================== -----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K -----END CERTIFICATE----- Certplus Class 2 Primary CA =========================== -----BEGIN CERTIFICATE----- MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR 5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ 7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW //1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 l7+ijrRU -----END CERTIFICATE----- DST Root CA X3 ============== -----BEGIN CERTIFICATE----- MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ -----END CERTIFICATE----- DST ACES CA X6 ============== -----BEGIN CERTIFICATE----- MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 oKfN5XozNmr6mis= -----END CERTIFICATE----- TURKTRUST Certificate Services Provider Root 1 ============================================== -----BEGIN CERTIFICATE----- MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ 8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H -----END CERTIFICATE----- TURKTRUST Certificate Services Provider Root 2 ============================================== -----BEGIN CERTIFICATE----- MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr 5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P 9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 UrbnBEI= -----END CERTIFICATE----- SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR 7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm 5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr 44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ -----END CERTIFICATE----- SwissSign Silver CA - G2 ======================== -----BEGIN CERTIFICATE----- MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm +/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH 6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P 4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L 3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx /uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u -----END CERTIFICATE----- GeoTrust Primary Certification Authority ======================================== -----BEGIN CERTIFICATE----- MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG 1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= -----END CERTIFICATE----- thawte Primary Root CA ====================== -----BEGIN CERTIFICATE----- MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ 1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== -----END CERTIFICATE----- VeriSign Class 3 Public Primary Certification Authority - G5 ============================================================ -----BEGIN CERTIFICATE----- MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq -----END CERTIFICATE----- SecureTrust CA ============== -----BEGIN CERTIFICATE----- MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b 01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR 3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= -----END CERTIFICATE----- Secure Global CA ================ -----BEGIN CERTIFICATE----- MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g 8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi 0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW -----END CERTIFICATE----- COMODO Certification Authority ============================== -----BEGIN CERTIFICATE----- MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH +7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV 4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA 1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN +8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== -----END CERTIFICATE----- Network Solutions Certificate Authority ======================================= -----BEGIN CERTIFICATE----- MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc /Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q 4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey -----END CERTIFICATE----- WellsSecure Public Root Certificate Authority ============================================= -----BEGIN CERTIFICATE----- MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ tylv2G0xffX8oRAHh84vWdw+WNs= -----END CERTIFICATE----- COMODO ECC Certification Authority ================================== -----BEGIN CERTIFICATE----- MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X 4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- IGC/A ===== -----BEGIN CERTIFICATE----- MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF 0mBWWg== -----END CERTIFICATE----- Security Communication EV RootCA1 ================================= -----BEGIN CERTIFICATE----- MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO /VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK 9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 -----END CERTIFICATE----- OISTE WISeKey Global Root GA CA =============================== -----BEGIN CERTIFICATE----- MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ /yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 +vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= -----END CERTIFICATE----- Microsec e-Szigno Root CA ========================= -----BEGIN CERTIFICATE----- MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA 4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a 86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= -----END CERTIFICATE----- Certigna ======== -----BEGIN CERTIFICATE----- MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY 1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== -----END CERTIFICATE----- AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. ====================================== -----BEGIN CERTIFICATE----- MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU 2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP 2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm 8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK 5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v /zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f /RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== -----END CERTIFICATE----- TC TrustCenter Class 2 CA II ============================ -----BEGIN CERTIFICATE----- MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB 7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk vQ== -----END CERTIFICATE----- TC TrustCenter Class 3 CA II ============================ -----BEGIN CERTIFICATE----- MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo 6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk 2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB 7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal 092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc 5A== -----END CERTIFICATE----- TC TrustCenter Universal CA I ============================= -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG 1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a 7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY -----END CERTIFICATE----- Deutsche Telekom Root CA 2 ========================== -----BEGIN CERTIFICATE----- MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU Cm26OWMohpLzGITY+9HPBVZkVw== -----END CERTIFICATE----- ComSign Secured CA ================== -----BEGIN CERTIFICATE----- MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs 49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH 7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP 51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== -----END CERTIFICATE----- Cybertrust Global Root ====================== -----BEGIN CERTIFICATE----- MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW 0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin 89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT 8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi 5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW WL1WMRJOEcgh4LMRkWXbtKaIOM5V -----END CERTIFICATE----- ePKI Root Certification Authority ================================= -----BEGIN CERTIFICATE----- MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX 12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= -----END CERTIFICATE----- T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 ============================================================================================================================= -----BEGIN CERTIFICATE----- MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR 6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= -----END CERTIFICATE----- Buypass Class 2 CA 1 ==================== -----BEGIN CERTIFICATE----- MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV 1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt 7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho -----END CERTIFICATE----- Buypass Class 3 CA 1 ==================== -----BEGIN CERTIFICATE----- MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c 1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 -----END CERTIFICATE----- EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 ========================================================================== -----BEGIN CERTIFICATE----- MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0 Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB /wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK 1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt 2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9 AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT -----END CERTIFICATE----- certSIGN ROOT CA ================ -----BEGIN CERTIFICATE----- MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD 0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD -----END CERTIFICATE----- CNNIC ROOT ========== -----BEGIN CERTIFICATE----- MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m mxE= -----END CERTIFICATE----- ApplicationCA - Japanese Government =================================== -----BEGIN CERTIFICATE----- MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g /DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL rosot4LKGAfmt1t06SAZf7IbiVQ= -----END CERTIFICATE----- GeoTrust Primary Certification Authority - G3 ============================================= -----BEGIN CERTIFICATE----- MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr 2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt -----END CERTIFICATE----- thawte Primary Root CA - G2 =========================== -----BEGIN CERTIFICATE----- MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== -----END CERTIFICATE----- thawte Primary Root CA - G3 =========================== -----BEGIN CERTIFICATE----- MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC +BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY 7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC 8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= -----END CERTIFICATE----- GeoTrust Primary Certification Authority - G2 ============================================= -----BEGIN CERTIFICATE----- MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 npaqBA+K -----END CERTIFICATE----- VeriSign Universal Root Certification Authority =============================================== -----BEGIN CERTIFICATE----- MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj 1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 mJO37M2CYfE45k+XmCpajQ== -----END CERTIFICATE----- VeriSign Class 3 Public Primary Certification Authority - G4 ============================================================ -----BEGIN CERTIFICATE----- MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB /zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== -----END CERTIFICATE----- NetLock Arany (Class Gold) Főtanúsítvány ============================================ -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu 0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw /HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- Staat der Nederlanden Root CA - G2 ================================== -----BEGIN CERTIFICATE----- MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ 5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65 48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737 qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz +51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm 66+KAQ== -----END CERTIFICATE----- CA Disig ======== -----BEGIN CERTIFICATE----- MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA 4Z7CRneC9VkGjCFMhwnN5ag= -----END CERTIFICATE----- Juur-SK ======= -----BEGIN CERTIFICATE----- MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC +Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 yyqcjg== -----END CERTIFICATE----- Hongkong Post Root CA 1 ======================= -----BEGIN CERTIFICATE----- MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== -----END CERTIFICATE----- SecureSign RootCA11 =================== -----BEGIN CERTIFICATE----- MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= -----END CERTIFICATE----- ACEDICOM Root ============= -----BEGIN CERTIFICATE----- MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4 MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2 3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9 2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz 4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU 9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1 ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA== -----END CERTIFICATE----- Verisign Class 3 Public Primary Certification Authority ======================================================= -----BEGIN CERTIFICATE----- MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ D/xwzoiQ -----END CERTIFICATE----- Microsec e-Szigno Root CA 2009 ============================== -----BEGIN CERTIFICATE----- MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG 0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm 1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi LXpUq3DDfSJlgnCW -----END CERTIFICATE----- E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi =================================================== -----BEGIN CERTIFICATE----- MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY 8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk 9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX -----END CERTIFICATE----- GlobalSign Root CA - R3 ======================= -----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ 0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r kpeDMdmztcpHWD9f -----END CERTIFICATE----- TC TrustCenter Universal CA III =============================== -----BEGIN CERTIFICATE----- MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMC REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy IFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAe Fw0wOTA5MDkwODE1MjdaFw0yOTEyMzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNU QyBUcnVzdENlbnRlciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0Ex KDAmBgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF5+cvAqBNLaT6hdqbJYUt QCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYvDIRlzg9uwliT6CwLOunBjvvya8o84pxO juT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8vzArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+Eut CHnNaYlAJ/Uqwa1D7KRTyGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1 M4BDj5yjdipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBhMB8G A1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI4jANBgkqhkiG9w0BAQUFAAOCAQEA g8ev6n9NCjw5sWi+e22JLumzCecYV42FmhfzdkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+ KGwWaODIl0YgoGhnYIg5IFHYaAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhK BgePxLcHsU0GDeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPHLQNjO9Po5KIq woIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg== -----END CERTIFICATE----- Autoridad de Certificacion Firmaprofesional CIF A62634068 ========================================================= -----BEGIN CERTIFICATE----- MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY 7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx 51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi 6Et8Vcad+qMUu2WFbm5PEn4KPJ2V -----END CERTIFICATE----- Izenpe.com ========== -----BEGIN CERTIFICATE----- MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ 03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU +zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK 0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ 0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE----- Chambers of Commerce Root - 2008 ================================ -----BEGIN CERTIFICATE----- MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ 0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH 3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF 9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ -----END CERTIFICATE----- Global Chambersign Root - 2008 ============================== -----BEGIN CERTIFICATE----- MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB /gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp 1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG /5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg 9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z 09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B -----END CERTIFICATE----- Go Daddy Root Certificate Authority - G2 ======================================== -----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq 9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD +qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r 5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 -----END CERTIFICATE----- Starfield Root Certificate Authority - G2 ========================================= -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx 4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 -----END CERTIFICATE----- Starfield Services Root Certificate Authority - G2 ================================================== -----BEGIN CERTIFICATE----- MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 -----END CERTIFICATE----- AffirmTrust Commercial ====================== -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv 0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= -----END CERTIFICATE----- AffirmTrust Networking ====================== -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 /PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 /ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= -----END CERTIFICATE----- AffirmTrust Premium =================== -----BEGIN CERTIFICATE----- MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV 5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs +7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 /bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo +Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC 6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK +4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== -----END CERTIFICATE----- AffirmTrust Premium ECC ======================= -----BEGIN CERTIFICATE----- MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X 57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM eQ== -----END CERTIFICATE----- Certum Trusted Network CA ========================= -----BEGIN CERTIFICATE----- MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= -----END CERTIFICATE----- Certinomis - Autorité Racine ============================= -----BEGIN CERTIFICATE----- MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw 2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g 530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna 4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ vgt2Fl43N+bYdJeimUV5 -----END CERTIFICATE----- Root CA Generalitat Valenciana ============================== -----BEGIN CERTIFICATE----- MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt +GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= -----END CERTIFICATE----- A-Trust-nQual-03 ================ -----BEGIN CERTIFICATE----- MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 ahq97BvIxYSazQ== -----END CERTIFICATE----- TWCA Root Certification Authority ================================= -----BEGIN CERTIFICATE----- MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP 4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG 9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== -----END CERTIFICATE----- Security Communication RootCA2 ============================== -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ +T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R 3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk 3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 -----END CERTIFICATE----- EC-ACC ====== -----BEGIN CERTIFICATE----- MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw 0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D 5EI= -----END CERTIFICATE----- Hellenic Academic and Research Institutions RootCA 2011 ======================================================= -----BEGIN CERTIFICATE----- MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI 1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa 71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u 8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH 3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD /md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N 7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 -----END CERTIFICATE----- Actalis Authentication Root CA ============================== -----BEGIN CERTIFICATE----- MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC 4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo 2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== -----END CERTIFICATE----- Trustis FPS Root CA =================== -----BEGIN CERTIFICATE----- MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P 8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl iB6XzCGcKQENZetX2fNXlrtIzYE= -----END CERTIFICATE----- StartCom Certification Authority ================================ -----BEGIN CERTIFICATE----- MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt 2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z 6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT 37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA 2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= -----END CERTIFICATE----- StartCom Certification Authority G2 =================================== -----BEGIN CERTIFICATE----- MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG 4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K 2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG /+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm 7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm obp573PYtlNXLfbQ4ddI -----END CERTIFICATE----- Buypass Class 2 Root CA ======================= -----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn 9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b /+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN rJgWVqA= -----END CERTIFICATE----- Buypass Class 3 Root CA ======================= -----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR 5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh 7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH 2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV /afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz 6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi Cp/HuZc= -----END CERTIFICATE----- T-TeleSec GlobalRoot Class 3 ============================ -----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK 9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W 0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== -----END CERTIFICATE----- 0.22.05U ;g.Rb���GBMB